Skip to Content
Static Segment

Static Segment

npm exec -- vovk new segment openapi --static # creates the static segment "openapi" at src/app/api/openapi/[[...vovk]]/route.ts

Next.js can pre-render API endpoints at build time with generateStaticParams . The Vovk.ts controllersToStaticParams helper uses this to emit static API endpoints, for minimal latency. Use it for OpenAPI definitions, historical datasets (refreshed from time to time by CI/CD) or other data that rarely changes. It also works in Static Export mode , with the output: 'export' Next.js option:

next.config.js
/** @type {import('next').NextConfig} */ const nextConfig = { output: 'export', }; module.exports = nextConfig;

Make generateStaticParams return controllersToStaticParams with your controllers, and Next.js pre-renders the route handler at build time. The segment needs no dynamic export, with cacheComponents on or off. Next.js refuses dynamic = 'force-static' when cacheComponents is on, as create-next-app 16.4+ sets it, so remove one that a segment still exports.

Next.js also refuses output: 'export' when cacheComponents is on. For a static export, set cacheComponents: false in next.config.

src/app/api/[[...vovk]]/route.ts
// ... export type Controllers = typeof controllers; export function generateStaticParams() { return controllersToStaticParams(controllers); } export const { GET } = initSegment({ controllers });

On static hosting, such as GitHub Pages, end the endpoint paths with .json, so the host sends the right HTTP headers.

import { get, prefix } from 'vovk'; @prefix('hello') export default class HelloController { @get('greeting.json') static async getHello() { return { greeting: 'Hello world!' }; } }

This gives an endpoint such as https://vovk.dev/api/hello/greeting.json , hosted on GitHub Pages.

With a slug other than the default vovk, such as /src/app/api/[[...custom]]/route.ts, pass it as the second argument:

export function generateStaticParams() { return controllersToStaticParams(controllers, 'custom'); }

Static Endpoint Parameters

The staticParams option of @get lists the param combinations to render, so one handler serves several static paths. A param in the controller’s prefix, such as @prefix('users/{userId}'), takes its value from the same objects. Below, one handler renders six variants of two params: section (a | b) and page (1 | 2 | 3).

import { z } from 'zod'; import { procedure, prefix, get, operation } from 'vovk'; @prefix('static-params') export default class StaticParamsController { @operation({ summary: 'Static Params', description: 'Get the static params: section and page', }) @get('{section}/page{page}.json', { staticParams: [ { section: 'a', page: '1' }, { section: 'a', page: '2' }, { section: 'a', page: '3' }, { section: 'b', page: '1' }, { section: 'b', page: '2' }, { section: 'b', page: '3' }, ], }) static getStaticParams = procedure({ params: z.object({ section: z.enum(['a', 'b']), page: z.enum(['1', '2', '3']), }), }).handle(async (_req, { section, page }) => { return { section, page }; }); }

View live example on examples.vovk.dev » 

This builds six JSON files:

Real-world example: static-klines

static-klines  is a Vovk.ts static segment. It pre-renders historical Binance Spot candles into ~17,000 plain JSON files on GitHub Pages. The whole API is static files with no server: no rate limits, no server compute, and no limit on caching. Its TypeScript and Python clients are generated from the same Zod schemas the server uses, and published. It shows controllersToStaticParams on a full dataset.

Last updated on