MainSDK
Search⌘K
How-to

Mount MainSDK in your framework

Add the adapter for Next.js, Hono, Express, Cloudflare Workers or a job worker so every request carries a trace, a context and a problem response.

Building for 0.2

Status Status is where the work is: Planned (in a milestone, not started), Building (tickets in progress), Preview (usable, may change), then the stability it ships at.

Stability The promise an interface ships with.

  • Stable Follows semantic versioning; breaking changes need a new major, 12 months of overlap and a codemod.
  • Beta Usable in production; may change before it becomes stable.
  • Experimental May change or go away; needs an explicit opt-in in main.yaml.
Stability levels
Updated 2026-10-28

You will mount one adapter and see a trace id on every log record. It takes about 10 minutes.

You need

  • A MainSDK app on Next.js, Hono, Express or Cloudflare Workers.

1. Mount it

server.ts (Hono)
import { Hono } from "hono";
import { mainsdk } from "mainsdk/hono";

const app = new Hono();
app.use("*", mainsdk());
app.get("/hello", (c) => c.json({ ok: true }));

Other frameworks:

app/api/orders/route.ts (Next.js App Router)
import { withMainSDK } from "mainsdk/next";

export const GET = withMainSDK(async (req, ctx) => Response.json({ trace: ctx.traceId }));
server.ts (Express)
import express from "express";
import { mainsdk } from "mainsdk/express";

const app = express();
app.use(mainsdk());
app.use(mainsdk.errors());     // last: turns a thrown error into a problem

2. Read the context

Inside any handler, requestContext() from mainsdk/adapters returns the trace id, user and tenant. Call /hello with a traceparent header and every log record carries that trace id. Without it the adapter starts a new one.

3. Add hooks if you need them

ts
import { Hono } from "hono";
import { mainsdk } from "mainsdk/hono";

declare const metrics: { done(traceId: string): void };
declare function warmCaches(): Promise<void>;
declare function flushQueues(): Promise<void>;

const app = new Hono();

app.use("*", mainsdk({
  hooks: { finally: (ctx) => metrics.done(ctx.traceId) },
  startup: async () => warmCaches(),
  shutdown: async () => flushQueues(),
}));

metrics, warmCaches and flushQueues are your own.

4. Run a job worker

jobs/report.ts
import { worker } from "mainsdk/worker";

declare function buildReport(orderId: string): Promise<void>;

export default worker(async (input: { orderId: string }, ctx) => {
  await buildReport(input.orderId);   // ctx.traceId is the producer's trace
});

Check it worked

bash
curl -i -H "traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01" localhost:3000/hello
npx mainsdk logs --trace 4bf92f3577b34da6a3ce929d0e0e4736

You should see the request's records under that trace.

Troubleshooting

  • MAIN-CORE-0500: a handler threw an error that is not a MainError. Read the log record with the same trace id.
  • A health probe answers 503: the app is draining, which is expected during shutdown.
  • A sign-in route answers 404 MAIN-AUTH-0084: the method is not in auth.methods.

See the adapters interface.

Was this page helpful?

↑↓ moveEnter openEsc close

Not decided yet