Mount the server
Create isolated fill and editor handlers, then mount them in your runtime.
dimahSurvey() creates one audience at a time. Every instance exposes a Fetch
handler for HTTP and a typed api for in-process server calls. Both audiences
must use the same SurveyStore.
Mount fill and editor on separate paths. A handler never exposes operations from the other audience.
Create the instances
import {
SURVEY_API_BASE_PATH,
SURVEY_EDITOR_API_BASE_PATH,
dimahSurvey,
guardAnonymous,
memoryAdapter,
} from "@dimah-survey/server";
const database = memoryAdapter();
export const editor = dimahSurvey({
audience: "editor",
database,
basePath: SURVEY_EDITOR_API_BASE_PATH,
});
export const fill = dimahSurvey({
audience: "fill",
database,
basePath: SURVEY_API_BASE_PATH,
guard: guardAnonymous(),
});basePath is part of the request contract and must match the corresponding
browser client's baseURL. The defaults are /api/survey for fill and
/api/admin/survey for editor.
A fill instance without guard throws at startup. An editor guard returns
nothing or throws. See Security.
memoryAdapter() is for tests and local development. Choose SQL or a custom
SurveyStore in Persistence.
Mount the HTTP handlers
import { toNextJsHandler } from "@dimah-survey/server/next";
import { fill } from "@/lib/survey";
export const { GET, POST, PUT, PATCH, DELETE } = toNextJsHandler(fill);Mount the editor the same way at app/api/admin/survey/[[...path]]/route.ts
with toNextJsHandler(editor).
Each framework adapter forwards the same Fetch handler; it never interprets SurveyJS JSON.
| Runtime | Export |
|---|---|
| Next.js | @dimah-survey/server/next → toNextJsHandler |
| Express | @dimah-survey/server/express → toExpressHandler |
| Hono | @dimah-survey/server/hono → toHonoHandler |
| Fastify | @dimah-survey/server/fastify → toFastifyHandler |
| Elysia | @dimah-survey/server/elysia → toElysiaHandler |
| SvelteKit | @dimah-survey/server/svelte-kit → toSvelteKitHandler |
| Node.js | @dimah-survey/server/node → toNodeHandler |
The Node-based adapters need the unread request stream. Mount Express before
express.json(). Configure Fastify so its JSON parser does not consume these
paths before the adapter.
import express from "express";
import { toExpressHandler } from "@dimah-survey/server/express";
import { fill } from "./survey";
const app = express();
app.all("/api/survey/*", toExpressHandler(fill));
app.use(express.json());Repeat the same adapter setup for the editor path. Never send both audiences through one instance.
Call the API on the server
fill.api and editor.api take { body } or { query }. They do not perform
an HTTP round trip. Pass headers or a Request when the guard needs cookies
or other caller context.
await editor.api.publishSurvey({
body: { id: "welcome" },
});import { headers } from "next/headers";
await fill.api.startResponse({
body: { surveyId: "welcome" },
headers: await headers(),
});Without forwarded request data, an in-process call has no session cookie.
Call from the browser
import { createFillClient } from "@dimah-survey/core";
const fillClient = createFillClient({ baseURL: "/api/survey" });
await fillClient.startResponse({ surveyId: "welcome" });Browser clients take flat objects and perform HTTP. Import them from
@dimah-survey/react in React code or @dimah-survey/core elsewhere. Do not
send a server instance to the client bundle.
See the complete method and route map in HTTP protocol.