eve
Version:
Filesystem-first framework for durable backend AI agents that run anywhere.
121 lines (88 loc) • 5.24 kB
text/mdx
---
title: "Nuxt"
description: "Run an eve agent and a Nuxt app as one project with the eve/nuxt module."
---
The `eve/nuxt` module runs a Nuxt frontend and an eve agent as a single project from one dev server and one Vercel deploy. The auto-imported [`useEveAgent`](./use-eve-agent-vue) composable finds the mounted routes on its own, so there's no CORS to configure and no URL env vars to keep in sync.
## Prerequisites
- The `eve` package installed in your project (`npm install eve`).
- An existing eve agent directory. If you don't have one, start from [Getting started](../../getting-started).
- A Nuxt app to mount the agent in.
## Register the module
```ts title="nuxt.config.ts"
export default defineNuxtConfig({
modules: ["eve/nuxt"],
});
```
The module looks for an `agent/` folder in the Nuxt project root. Pass `eveRoot` when the agent lives elsewhere:
```ts
export default defineNuxtConfig({
modules: ["eve/nuxt"],
eve: {
eveRoot: "../my-agent",
},
});
```
The `eve` key accepts only two options, `eveRoot` and `eveBuildCommand`.
## Call the composable
`useEveAgent` (`eve/vue`) is auto-imported, so a component calls it without an explicit import and without naming a host:
```vue
<script setup lang="ts">
const { status, send } = useEveAgent();
const isBusy = computed(() => status.value === "submitted" || status.value === "streaming");
const message = ref("");
async function handleSubmit() {
const text = message.value.trim();
if (!text || isBusy.value) return;
message.value = "";
await send({ message: text });
}
</script>
<template>
<form .prevent="handleSubmit">
<input v-model="message" :disabled="isBusy" />
<button type="submit" :disabled="isBusy">Send</button>
</form>
</template>
```
The default eve channel is fail-closed. With no `agent/channels/eve.ts` authored, eve registers `eveChannel({ auth: [vercelOidc(), localDev()] })`: `vercelOidc()` gets the first chance to resolve a Vercel caller, `localDev()` opens the remaining localhost requests, and everything else gets a `401`. To run your own auth policy, add `agent/channels/eve.ts`:
```ts title="agent/channels/eve.ts"
import { eveChannel } from "eve/channels/eve";
import { localDev, vercelOidc } from "eve/channels/auth";
export default eveChannel({ auth: [vercelOidc(), localDev()] });
```
For a public demo, use `none()` (also from `eve/channels/auth`) to skip authentication. See [Channels](../../channels/overview) and [Auth & route protection](../auth-and-route-protection).
## Dev vs deploy topology
- **Local dev.** `npm run dev` starts the eve dev server next to `nuxt dev` and proxies the eve routes through it. As far as the browser knows, everything is the Nuxt origin.
- **Vercel.** The web app and the eve runtime deploy as a single project. On Vercel builds the module adds Build Output [`services`](https://vercel.com/docs/services) for eve and `routes` that send `/eve/v1/**` to that service before filesystem routing; the Nuxt app itself remains the default app. No `vercel.json` is required. By default the generated service runs the installed eve binary from the agent root, so the agent directory does not need its own `package.json`. When the agent needs its own build step, set `eveBuildCommand`:
```ts
export default defineNuxtConfig({
modules: ["eve/nuxt"],
eve: {
eveBuildCommand: "npm run build:eve",
},
});
```
- **Non-Vercel hosts.** Point Nuxt at a separate eve origin with `EVE_NUXT_PRODUCTION_ORIGIN`. To override the local port (default `4274`), use `EVE_NUXT_PRODUCTION_PORT`:
```bash
EVE_NUXT_PRODUCTION_ORIGIN=https://agent.example.com npm run build
EVE_NUXT_PRODUCTION_PORT=5000 npm run build && npm run preview
```
## Managing vercel.json yourself
When `vercel.json` declares [`services`](https://vercel.com/docs/services), the module generates nothing and your configuration owns routing. It must include the eve service (`framework: "eve"`) and a rewrite that exposes the eve transport, or the module fails the build:
```json title="vercel.json"
{
"services": {
"web": { "root": ".", "framework": "nuxtjs" },
"eve": { "root": "agent", "framework": "eve", "buildCommand": "eve build" }
},
"rewrites": [{ "source": "/eve/v1/(.*)", "destination": { "service": "eve" } }]
}
```
### Migrating from experimentalServices
Earlier versions of the module wrote the legacy `experimentalServices` field into `vercel.json`. Vercel no longer routes that model — deployments build both services but every `/eve/v1/*` request returns a platform NOT_FOUND — so the module now ignores the field and warns when it sees one. Migrate either way:
- **Generated (default).** Delete `vercel.json` (or just its `experimentalServices` block) and set the project's Framework Preset back to Nuxt.js. The module generates the eve service and its routing on every Vercel build.
- **Hand-maintained.** Replace `experimentalServices` with the stable `services` and `rewrites` shown above, and keep the "Services" Framework Preset.
## What to read next
- [`useEveAgent` (Vue)](./use-eve-agent-vue): the composable API
- [Auth & route protection](../auth-and-route-protection)
- [Deployment](../deployment/overview)