Skip to Content
SegmentStatic segment

Static segment

npx vovk new segment openapi --static # will create a new static segment with name "openapi" at src/app/api/openapi/[[...vovk]]/route.ts

Next.js is able to render API endpoints at build time using generateStaticParams . Vovk.ts provides generateStaticAPI function to utilize this feature and generate static API endpoints for the lowest latency possible. You can use it to serve OpenAPI data, historical data (you can configure CI/CD job to be run periodically to update it), or any other static data that doesn’t change often. It can also be used in a Static Export mode  with the output: 'export' Next.js config setting:

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

The only thing you need to the segment is to return generateStaticAPI results from generateStaticParams function.

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

In order to make it work on a static website hosting like Github Pages, you may need to define .json extension in your endpoint definition to make it return proper HTTP headers.

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

As result you’re going to get an endpoint that looks like that: https://vovk.dev/api/hello/greeting.json  (hosted on Github Pages).

In case if you use a custom slug (e.g. /src/app/api/[[...custom]]/route.ts) instead of vovk, the default value, you can provide it as a second argument.

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

Static endpoint params

@get decorator accepts a set of options, one of them is staticParams that allows to define static parameters for the endpoint to simulate conditional routing. The example below demonstrates a static API represented by a single handler that renders 6 sets of static parameters: section (a | b) and page (1 | 2 | 3).

import { z } from 'zod/v4'; import { prefix, get, openapi } from 'vovk'; import { withZod } from 'vovk-zod'; @prefix('static-params') export default class StaticParamsController { @openapi({ 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 }; // return the params as is }, }); }

It will build 6 JSON files:

See live example here 

RPC client

It doesn’t matter wether you use static API or dynamic one, you can use the same RPC client to call it. This includes client-side validation, type inference, etc.

const resp = await StaticParamsRPC.getStaticParams({ params: { section: 'a', page: '1', }, }); console.log(resp); // { section: 'a', page: '1' }
Last updated on