---
title: "Module options"
description: "Every option of the Nuxt module's backend key with its default, the runtime config it publishes, and its ModuleOptions interface."
canonical_url: "https://nuxt-backend.dev/docs/reference/configuration/module-options"
---
# Module options

> Every option of the Nuxt module's backend key with its default, the runtime config it publishes, and its ModuleOptions interface.

## Options

The `backend` options are optional. The module reads `url` and `siteUrl` from explicit module options first, then from this package's own environment variables, then derives both from the deployment slug the platform CLI writes to `.env.local` — so in practice neither is set by hand.

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  modules: ['nuxt-backend'],
  backend: {
    url: 'https://your-deployment.convex.cloud',
    siteUrl: 'https://your-deployment.convex.site',
    authRoute: '/api/auth',
  },
})
```

<field-group>
<field name="url" type="string">

Backend deployment URL (the `.convex.cloud` origin). Falls back to `NUXT_PUBLIC_BACKEND_URL`, then to the URL derived from the deployment slug.

</field>

<field name="siteUrl" type="string">

Backend site URL — the HTTP-routes origin the auth proxy, the AI stream, and the agent exchange talk to. Falls back to `NUXT_PUBLIC_BACKEND_SITE_URL`, then to the derived value.

</field>

<field name="authRoute" type="string" default-value="'/api/auth'">

Path of the same-origin Better Auth proxy. If you change it, keep `setupAuth`'s `basePath` and the Convex auth config on the same path.

</field>

<field name="scaffold" type="'auto' | false" default-value="'auto'">

Auto-scaffold missing Convex backend files on dev startup. Set `false` when you scaffold explicitly with `npx nuxt-backend init`.

</field>

<field name="pages" type="ModulePagesOptions" default-value="every page at its default path">

Where the ready-made pages mount: `login`, `pricing`, `settings`, `profile`, `security`, `acceptInvitation` and `agentConsent`. Every page is on; give a key a path to move it. The `auth` middleware sends a signed-out visitor to `pages.login`. To replace a page, put an app page at its path: an app page always wins. A value that is not a path stops the build. See [Default pages](https://nuxt-backend.dev/docs/ui/pages).

</field>

<field name="autoEnv" type="boolean" default-value="true">

On dev startup, provision the dev deployment's environment once (the `env push` engine: `AUTH_SECRET`, `SITE_URL`, the dev-only OTP log flag). Never touches a non-dev deployment. See the [CLI](https://nuxt-backend.dev/docs/reference/cli).

</field>

<field name="mcp" type="ModuleMcpOptions" default-value="'/mcp'">

The OAuth-protected agent surface: the route, the server metadata, sessions and the Convex functions the built-in tools call. See [MCP server](https://nuxt-backend.dev/docs/agents/mcp).

</field>

<field name="devtools" type="boolean" default-value="true">

The Backend tab in Nuxt DevTools (dev only). See [DevTools](https://nuxt-backend.dev/docs/start/develop/devtools).

</field>
</field-group>

Auth is always enabled by the module.

An option 0.2 removed stops the build with an error that names it and links the [Upgrade page](https://nuxt-backend.dev/docs/ship/migrate/upgrade#pages-the-stylesheet-and-the-webhook-log-are-always-on): `loginPath` (now `pages.login`), `css` (the stylesheet always loads), and a page turned on or off with `true` or `false`.

## Runtime config

The module publishes two keys under `runtimeConfig.public.backend`, typed through `@nuxt/schema`:

- `runtimeConfig.public.backend.pages` — the resolved mount path per page key (`login`, `pricing`, `settings`, `profile`, `security`, `acceptInvitation`, `agentConsent`), every one a path, since every page mounts. The shipped components cross-link through it; yours can too.
- `runtimeConfig.public.backend.authRoute` — the same-origin auth route (`backend.authRoute`), through which `<AuthForm>` resumes an agent's authorization paused for sign-in.

The deployment URLs are not on `backend`: the module forwards `url` and `siteUrl` to the underlying Convex integration, which exposes them as `runtimeConfig.public.convex.url`, `runtimeConfig.public.convex.siteUrl` and, server-side, `runtimeConfig.convex.siteUrl`.

## The interface

```ts
import nuxtBackendModule, { type ModuleOptions } from 'nuxt-backend'

