Segment
Overview
Many backend frameworks that follow the ControllerโServiceโRepository pattern use the following routing hierarchy:
- Backend server โ the top level, composed of controllers.
- Controllers โ the next level, composed of handlers.
- Handlers โ the lowest level; they process requests.
Vovk.ts introduces a new level between the backend server and controllers:
- Backend server โ the top level, composed of segments.
- Segments โ the second level, composed of controllers.
- Controllers โ the third level, composed of handlers.
- Handlers โ the lowest level; they process requests.
Each segment owns a specific path, such as /api/foo or /api/bar, and is compiled into a separate serverless function. This lets you split backend code into smaller, focused โmini backends,โ similar to how frontend code is split into pages in Next.js. Segments are implemented using Next.js optional catch-all segmentsย and are initialized by calling initSegment in the route.ts file located in [[โฆslug]] folders.
When NODE_ENV is set to "development", each segment exposes a _schema_ endpoint that serves the segment schema. The dev CLI calls this endpoint to retrieve the schema and build JSON files in .vovk-schema/. This avoids importing Node.js modules in Next.js code and allows export const runtime = 'edge' in route.ts without issues.
Segment Priority
With multiple segments, the most specific (deepest) one takes priority. For example:
- /src/app/api/[[โฆslug]]/ โ root segment
- /src/app/api/foo/[[โฆslug]]/ โ
foosegment - /src/app/api/foo/bar/[[โฆslug]]/ โ
foo/barsegment
A request to /api/foo/bar is handled by the foo/bar segment. If it doesnโt match, but the foo segment does, foo handles it. Otherwise, the root segment handles it.
You can change the API folder name from api to anything else via the config option
rootEntry.
Creating Segments
Vovk.ts implements segments via an โoptional catch-all segmentโย , defined as route.ts in /src/app/api/[[โฆslug]]/ for the root segment or /src/app/api/segment-name/[[โฆslug]]/ for others. It exports route handlersย (GET, POST, etc.) created by initSegment. The file can also export any Next.js segment config optionsย , such as runtime or maxDuration. If a segment should be callable from the frontend via RPC, it should also export a Controllers type used to type the generated TypeScript RPC library.
The module generator and this documentation use vovk as the slug name, but any valid name works.
Initialize a segment by calling initSegment in route.ts. The function accepts:
controllersโ an object with controllers used by the segment. Object keys define generated RPC module names; values are the controllers.segmentNameโ the segment name. Defaults to an empty string for the root segment.emitSchemaโ whether to emit the schema for the segment. Defaults totrue.exposeValidationโ whether to expose validation data. Defaults totrue.onErrorโ a callback invoked on errors with:error: HttpErrorโ the error instance.request: NextRequestโ the incoming request (headers, URL, etc.).
The Root Segment
npx vovk new segment # creates a new root segment at src/app/api/[[...vovk]]/route.tsFor simple single-page apps, a single root segment is sufficient. In this setup, the backend is bundled into one serverless function when deployed.
Example route.ts for a single-segment app:
import { initSegment } from 'vovk';
import UserController from '../../modules/user/UserController';
import PostController from '../../modules/post/PostController';
// maximum request duration in seconds; useful for long-running JSON Lines requests
export const maxDuration = 300;
const controllers = {
UserRPC: UserController,
PostRPC: PostController,
};
// export the controllers type to be used in the client code
export type Controllers = typeof controllers;
// export the Next.js route handlers
export const { GET, POST, PUT, DELETE } = initSegment({
controllers,
});The schema for the root segment is stored at .vovk-schema/root.json.
The name root is used only for file naming. In configuration and elsewhere, the root segment name is an
empty string.
Multiple Segments
Create multiple segments to split your backend into separate serverless functions. Reasons include:
- Reducing bundle size by splitting code.
- Separating app areas (e.g., root,
admin,customer,customer/public). - Supporting multiple API versions (e.g.,
v1,v2). - Creating a static segment for OpenAPI specs, historical data, etc.
Each segmentโs nested folder determines both the API path and the segment name. For example, /src/app/api/segment-name/[[โฆslug]]/ is served at /api/segment-name. Nesting is unlimited.
For non-root segments, provide segmentName to initSegment:
// ...
export const { GET, POST, PUT, DELETE } = initSegment({
segmentName: 'foo',
controllers,
});The schema for foo is stored at .vovk-schema/foo.json.
For deeper nesting, e.g., /src/app/api/foo/bar/baz/[[โฆslug]]/, set segmentName to "foo/bar/baz". The schema is stored at .vovk-schema/foo/bar/baz.json.