Skip to main content

TypeScript API Client

Glean's TypeScript API client provides full type safety for integrating Glean's search and AI capabilities into web applications and Node.js services.

@gleanwork/api-client

Official TypeScript/JavaScript client for Glean's Client API

Installation

npm install @gleanwork/api-client

Quick Start

import { Glean } from "@gleanwork/api-client";

const client = new Glean({
apiToken: process.env.GLEAN_API_TOKEN,
serverURL: process.env.GLEAN_SERVER_URL,
});

const result = await client.client.chat.create({
messages: [{
fragments: [{ text: "What are our company values?" }]
}]
});

Core Features

Chat API

// Simple chat
const response = await client.client.chat.create({
messages: [{ fragments: [{ text: "Explain our Q4 strategy" }] }]
});

// Streaming responses
const stream = client.client.chat.stream({
messages: [{ fragments: [{ text: "What are our priorities?" }] }]
});

for await (const chunk of stream) {
console.log(chunk.text);
}

Search API

const results = await client.client.search.search({
query: "quarterly business review",
pageSize: 10
});

results.results?.forEach(result => {
console.log(`Title: ${result.title}`);
console.log(`URL: ${result.url}`);
});

Framework Integrations

Next.js API Route

// app/api/chat/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { Glean } from '@gleanwork/api-client';

export async function POST(request: NextRequest) {
const { message } = await request.json();

const client = new Glean({
apiToken: process.env.GLEAN_API_TOKEN!,
serverURL: process.env.GLEAN_SERVER_URL!,
});

const response = await client.client.chat.create({
messages: [{ fragments: [{ text: message }] }]
});

return NextResponse.json({ response: response.text });
}

React Component

import React, { useState } from 'react';
import { Glean } from '@gleanwork/api-client';

export function ChatComponent({ apiToken, serverURL }) {
const [input, setInput] = useState('');
const [response, setResponse] = useState('');

const client = new Glean({ apiToken, serverURL });

const handleSubmit = async (e) => {
e.preventDefault();
const result = await client.client.chat.create({
messages: [{ fragments: [{ text: input }] }]
});
setResponse(result.text || '');
};

return (
<form onSubmit={handleSubmit}>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Ask a question..."
/>
<button type="submit">Send</button>
{response && <div>{response}</div>}
</form>
);
}

Express.js

import express from 'express';
import { Glean } from '@gleanwork/api-client';

const app = express();
app.use(express.json());

const client = new Glean({
apiToken: process.env.GLEAN_API_TOKEN!,
serverURL: process.env.GLEAN_SERVER_URL!,
});

app.post('/api/chat', async (req, res) => {
const { message } = req.body;

const response = await client.client.chat.create({
messages: [{ fragments: [{ text: message }] }]
});

res.json({ response: response.text });
});

Authentication

User-Scoped Tokens

const client = new Glean({
apiToken: "your-user-token",
serverURL: "https://your-server-id-be.glean.com"
});

Global Tokens with ActAs

const response = await client.client.chat.create({
messages: [{ fragments: [{ text: "Hello" }] }]
}, {
headers: { "X-Glean-ActAs": "user@company.com" }
});

OAuth Access Tokens

An OAuth access token is a bearer credential, so it goes in the same apiToken field — no separate option is needed:

const client = new Glean({
apiToken: oauthAccessToken,
serverURL: "https://your-server-id-be.glean.com",
});

Tokens issued by the Glean OAuth Authorization Server (including tokens obtained via Dynamic Client Registration) are detected automatically. Tokens issued by an external identity provider (Google, Okta, Azure, etc.) additionally require the X-Glean-Auth-Type: OAUTH header on each request:

const response = await client.client.chat.create(
{ messages: [{ fragments: [{ text: "Hello" }] }] },
undefined,
undefined,
{ headers: { "X-Glean-Auth-Type": "OAUTH" } },
);

See the OAuth authentication guide for identity-provider setup.

Complete Example: Authorization Code with PKCE

This example uses openid-client (v6) with Express. It runs the Authorization Code flow with PKCE, then passes the resulting access token to the Glean client. Point OAUTH_ISSUER at the Glean OAuth Authorization Server metadata (https://your-server-id-be.glean.com/.well-known/oauth-authorization-server) or your IdP's issuer.

import express from 'express';
import session from 'express-session';
import * as client from 'openid-client';
import { Glean } from '@gleanwork/api-client';

const app = express();
app.use(session({ secret: 'change-me', resave: false, saveUninitialized: false }));

let config: client.Configuration;
async function init() {
config = await client.discovery(
new URL(process.env.OAUTH_ISSUER!),
process.env.OAUTH_CLIENT_ID!,
process.env.OAUTH_CLIENT_SECRET, // omit for a public client
);
}

app.get('/login', async (req, res) => {
// PKCE values must be generated per request and stored in the session.
const codeVerifier = client.randomPKCECodeVerifier();
const codeChallenge = await client.calculatePKCECodeChallenge(codeVerifier);
(req.session as any).codeVerifier = codeVerifier;

const params: Record<string, string> = {
redirect_uri: 'http://localhost:3000/callback',
scope: 'openid offline_access SEARCH', // SEARCH lets the token call /search (a Glean scope); offline_access requests a refresh token
code_challenge: codeChallenge,
code_challenge_method: 'S256',
};
// state is only needed if the server doesn't advertise PKCE support
if (!config.serverMetadata().supportsPKCE()) {
const state = client.randomState();
(req.session as any).state = state;
params.state = state;
}
res.redirect(client.buildAuthorizationUrl(config, params).href);
});

app.get('/callback', async (req, res) => {
const currentUrl = new URL(req.url, `http://${req.headers.host}`);
const tokens = await client.authorizationCodeGrant(config, currentUrl, {
pkceCodeVerifier: (req.session as any).codeVerifier,
expectedState: (req.session as any).state, // undefined when PKCE is used
});

const glean = new Glean({
apiToken: tokens.access_token,
serverURL: process.env.GLEAN_SERVER_URL!,
});

const results = await glean.client.search.query(
{ query: 'quarterly reports', pageSize: 10 },
undefined,
// Omit these headers when the token is from the Glean Authorization Server.
{ headers: { 'X-Glean-Auth-Type': 'OAUTH' } },
);
res.json(results);
});

init().then(() => app.listen(3000));
tip

Access tokens expire. Persist tokens.refresh_token and refresh with client.refreshTokenGrant(config, refreshToken) before expiry.

Error Handling

try {
const response = await client.client.chat.create({
messages: [{ fragments: [{ text: "Hello" }] }]
});
} catch (error) {
console.error('API error:', error);
}

Testing

import { jest } from '@jest/globals';
import { Glean } from '@gleanwork/api-client';

jest.mock('@gleanwork/api-client');

const MockedGlean = Glean as jest.MockedClass<typeof Glean>;

test('chat service', async () => {
const mockCreate = jest.fn().mockResolvedValue({
text: 'Test response'
});

MockedGlean.mockImplementation(() => ({
client: { chat: { create: mockCreate } }
} as any));

// Test your code here
});

Additional Resources