interface ModuleOptions {
  url?: string                        // the deployment's client URL (`.convex.cloud`); defaults to
                                      // NUXT_PUBLIC_BACKEND_URL, else the URL derived from the
                                      // CONVEX_DEPLOYMENT slug the platform CLI writes to .env.local
  siteUrl?: string                    // the HTTP-routes origin (`.convex.site`) the auth proxy, /ai/stream and
                                      // the agent exchange talk to; defaults to NUXT_PUBLIC_BACKEND_SITE_URL
                                      // (or NUXT_PUBLIC_CONVEX_SITE_URL), else the derived value, else the
                                      // `.site` twin of `url` (or NUXT_PUBLIC_BACKEND_URL / NUXT_PUBLIC_CONVEX_URL)
  authRoute?: string                  // default '/api/auth'
  scaffold?: 'auto' | false           // default 'auto' — auto-scaffold backend files on dev startup
  pages?: ModulePagesOptions          // where the ready-made pages mount (see below)
  autoEnv?: boolean                   // default true — on dev startup, provision the dev deployment env
                                      // (the `env push` engine, once per deployment; never non-dev)
  mcp?: ModuleMcpOptions              // the OAuth-protected agent surface at '/mcp'
                                      // (experimental; see STABILITY.md). The module sets
                                      // `nitro.experimental.asyncContext` so tool handlers
                                      // can read the current request
  devtools?: boolean                  // default true — the Backend tab in Nuxt DevTools (dev only)
}

interface ModuleMcpOptions {
  route?: string                       // default '/mcp'
  name?: string                        // server name / description / instructions
  description?: string                 //   forwarded to the MCP toolkit
  instructions?: string
  sessions?: boolean | {               // stateful transport sessions, default off
    enabled?: boolean
    maxDuration?: number               // ms of inactivity before a session is dropped;
                                       //   toolkit default 1_800_000 (30 minutes)
    maxSessions?: number               // concurrent sessions — beyond it the endpoint answers 503;
                                       //   toolkit default 1000
  }
  functions?: Partial<Record<BackendMcpFunctionKey, string>>
                                       // override the Convex functions the built-in tools call
                                       // ('module:export' refs) when the scaffolded files were renamed
}

