FORM FOLLOWS DOMAIN. DOMAIN COMES FIRST.NODE.JS / REACT
DOMAIN LOGIC → REST → NEXT.JS

YOUR DOMAIN.
NEXT BACKEND.

@domain-first/handlers-next

Bring Domain First handlers to the Next.js App Router. Keep business behavior in a handler and HTTP contracts at the boundary.

01Write the handler
02Describe the endpoint
03Export the Next.js route
01 / SETUP

Add the Next.js adapter.

In your Next.js application, install the adapter, handler libraries and the validator used below.

Terminal
npm i @domain-first/handlers-next @domain-first/handlers-rest @domain-first/handlers zod
02 / APPLICATION

Keep the calculation in a handler.

This example adds two numbers. The application function receives validated values and returns its result.

src/application/sum.tsTypeScript
import { defineHandler } from '@domain-first/handlers';
import { z } from 'zod';

export const sum = defineHandler({
  inputSchema: z.object({ a: z.number(), b: z.number() }),
  outputSchema: z.number(),
  handler: async ({ a, b }) => a + b,
});
03 / REST BOUNDARY

Adapt the endpoint to Next.js.

Use nextAdapter with createEndpoint. Reuse the handler schema for the request body and map the validated body into its input.

src/presentation/sum-endpoint.tsTypeScript
import { createEndpoint } from '@domain-first/handlers-rest';
import { nextAdapter } from '@domain-first/handlers-next';
import { sum } from '../application/sum';

const endpoint = createEndpoint()(nextAdapter);

export const sumPOST = endpoint(sum, {
  route: { method: 'post', path: ['sum'] },
})
  .input((schema) => ({ body: schema }))
  .mapInput(({ body }) => body);
04 / APP ROUTER

One route entry point.

Import the full REST endpoint array from your presentation layer. Pass it to nextEndpoints once, then export the same handler for every HTTP method. One App Router entry point serves the whole collection.

app/api/[...path]/route.tsTypeScript
import { nextEndpoints } from '@domain-first/handlers-next';
import { restHandlers } from '@/presentation/rest';

const handler = nextEndpoints(restHandlers);

export const GET = handler;
export const POST = handler;
export const PUT = handler;
export const PATCH = handler;
export const DELETE = handler;

The scaffolder automatically registers each new REST endpoint in @/presentation/rest. Import restHandlers and pass it to nextEndpoints — the collection is already maintained for you.

Try the endpoint
curl -X POST http://localhost:3000/api/sum \
  -H 'Content-Type: application/json' \
  -d '{"a":10,"b":30}'
# Result: 40
05 / REFERENCE

Two exports. One boundary.

nextAdapter

The Next.js adapter for createEndpoint().

nextEndpoints(routes)

Creates the App Router handler from your endpoint collection.

KEEP EXPLORING

Request mapping, error statuses, context and OpenAPI belong to the REST guide ↗. Application contracts belong to the Handlers guide ↗.