OOtis Docs

Integration guides

Framework reference

Where initOtis goes for each major JavaScript framework, and which platform guide to read next.

This page is a quick reference for the init pattern in each common framework — where to put initOtis() on the server and the client, and which framework-prefixed env var to use for the browser key. Once init is wired correctly, follow the platform-level guide for the rest (flush, context propagation, AI wrapping):

If your framework isn't listed below, the closest match is usually Express/Hono (long-running) or AWS Lambda (serverless).

Next.js

Use the dedicated guide: Next.js on Vercel. Server init goes in instrumentation.ts via register(); client init goes in app/layout.tsx via OtisProvider. Browser env var: NEXT_PUBLIC_OTIS_API_KEY.

Remix / React Router v7

Server — top-level import in entry.server.ts (generate it via npx remix reveal entry.server if it's not already present):

app/entry.server.ts
import "./otis";  // first import, before any handlers load
// ...rest of entry.server.ts

Where app/otis.ts calls initOtis({ apiKey: process.env.OTIS_API_KEY, serverless: true /* or false */ }). Use serverless: true on Vercel/Cloudflare adapters; omit it on the remix-serve Node adapter.

Client — top of entry.client.ts:

app/entry.client.ts
import { initOtis } from "@runotis/sdk";
import { hydrateRoot } from "react-dom/client";

initOtis({
  apiKey: import.meta.env.VITE_OTIS_API_KEY,
  serviceName: "my-app",
  browser: { autoAnonymousUserId: true, autoSessionId: true },
});

hydrateRoot(/* ... */);

Browser env var: VITE_OTIS_API_KEY (Remix uses Vite). Platform guide: Serverless for Vercel/Cloudflare/Netlify adapters; Node.js for remix-serve or Express adapters.

SvelteKit

Serversrc/hooks.server.ts runs once per process at server start:

src/hooks.server.ts
import { initOtis } from "@runotis/sdk";
import { OTIS_API_KEY } from "$env/static/private";

initOtis({
  apiKey: OTIS_API_KEY,
  serviceName: "my-app",
  serverless: true,  // most SvelteKit deploys are serverless (Vercel/Netlify/CF)
});

export const handle = async ({ event, resolve }) => resolve(event);

Client — root +layout.svelte:

src/routes/+layout.svelte
<script>
  import { onMount } from "svelte";
  import { initOtis } from "@runotis/sdk";
  import { PUBLIC_OTIS_API_KEY } from "$env/static/public";

  onMount(() => {
    initOtis({
      apiKey: PUBLIC_OTIS_API_KEY,
      serviceName: "my-app",
      browser: { autoAnonymousUserId: true, autoSessionId: true },
    });
  });
</script>

<slot />

Browser env var: PUBLIC_OTIS_API_KEY. Platform guide: Serverless for Vercel/Netlify/Cloudflare adapters; Node.js for the Node adapter.

Nuxt

Serverserver/plugins/otis.ts runs once per Nitro startup:

server/plugins/otis.ts
import { initOtis } from "@runotis/sdk";

export default defineNitroPlugin(() => {
  const config = useRuntimeConfig();
  initOtis({
    apiKey: config.otisApiKey,
    serviceName: "my-app",
    serverless: true,  // omit if deploying to the node-server preset
  });
});

Clientplugins/otis.client.ts:

plugins/otis.client.ts
import { initOtis } from "@runotis/sdk";

export default defineNuxtPlugin(() => {
  const config = useRuntimeConfig();
  initOtis({
    apiKey: config.public.otisApiKey,
    serviceName: "my-app",
    browser: { autoAnonymousUserId: true, autoSessionId: true },
  });
});

Register the keys in nuxt.config.ts:

nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    otisApiKey: "",                  // reads OTIS_API_KEY (server-only)
    public: { otisApiKey: "" },      // reads NUXT_PUBLIC_OTIS_API_KEY (browser)
  },
});

Platform guide: Serverless for Vercel/Netlify/Cloudflare presets; Node.js for the node-server preset.

Astro (SSR)

Static-only Astro sites only need the client init below. For SSR (output: 'server' or 'hybrid'):

Serversrc/middleware.ts. Module-scope code runs once on server start:

src/middleware.ts
import { initOtis } from "@runotis/sdk";

initOtis({
  apiKey: import.meta.env.OTIS_API_KEY,
  serviceName: "my-app",
  serverless: true,  // Vercel/Netlify adapters
});

export const onRequest = async (_context, next) => next();

Client — script in your root layout (loaded once per page navigation in MPA mode):

src/layouts/Base.astro
---
---
<html>
  <body>
    <slot />
    <script>
      import { initOtis } from "@runotis/sdk";
      initOtis({
        apiKey: import.meta.env.PUBLIC_OTIS_API_KEY,
        serviceName: "my-app",
        browser: { autoAnonymousUserId: true, autoSessionId: true },
      });
    </script>
  </body>
</html>

Browser env var: PUBLIC_OTIS_API_KEY. Platform guide: Serverless for Vercel/Netlify adapters; Node.js for the Node adapter.

NestJS

NestJS uses dependency injection, but the singleton pattern from the Node.js guide still applies — initialize before NestFactory.create() so the singleton is ready when modules instantiate:

src/main.ts
import { initOtis } from "@runotis/sdk";

export const otis = initOtis({
  apiKey: process.env.OTIS_API_KEY!,
  serviceName: "my-app",
});

import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);
}
bootstrap();

process.on("SIGTERM", async () => {
  await otis.shutdown();
  process.exit(0);
});

Import the otis singleton directly in services that need it, or wrap it in a provider for DI. Platform guide: Node.js.

Express, Fastify, Hono

Use the Node.js guide directly. Init via import "./otis" at the top of the server entry, before any handler files load.

For Hono on Cloudflare Workers, see the section below.

Cloudflare Workers

Use the Serverless guide. Key difference from Node: process.env is not populated at module scope. Initialize inside the fetch(req, env, ctx) handler using env.OTIS_API_KEY, and cache the instance in a module-level variable so warm requests reuse it.

Not covered here

  • Bun, Deno — most code targeting these works with the Node guide. See the runtime capability table in Tracing for which features require AsyncLocalStorage.
  • Google Cloud Functions, Azure Functions — same shape as AWS Lambda; follow Serverless.
  • Heroku, Fly.io, Render, Railway — long-running Node servers; follow Node.js.

On this page