Persistence And Session API
This page covers durable storage and the built-in HTTP-friendly session layer.
Session Store Options
The library ships with three store patterns:
InMemorySessionStoreGood for tests and single-process local developmentPostgresSessionStoreDurable production storage with tenant scopingRedisSessionStoreBring-your-own Redis client for cache-style storage
In-Memory Storage
import { InMemorySessionStore, LLMClient } from 'unified-llm-client';
const sessionStore = new InMemorySessionStore();
const client = new LLMClient({
defaultModel: 'gpt-4o',
sessionStore,
});This store is process-local. If the process restarts, the session history is gone.
Postgres Storage
import { LLMClient, PostgresSessionStore } from 'unified-llm-client';
const sessionStore = PostgresSessionStore.fromEnv();
const client = LLMClient.fromEnv({
defaultModel: 'gpt-4o',
sessionStore,
});Important notes:
DATABASE_URLmust exist in the consuming application environment.- The store creates its schema/table lazily on first use.
- Session rows are scoped by
tenantIdwhen provided.
Automatic Postgres Wiring
If you call LLMClient.fromEnv() and DATABASE_URL is present, the library automatically uses PostgresSessionStore.fromEnv() for conversation() calls unless you pass an explicit sessionStore.
That means the following is enough for many projects:
const client = LLMClient.fromEnv({
defaultModel: 'gpt-4o',
});
const conversation = await client.conversation({
sessionId: 'support-123',
});Redis Storage
RedisSessionStore works with a client you provide.
import { LLMClient, RedisSessionStore } from 'unified-llm-client';
const sessionStore = new RedisSessionStore({
client: redisClient,
ttlSeconds: 3600,
});
const client = new LLMClient({
defaultModel: 'gpt-4o',
sessionStore,
});Your Redis client must implement:
get()set()del()scanIterator()orkeys()
Redis is useful when you want fast session storage with TTL-based expiry, but it is not a substitute for analytics storage.
Usage Logging And Aggregation
If you want per-request analytics and exportable aggregates, add a usage logger.
Console Logger
import { ConsoleLogger, LLMClient } from 'unified-llm-client';
const client = LLMClient.fromEnv({
defaultModel: 'gpt-4o',
usageLogger: new ConsoleLogger(),
});This is helpful during development because it prints sanitized usage events.
Postgres Usage Logger
import { LLMClient, PostgresUsageLogger } from 'unified-llm-client';
const usageLogger = PostgresUsageLogger.fromEnv();
const client = LLMClient.fromEnv({
defaultModel: 'gpt-4o',
usageLogger,
});After requests have been logged, aggregate usage like this:
const usage = await client.getUsage({
tenantId: 'tenant-1',
});
const csv = await client.exportUsage('csv', {
tenantId: 'tenant-1',
});
console.log(usage.totalCostUSD);
console.log(csv);Use PostgresUsageLogger when you need dashboards, billing reports, or operational monitoring by tenant, model, or session.
Build An HTTP Session Layer
The library exports createSessionApi() so you can expose session operations over HTTP without rewriting the conversation logic yourself.
import {
LLMClient,
PostgresSessionStore,
createSessionApi,
} from 'unified-llm-client';
const sessionStore = PostgresSessionStore.fromEnv();
const client = LLMClient.fromEnv({
defaultModel: 'gpt-4o',
sessionStore,
});
const sessionApi = createSessionApi({
client,
sessionStore,
});
const response = await sessionApi.handle(
new Request('https://example.test/sessions', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
sessionId: 'support-123',
system: 'Be concise.',
}),
}),
);Session API Endpoints
The built-in endpoints cover the full session lifecycle:
POST /sessionsPOST /sessions/{id}/messageGET /sessions/{id}GET /sessions/{id}/messagesDELETE /sessions/{id}POST /sessions/{id}/compactPOST /sessions/{id}/forkGET /sessions
Use the dedicated SESSION_API_REFERENCE.md document for the full request and response contract.
Add Authentication Or Tenant Context
createSessionApi() accepts middleware and request-context hooks so you can inject auth and tenant scoping.
const sessionApi = createSessionApi({
client,
sessionStore,
middleware: [
async (request) => {
const tenantId = request.headers.get('x-tenant-id');
return tenantId
? { tenantId }
: Response.json({ error: 'Unauthorized' }, { status: 401 });
},
],
});This makes it practical to keep tenant-specific data separation inside your own application rules instead of hard-coding auth into the library.
When To Use Which Layer
- Use
Conversationdirectly when your app already has its own backend orchestration. - Use
SessionApiwhen you want a thin HTTP service for frontend clients or other services. - Use Postgres when you want durable history.
- Use Redis when you want fast expiring state.
- Use a usage logger when you want analytics, billing, or monitoring.
Next Step
If you are moving toward production traffic, continue with Production Guide.