Skip to Content
SegmentGetting Started

Segment

Overview

Many backend frameworks that follow the Controller–Service–Repository pattern use the following hierarchy:

  • Backend server — the top level, composed of controllers.
  • Controllers — the next level, composed of handlers.
  • Handlers — the lowest level; they process requests.

(Services are omitted here because they do not affect the routing hierarchy.)

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]]/foo segment
  • /src/app/api/foo/bar/[[…slug]]/foo/bar segment

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 to true.
  • exposeValidation — whether to expose validation data. Defaults to true.
  • 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.ts

vovk new documentation

For 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:

src/app/api/[[...vovk]]/route.ts
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:

src/app/api/foo/[[...vovk]]/route.ts
// ... 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.

Last updated on