> hypequery

Serve Runtime

Understand how hypequery's Serve runtime runs standalone, inside frameworks, or entirely in-process

Serve Runtime Overview

Serve turns query definitions into reusable HTTP handlers, docs, and tooling. This page explains how the runtime is structured so you can choose the right delivery mode for your app.

One Definition, Many Runtimes

When you call initServe (or defineServe) you get back an api object. That object is environment-agnostic:

  • api.route('/hello', api.queries.hello) registers an endpoint description
  • api.handler exposes a framework-agnostic ServeHandler
  • api.start() (and the CLI) boot a Node HTTP server
  • api.run('hello') executes the same logic in-process (aliases: api.execute, api.client)

Because all of those methods share the same metadata and resolvers, you can move between deployment models without rewriting queries.

Delivery Modes

ModeHow it worksWhen to choose
Standalone servernpx hypequery dev api/queries.ts (or serve) wraps api.start() to run a Node HTTP server, host docs, and emit OpenAPI.Quick local previews, dedicated analytics services (Render, Fly, Railway, Docker).
Embedded framework handlerImport the same api into Next.js, Remix, Express, Fastify, SST, etc. and wire up the provided handler/route helpers.You already have an HTTP server and want the analytics endpoints to live beside existing routes.
Edge/Fetch runtimesUse createFetchHandler(api.handler) to deploy on Vercel Edge, Cloudflare Workers, or Service Workers.Need low-latency, edge-hosted analytics or want to avoid Node entirely.
In-process executionCall api.run(key) / api.execute / api.client directly—no HTTP involved.Cron jobs, SSR, background workers, or AI agents that just need the data.

Standalone Server (CLI & Node Helper)

# Dev mode: watches files, hot reloads docs
npx hypequery dev analytics/queries.ts --port 4000

# Production mode: no watcher, same server
npx hypequery serve analytics/queries.ts --port 8080

hypequery dev uses serveDev(api, { port }) under the hood (pretty logs + docs URL output) and adds file watching on top. hypequery serve uses api.start({ port }) for a minimal production server. You can do the same in code:

import { api } from './analytics/queries';

const server = await api.start({ port: 4000 });
// later: await server.stop();

If you want the same dev server behavior in code (logging + docs URL output), use serveDev:

import { serveDev } from '@hypequery/serve';
import { api } from './analytics/queries';

const server = await serveDev(api, { port: 4000 });
// later: await server.stop();

Embedding in Frameworks

Because every query is already a handler, you can mount the runtime anywhere:

// Next.js App Router example (app/api/weekly-revenue/route.ts)
import { api } from '@/analytics/queries';
import { createFetchHandler } from '@hypequery/serve';

api.route('/weeklyRevenue', api.queries.weeklyRevenue, { method: 'POST' });
const handler = createFetchHandler(api.handler);

export const POST = handler;

Need more control? Use the adapters directly:

import { createNodeHandler } from '@hypequery/serve';
import { api } from './analytics/queries';
import express from 'express';

const app = express();
app.use('/api/analytics', createNodeHandler(api.handler));

Edge runtimes can reuse the Fetch handler:

// Cloudflare Worker
import { createFetchHandler } from '@hypequery/serve';
import { api } from './analytics/queries';

const handler = createFetchHandler(api.handler);

export default {
  fetch(request: Request) {
    return handler(request);
  },
};

Docs and OpenAPI Everywhere

Whether you run the CLI server or embed handlers yourself, docs and OpenAPI are served from the same runtime:

  • Docs UI: ${basePath}/docs (default)
  • OpenAPI JSON: ${basePath}/openapi.json (default)

The defaults can be customized via docs.path and openapi.path in initServe/defineServe.

Choosing a Path

  1. Need a quick API → run hypequery dev and deploy that server directly.
  2. Have an existing server → import api and mount the provided handlers.
  3. Doing serverless/edge → use the Fetch adapter for your platform.
  4. No HTTP required → call api.run() from the environments you already control.

On this page