# Nuxt SDK (/docs/sdk/nuxt)

The Nuxt SDK sits around the [Javascript Server SDK](/docs/sdk/javascript#server) and gives Nuxt a server middleware for Nitro (Nuxt 3 and 4).

## Install [#install]

```bash
npm i @obelism/improve-sdk-nuxt
```

## Generator [#generator]

The main export generates the middleware from the passed configuration. Export it from a file in `server/middleware/`.

```ts title="server/middleware/improve.ts"
import { ImproveServerSDK } from '@obelism/improve-sdk/server'
import { generateImproveNuxtMiddleware } from '@obelism/improve-sdk-nuxt'

export default generateImproveNuxtMiddleware({
	improveSdk: new ImproveServerSDK(IMPROVE_ARGS),
	serverABtests: [],
})
```

### improveSdk [#improvesdk]

```ts
generateImproveNuxtMiddleware({
	improveSdk: ImproveServerSDK,
})
```

The [ImproveServerSDK](/docs/sdk/javascript#server) to be used. The middleware fetches its datafile on the first request a server instance gets, once for concurrent requests, and keeps it: a test changed after that reaches the instance when it restarts or through a deploy. Pages wait for that fetch, so set `configRetries: 0` (and a `fetchTimeout`) to fail fast; without a datafile the page renders without a decision.

### serverABtests [#serverabtests]

```ts
generateImproveNuxtMiddleware({
	serverABtests: ServerABTestConfig[]
})
```

Not every A/B test from the config needs to be decided on the server. Each entry in this list is one server side test:

```ts
export type ServerABTestConfig = {
	// Needs to match an A/B test in Improve
	slug: string

	// The page route to decide it on: a path (`/`), a parameter
	// (`/products/[id]`) or a catch-all (`/blog/[...slug]`)
	routeHandler: string
}
```

Parameters match letters, digits and `-`; optional parameters (`[[id]]`) don't match. With `app.baseURL` set, include it. Several tests can share a route.

### options [#options]

```ts
generateImproveNuxtMiddleware({
	options?: {
		visitorId?: CookieOptions
		testValue?: CookieOptions
	}
})
```

Optional overrides for the cookies the middleware reads and writes. `visitorId` customizes the `obelism_visitor_id` cookie and `testValue` the per-test value cookie: both accept h3's `setCookie` options (`maxAge`, `path`, `domain`, `secure`, `sameSite`, …). Defaults: `path: '/'` and a week. Keep them readable from JavaScript (no `httpOnly`): the client SDK reads them. The middleware still reads the old `visitorId` cookie, so existing visitors keep their id.

## The middleware [#the-middleware]

For a page request on a route with a server test, the middleware decides on one of three reasons:

- A query parameter named after the test with a valid option (`/?startpage-visual=variation`). Recommended for development or testing purposes; it shouldn't be used for real users since it impacts the distribution.
- The test's cookie, when it names a valid option. The middleware both reads and writes this cookie.
- A new decision for the visitor (from the `obelism_visitor_id` cookie, or a new id). On a long-running server the `ImproveServerSDK` also remembers a visitor's decision in memory; on serverless or edge it can't.

It leaves bots, non-GET requests, Nuxt's own requests (`/_nuxt/`, `/__nuxt_error`, payloads), `/api/` and files alone. A page it decided for gets `Cache-Control: private`.

## getImproveContext [#getimprovecontext]

```ts
const variants: Record<string, string> = getImproveContext(event)
```

The variants the middleware decided for this request, by test slug (also on `event.context.improve`). Empty for a bot, a route without a server test, or without a datafile. In a page, read it from `useRequestEvent()` into `useState`; see [Nuxt](/docs/getting-started/nuxt#render-the-variant).

## Country targeting [#country-targeting]

The middleware reads the visitor's country from the edge geo headers (Vercel's `x-vercel-ip-country`, falling back to Cloudflare's `cf-ipcountry`) and passes it into the server SDK's bucketing as the `geo` argument. So audiences that target `country` resolve automatically for server side tests, with no extra code. Because the browser can't determine country from the user agent, this server-side pass is the only place a `country` audience is evaluated; the middleware writes the resulting variant to the test cookie, which the [client SDK](/docs/sdk/javascript#gettestvalue-1) then honors.
