> hypequery

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 to localhost:4000) with query logging always enabled (logs completed/errored requests to the terminal). Accepts port, hostname, signal, logger, and quiet.
  • Node adaptercreateNodeHandler converts a ServeHandler into a Node (req, res) listener, and startNodeServer boots an HTTP server with graceful shutdown helpers.
  • Fetch/edge adaptercreateFetchHandler produces a (request: Request) => Response for edge runtimes, Cloudflare Workers, Remix loaders, etc.
  • Vercel adapterscreateVercelEdgeHandler wraps the fetch adapter for Edge Functions, while createVercelNodeHandler reuses the Node adapter for the Node runtime.

Documentation + OpenAPI utilities

  • buildOpenApiDocument(endpoints, options?) – Generates an OpenAPI 3.1 document from any list of ServeEndpoints. 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 type field.

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.

On this page