Static Segment
npx vovk new segment openapi --static # creates a new static segment named "openapi" at src/app/api/openapi/[[...vovk]]/route.tsNext.js can pre-render API endpoints at build time using generateStaticParams . Vovk.ts provides the generateStaticAPI helper to take advantage of this and emit static API endpoints for minimal latency. Use it to serve OpenAPI definitions, historical datasets (refreshed periodically via CI/CD), or other infrequently changing data. It also works in Static Export mode with the output: 'export' Next.js option:
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'export',
};
module.exports = nextConfig;All you need to do is implement generateStaticParams and return generateStaticAPI with your controller list.
// ...
export type Controllers = typeof controllers;
export function generateStaticParams() {
return generateStaticAPI(controllers);
}
export const { GET } = initSegment({ controllers });When deploying to static hosting (e.g., GitHub Pages), include a .json extension in endpoint paths to ensure proper HTTP headers are served.
import { get, prefix } from 'vovk';
@prefix('hello')
export default class HelloController {
@get('greeting.json')
static async getHello() {
return { greeting: 'Hello world!' };
}
}This produces an endpoint like: https://vovk.dev/api/hello/greeting.json (hosted on GitHub Pages).
If you use a custom slug (e.g., /src/app/api/[[...custom]]/route.ts) instead of the default vovk, pass it as the second argument:
export function generateStaticParams() {
return generateStaticAPI(controllers, 'custom');
}Static Endpoint Parameters
The @get decorator accepts an options object. One of the options, staticParams, lets you enumerate static parameter combinations to simulate conditional routing. The example below shows a single handler that renders six variants for two parameters: section (a | b) and page (1 | 2 | 3).
import { z } from 'zod/v4';
import { prefix, get, operation } from 'vovk';
import { withZod } from 'vovk-zod';
@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 = withZod({
params: z.object({
section: z.enum(['a', 'b']),
page: z.enum(['1', '2', '3']),
}),
handle: async (_req, { section, page }) => {
return { section, page };
},
});
}This builds six JSON files:
- /static-params/a/page1.json
- /static-params/a/page2.json
- /static-params/a/page3.json
- /static-params/b/page1.json
- /static-params/b/page2.json
- /static-params/b/page3.json
See the live example here .
RPC Client
Whether the API is static or dynamic, you call it with the same RPC client, including client-side validation and type inference.
const resp = await StaticParamsRPC.getStaticParams({
params: {
section: 'a',
page: '1',
},
});
console.log(resp); // { section: 'a', page: '1' }Troubleshooting
When output: 'export' is set in next.config.js, running npm run dev may show a schema parsing error:
⨯ SyntaxError: Unexpected non-whitespace character after JSON at position 396 (line 1 column 397)
at JSON.parse (<anonymous>) {
page: '/api/_schema_'
}To avoid this during npm run dev, close any open app tabs until the initial request to /_schema_ completes. Then reopen the app. If the error persists, please open an issue on the Vovk.ts GitHub repository with details.