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 descriptionapi.handlerexposes a framework-agnosticServeHandlerapi.start()(and the CLI) boot a Node HTTP serverapi.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
| Mode | How it works | When to choose |
|---|---|---|
| Standalone server | npx 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 handler | Import 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 runtimes | Use 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 execution | Call 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
- Need a quick API → run
hypequery devand deploy that server directly. - Have an existing server → import
apiand mount the provided handlers. - Doing serverless/edge → use the Fetch adapter for your platform.
- No HTTP required → call
api.run()from the environments you already control.