Serve API
API guide for defineServe, ServeConfig, and related utilities in @hypequery/serve
@hypequery/serve reference
The serve package turns your metrics into HTTP endpoints, docs, and OpenAPI artifacts. This page lists every exported primitive so you can wire the runtime intentionally. For a conceptual tour of delivery options, read the Serve runtime overview first.
defineServe
import { defineServe } from '@hypequery/serve';
export const api = defineServe({
queries: {
activeUsers: {
query: async ({ db }) => db.table('users').where('status', 'eq', 'active').count(),
inputSchema: z.object({ region: z.string().optional() }),
outputSchema: z.object({ total: z.number() }),
method: 'POST',
cacheTtlMs: 5_000,
tags: ['users'],
auth: async ({ request }) => verifySession(request),
},
},
basePath: '/api/analytics',
tenant: { extract: (auth) => auth?.accountId, column: 'account_id', mode: 'auto-inject' },
auth: async ({ request }) => getAuthContext(request),
middlewares: [logRequests],
docs: { enabled: true, path: '/docs', title: 'Analytics API' },
openapi: { enabled: true, path: '/openapi.json', info: { title: 'Analytics', version: '1.2.0' } },
context: async ({ request, auth }) => ({ requestId: request.headers['x-request-id'], auth }),
hooks: {
onRequestStart: ({ queryKey, requestId }) => trace.begin(requestId, queryKey),
onRequestEnd: ({ durationMs, queryKey }) => metrics.record(queryKey, durationMs),
onError: ({ error, queryKey }) => logger.error(error, { queryKey }),
},
});
ServeConfig options
Prop
Type
Query definitions (ServeQueryConfig)
You can describe endpoints declaratively or pass executable functions directly. When using the config form the following fields are available:
Prop
Type
Executable functions can be passed directly in queries when you just need a handler—defineServe wraps them in a default configuration.
Builder API
defineServe returns a ServeBuilder with composable runtime helpers:
Prop
Type
Runtime helpers
serveDev(api, options?)– Launches a Node server (defaults tolocalhost:4000) with query logging always enabled (logs completed/errored requests to the terminal). Acceptsport,hostname,signal,logger, andquiet.- Node adapter –
createNodeHandlerconverts aServeHandlerinto a Node(req, res)listener, andstartNodeServerboots an HTTP server with graceful shutdown helpers. - Fetch/edge adapter –
createFetchHandlerproduces a(request: Request) => Responsefor edge runtimes, Cloudflare Workers, Remix loaders, etc. - Vercel adapters –
createVercelEdgeHandlerwraps the fetch adapter for Edge Functions, whilecreateVercelNodeHandlerreuses the Node adapter for the Node runtime.
Documentation + OpenAPI utilities
buildOpenApiDocument(endpoints, options?)– Generates an OpenAPI 3.1 document from any list ofServeEndpoints. Use it to persist specs or feed schema registries.buildDocsHtml(openapiUrl, docsOptions?)– Produces the Redoc-powered HTML served at/docs. You can host the markup yourself by calling this helper directly.
Observability
Every response includes an X-Request-Id header (generated or echoed from the incoming x-request-id / x-trace-id). Use it to correlate HTTP responses with logs.
Query logging
Enable query logging to observe endpoint execution in production or during development.
// Human-readable text to stdout
const api = defineServe({ queries, queryLogging: true });
// ✓ GET /api/analytics/revenue → 200 (12ms)
// ✗ POST /api/analytics/report → 500 (3ms) — Connection refused
// Structured JSON for log aggregators
const api = defineServe({ queries, queryLogging: 'json' });
// {"level":"info","msg":"GET /api/analytics/revenue","requestId":"...","endpoint":"revenue","status":200,"durationMs":12,"timestamp":"..."}
// Custom callback (ship to Datadog, Sentry, etc.)
const api = defineServe({
queries,
queryLogging: (event) => {
datadogLogs.logger.info(event.endpointKey, {
duration: event.durationMs,
status: event.responseStatus,
});
},
});
When queryLogging is omitted (the default), no listeners are registered and the emit path is skipped entirely — zero runtime overhead.
In development, serveDev() always subscribes its own terminal logger regardless of this setting.
Slow query warnings
Flag queries that exceed a duration threshold:
const api = defineServe({
queries,
queryLogging: 'json',
slowQueryThreshold: 2000, // ms
});
// console.warn: [hypequery/slow-query] GET /api/analytics/report (report) took 3400ms (threshold: 2000ms)
slowQueryThreshold registers an independent listener that calls console.warn, so it works alongside any queryLogging mode (or even without it — set slowQueryThreshold alone if you only want warnings).
Programmatic access
Use api.queryLogger to subscribe manually:
const api = defineServe({ queries });
// Subscribe to all events
const unsubscribe = api.queryLogger.on((event) => {
if (event.status === 'completed') {
histogram.record(event.durationMs);
}
});
// Check listener count (for diagnostics)
api.queryLogger.listenerCount; // 1
// Clean up
unsubscribe();
ServeQueryEvent
Prop
Type
Formatting utilities
Two built-in formatters are exported for use in custom logging setups:
import { formatQueryEvent, formatQueryEventJSON } from '@hypequery/serve';
// Human-readable: " ✓ GET /api/analytics/revenue → 200 (12ms)"
formatQueryEvent(event);
// Structured JSON: '{"level":"info","msg":"GET /api/analytics/revenue",...}'
formatQueryEventJSON(event);
Both return null for started events (only format completions and errors).
Types worth knowing
-
ServeMiddleware–(ctx, next) => result. Mutate context, emit logs, wrap cache, etc. -
AuthStrategy–({ request, endpoint }) => auth \| null. Compose multiple strategies to support API keys, JWTs, and tenant lookups. -
TenantConfig– Enforce tenant isolation by extracting IDs, requiring presence, and optionally auto-injecting filters. -
ServeLifecycleHooks– Observe every request for logging/metrics/tracing. -
ServeQueryLogger– Event emitter for endpoint executions. Exposes.on(callback),.listenerCount, and.removeAll(). -
ServeQueryEvent– Event payload emitted during each endpoint lifecycle (started, completed, error). See Observability for the full field list. -
ErrorEnvelope– Shape of errors returned from hypequery endpoints:{ "error": { "type": "VALIDATION_ERROR" | "UNAUTHORIZED" | "QUERY_FAILURE" | "CLICKHOUSE_UNREACHABLE" | "RATE_LIMITED" | "NOT_FOUND" | "INTERNAL_SERVER_ERROR", "message": "Human-friendly summary", "details": { "issues": [ /* zod validation errors */ ], "reason": "missing_credentials", "queryId": "...", // ... provider-specific metadata } } }Use this structure when surfacing errors to clients or agents so they can branch on the
typefield.
With these pieces you can embed analytics directly in your app, expose an HTTP API, or plug the same definitions into edge runtimes without rewriting handlers.