# Mount the server (https://survey.dimah.dev/docs/integration)



`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`.

<Callout>
  Mount fill and editor on separate paths. A handler never exposes operations
  from the other audience.
</Callout>

## Create the instances [#create-the-instances]

```ts title="lib/survey.ts"
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](https://survey.dimah.dev/docs/security.md).

`memoryAdapter()` is for tests and local development. Choose SQL or a custom
`SurveyStore` in [Persistence](https://survey.dimah.dev/docs/persistence.md).

## Mount the HTTP handlers [#mount-the-http-handlers]

```ts title="app/api/survey/[[...path]]/route.ts"
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`            |

<Callout type="warn">
  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.
</Callout>

<Tabs items="[&#x22;Express&#x22;, &#x22;Hono&#x22;, &#x22;Fastify&#x22;, &#x22;Elysia&#x22;, &#x22;SvelteKit&#x22;, &#x22;Node.js&#x22;]">
  <Tab value="Express">
    ```ts
    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());
    ```
  </Tab>

  <Tab value="Hono">
    ```ts
    import { Hono } from "hono";
    import { toHonoHandler } from "@dimah-survey/server/hono";
    import { fill } from "./survey";

    const app = new Hono();
    app.on(
      ["GET", "POST", "PUT", "PATCH", "DELETE"],
      "/api/survey/*",
      toHonoHandler(fill),
    );
    ```
  </Tab>

  <Tab value="Fastify">
    ```ts
    import Fastify from "fastify";
    import { toFastifyHandler } from "@dimah-survey/server/fastify";
    import { fill } from "./survey";

    const app = Fastify();
    app.all("/api/survey/*", toFastifyHandler(fill));
    ```
  </Tab>

  <Tab value="Elysia">
    ```ts
    import { Elysia } from "elysia";
    import { toElysiaHandler } from "@dimah-survey/server/elysia";
    import { fill } from "./survey";

    new Elysia()
      .all("/api/survey/*", toElysiaHandler(fill))
      .listen(3000);
    ```
  </Tab>

  <Tab value="SvelteKit">
    ```ts title="src/routes/api/survey/[...path]/+server.ts"
    import { toSvelteKitHandler } from "@dimah-survey/server/svelte-kit";
    import { fill } from "$lib/survey";

    const handler = toSvelteKitHandler(fill);
    export const GET = handler;
    export const POST = handler;
    export const PUT = handler;
    export const PATCH = handler;
    export const DELETE = handler;
    ```
  </Tab>

  <Tab value="Node.js">
    ```ts
    import { createServer } from "node:http";
    import { toNodeHandler } from "@dimah-survey/server/node";
    import { fill } from "./survey";

    createServer(toNodeHandler(fill)).listen(3000);
    ```
  </Tab>
</Tabs>

Repeat the same adapter setup for the editor path. Never send both audiences
through one instance.

## Call the API on the server [#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.

```ts
await editor.api.publishSurvey({
  body: { id: "welcome" },
});
```

```ts
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 [#call-from-the-browser]

```ts
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](https://survey.dimah.dev/docs/protocol.md).