interface ModulePagesOptions {
  login?: string             // default '/login' (public) — also the auth middleware's redirect target
  pricing?: string           // default '/pricing' (public)
  settings?: string          // default '/settings' (private)
  profile?: string           // default '/profile' (private)
  security?: string          // default '/security' (private)
  acceptInvitation?: string  // default '/accept-invitation' (private) — keep equal to setupAuth's organization.invitationPath
  agentConsent?: string      // default '/oauth/consent' (private) — keep equal to setupAuth's mcp.consentPath
}
```

`url` and `siteUrl` are forwarded to the base module as *defaults*, so an
explicit `convex.url` / `convex.siteUrl` in `nuxt.config` still wins over
them. `mcp.route`, `name`, `description`, `instructions` and `sessions` are
forwarded to `@nuxtjs/mcp-toolkit` the same way — an app's own top-level
`mcp` config wins; `functions` is read by this package (see
[MCP server](https://nuxt-backend.dev/docs/agents/mcp)).

Every page mounts: per page key, a path moves the page there, and a key left
out keeps its default path. **An app page at the same path always wins** — the
module detects it, skips its own, and logs the override; you never fight the
router to replace a built-in page.

The module also augments runtime config and app config:

- `PublicRuntimeConfig.backend.pages` — the resolved path per page key,
which is how the shipped components cross-link (settings → pricing,
pricing → login).
- `PublicRuntimeConfig.backend.authRoute` — the same-origin auth route,
through which `<AuthForm>` resumes an agent's authorization.
- Those are the only keys the module publishes under `backend`; the
deployment URLs stay on the base module's
`PublicRuntimeConfig.convex.{ url, siteUrl }`, which is where the runtime
reads them from (`useAiStream()` builds its endpoint off `convex.siteUrl`).
- `AppConfig.backend` — the [content layer](https://nuxt-backend.dev/docs/ui/customize/labels):
plan catalog, labels, brand

## Provided vs. intentionally app-owned

`nuxt-backend` is an all-in-one integration: it ships working pages and a
neutral stylesheet out of the box, while keeping every one of them replaceable
without ejecting.

<table>
<thead>
  <tr>
    <th>
      Capability (Nuxt Kit / mechanism)
    </th>
    
    <th>
      In <code>
        nuxt-backend
      </code>
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Module options · <code>
        defineNuxtModule({ configKey })
      </code>
    </td>
    
    <td>
      ✅ <code>
        backend.url
      </code>
      
      , <code>
        siteUrl
      </code>
      
      , <code>
        authRoute
      </code>
      
      , <code>
        scaffold
      </code>
      
      , <code>
        pages
      </code>
      
      , <code>
        autoEnv
      </code>
      
      , <code>
        mcp
      </code>
      
      , <code>
        devtools
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Runtime config · <code>
        options.runtimeConfig
      </code>
    </td>
    
    <td>
      ✅ public + private <code>
        backend
      </code>
      
       keys (incl. resolved page paths)
    </td>
  </tr>
  
  <tr>
    <td>
      App config · <code>
        options.appConfig
      </code>
    </td>
    
    <td>
      ✅ <code>
        appConfig.backend
      </code>
      
       content layer (plans, labels, brand)
    </td>
  </tr>
  
  <tr>
    <td>
      App plugins · <code>
        addPlugin
      </code>
    </td>
    
    <td>
      ✅ client + server plugins, injects <code>
        $convex
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Auto-imported composables · <code>
        addImports
      </code>
    </td>
    
    <td>
      ✅ data + auth + SaaS composables (<code>
        useBilling
      </code>
      
      , <code>
        usePasskeys
      </code>
      
      , …)
    </td>
  </tr>
  
  <tr>
    <td>
      Auto-imported components · <code>
        addComponent
      </code>
    </td>
    
    <td>
      ✅ <code>
        <AuthForm>
      </code>
      
      , <code>
        <PricingTable>
      </code>
      
      , <code>
        <WorkspaceSettings>
      </code>
      
      , <code>
        <ProfileSettings>
      </code>
      
      , <code>
        <SecuritySettings>
      </code>
      
      , <code>
        <RoleBoundary>
      </code>
      
      , <code>
        <FeatureBoundary>
      </code>
      
      , <code>
        <AcceptInvitation>
      </code>
      
      , <code>
        <GiftClaimBanner>
      </code>
      
      , …
    </td>
  </tr>
  
  <tr>
    <td>
      Route middleware · <code>
        addRouteMiddleware
      </code>
    </td>
    
    <td>
      ✅ <code>
        auth
      </code>
      
       (opt-in, non-global)
    </td>
  </tr>
  
  <tr>
    <td>
      Server routes · <code>
        addServerHandler
      </code>
    </td>
    
    <td>
      ✅ auth proxy at <code>
        authRoute/**
      </code>
      
      ; the OAuth-gated MCP endpoint at <code>
        mcp.route
      </code>
      
       (+ its <code>
        /.well-known
      </code>
      
       discovery documents)
    </td>
  </tr>
  
  <tr>
    <td>
      Server auto-imports · <code>
        addServerImports
      </code>
    </td>
    
    <td>
      ✅ <code>
        fetchQuery
      </code>
      
      , <code>
        preloadQuery
      </code>
      
      , <code>
        backendAuth
      </code>
      
      , <code>
        useBackendMcp
      </code>
      
      , …
    </td>
  </tr>
  
  <tr>
    <td>
      Import aliases · <code>
        options.alias
      </code>
      
       (+ <code>
        nitro.alias
      </code>
      
      )
    </td>
    
    <td>
      ✅ <code>
        #backend
      </code>
      
      , <code>
        #backend/api
      </code>
      
      , <code>
        #backend/server
      </code>
      
      , <code>
        #backend/dataModel
      </code>
      
      , <code>
        #backend/_generated
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Type augmentation · <code>
        declare module
      </code>
    </td>
    
    <td>
      ✅ runtime config + app config + generated declarations
    </td>
  </tr>
  
  <tr>
    <td>
      Package entrypoints · <code>
        exports
      </code>
      
       map
    </td>
    
    <td>
      ✅ module, server helpers (<code>
        nuxt-backend/billing
      </code>
      
      , <code>
        /email
      </code>
      
      , <code>
        /auth
      </code>
      
      , …), the component (<code>
        /convex.config
      </code>
      
      , <code>
        /_generated/component
      </code>
      
      )
    </td>
  </tr>
  
  <tr>
    <td>
      Scaffolded files on disk
    </td>
    
    <td>
      ✅ first-run backend wiring
    </td>
  </tr>
  
  <tr>
    <td>
      Pages / routes · <code>
        extendPages
      </code>
    </td>
    
    <td>
      ✅ the full default-page set (<code>
        pages
      </code>
      
      ), each movable and app-shadowable; never prerendered, private ones kept out of indexes and caches at their paths
    </td>
  </tr>
  
  <tr>
    <td>
      Global CSS · <code>
        options.css
      </code>
    </td>
    
    <td>
      ✅ the neutral <code>
        ui.css
      </code>
      
      , first and layered, so app CSS outside a layer always wins; after it, a generated stylesheet with the <code>
        light-dark()
      </code>
      
       default and the Nuxt UI and color-mode bridge
    </td>
  </tr>
  
  <tr>
    <td>
      DevTools · <code>
        devtools:customTabs
      </code>
      
      , <code>
        extendServerRpc
      </code>
    </td>
    
    <td>
      ✅ the Backend tab with its own RPC, and an activity log on the Vue DevTools timeline (<code>
        devtools: false
      </code>
      
       to opt out)
    </td>
  </tr>
  
  <tr>
    <td>
      Layouts · <code>
        addLayout
      </code>
    </td>
    
    <td>
      — app-owned
    </td>
  </tr>
  
  <tr>
    <td>
      Route rules · <code>
        extendRouteRules
      </code>
    </td>
    
    <td>
      ✅ <code>
        prerender: false
      </code>
      
       at every page's path; at a private page's, also <code>
        robots: false
      </code>
      
      , <code>
        sitemap: false
      </code>
      
      , and <code>
        cache
      </code>
      
      , <code>
        swr
      </code>
      
       and <code>
        isr
      </code>
      
       off with <code>
        cache-control: private, no-store
      </code>
      
       (<a href="https://nuxt-backend.dev/docs/ui/pages#search-engines">
        search engines
      </a>
      
      , <a href="https://nuxt-backend.dev/docs/ui/pages#caches">
        caches
      </a>
      
      ); a rule you set for the same path wins key by key
    </td>
  </tr>
  
  <tr>
    <td>
      Global route middleware · <code>
        addRouteMiddleware({ global: true })
      </code>
    </td>
    
    <td>
      — app-owned (auth is opt-in per page)
    </td>
  </tr>
  
  <tr>
    <td>
      Nitro plugins · <code>
        addServerPlugin
      </code>
    </td>
    
    <td>
      — not used
    </td>
  </tr>
  
  <tr>
    <td>
      Bundler plugins · <code>
        addVitePlugin
      </code>
      
       / <code>
        extendViteConfig
      </code>
    </td>
    
    <td>
      — not used
    </td>
  </tr>
  
  <tr>
    <td>
      Nuxt layer · <code>
        extends
      </code>
    </td>
    
    <td>
      — not shipped as a layer
    </td>
  </tr>
</tbody>
</table>

<note>

Everything the module mounts is progressively replaceable — the
[customization ladder](https://nuxt-backend.dev/docs/ui/pages): override `--bk-*` CSS tokens,
edit `appConfig.backend` content, fill component slots, or shadow a route with
your own page built on the same composables. No eject step exists because none
is needed.

</note>


## Sitemap

See the full [sitemap](https://nuxt-backend.dev/sitemap.md) for all pages.
