# H3 and Nitro 3

> Run withBetterSupabase as H3 2 middleware, with the caller's repositories on event.context.

Source: https://bettersupabase.com/docs/frameworks/h3

`toH3(entries)` from `better-supabase/h3` returns H3 2 middleware. Every key
[`withBetterSupabase`](/docs/auth/middleware) contributes lands on
`event.context`:

```ts title="server.ts"
import { H3 } from "h3";
import { toH3 } from "better-supabase/h3";
import { createServer, withBetterSupabase } from "better-supabase/server";
import { betterSupabase } from "./lib/supabase";

const bs = createServer(betterSupabase);

const app = new H3()
  .use(toH3([withBetterSupabase(bs, { allow: ["user"] })]))
  .get("/notes", (event) => event.context.db.notes.findMany().orThrow());

export default { fetch: app.fetch };
```

The handler's value comes back to the bridge as a `Response` (a plain value as
JSON, `undefined` as 204), so the entries can add refreshed cookies and
`bs-primary-until` to it. A guard refusal answers 401 or 403 Problem Details
before the handler runs.

`guard(options)` from `better-supabase/h3` refuses callers per route, with
the options every adapter takes (`allow`, `aal`, `scopes`, `roles`,
`requireTenant`, `permission`, `authorize`, `signIn`, `mfa`). A `permission`
is decided by the guard's `authorizer` (see
[Authorizers](/docs/extending/authorizers)); without one, the route refuses
every caller. `problemOnError()` answers errors thrown by `.orThrow()` with
Problem Details:

```ts
import { H3 } from "h3";
import { guard, problemOnError } from "better-supabase/h3";

const app = new H3({ onError: problemOnError() }).get(
  "/reports",
  (event) => event.context.db.reports.findMany().orThrow(),
  { middleware: [guard({ permission: "reports:read", authorizer })] },
);
```

In Nitro 3, register the same middleware in `server/middleware`. Nuxt 3 and 4
serve through Nitro 2 on H3 1; use [`better-supabase/nuxt`](/docs/frameworks/nuxt)
there.

```ts title="server/middleware/supabase.ts"
import { defineMiddleware } from "h3";
import { toH3 } from "better-supabase/h3";
import { withBetterSupabase } from "better-supabase/server";
import { bs } from "../utils/supabase";

export default defineMiddleware(
  toH3([withBetterSupabase(bs, { refresh: true, allow: ["user", "anon"] })]),
);
```

The bridge makes the request body readable twice, so an entry and the handler
both see it.