nuxt-endpoints
Nuxt Endpoints
Typed APIs, generated clients, and OpenAPI for Nuxt server routes — from one endpoint definition.
Nuxt 4 today, designed for the Nuxt 5 generation. Nuxt Endpoints provides the
$endpoint/useEndpointexperience now and will adopt H3, Nitro, and Nuxt primitives upstream as they become available, while keeping application-facing APIs as stable as practical.
One definition, everything typed
For a runnable contract-to-client example, see the cursor pagination walkthrough: inspect the HTTP fields, server type errors, and Pinia Colada page loading from one declaration.
Describe the HTTP contract once, next to the handler, with the schema library you already use (Zod, Valibot, or Effect Schema):
// server/api/users/[id].get.ts
import { z } from 'zod'
export default defineEndpoint({
name: 'getUser',
summary: 'Get a user',
request: {
params: z.object({ id: z.coerce.number() }),
},
responses: {
200: z.object({ id: z.number(), name: z.string() }),
404: z.object({ message: z.string() }),
},
handler: (event) => {
const { params } = event.validated
const user = findUser(params.id) // params.id is a number — already validated and coerced
if (!user) return event.respond(404, { message: 'Not found' })
return user
},
})
That single definition gives you:
1. Runtime validation — params, query, headers, and body are validated before the handler runs. Handler code sees the parsed schema output, so coercion and transforms are already applied.
Declared response bodies and headers are also checked while developing. Production skips that
second traversal by default; set validation.response in server/endpoints/runtime.ts to
'always' or 'never' when the application needs a different policy.
2. A fully typed client — no codegen step to run, no types to import:
<script setup lang="ts">
// The lazy request resolves to the declared status union.
const result = await $endpoint.getUser({
params: { id: '1' },
})
if (result.status === 404) {
result.body.message // typed as the 404 schema
}
</script>
3. An OpenAPI 3.1 document — served at /_endpoints/schema in dev, generated from the same schemas. No separate spec to maintain.
The boundary stays an explicit, file-based Nuxt HTTP route, callable by mobile
apps, other services, or curl. Nuxt Endpoints extends the handler contract; it
does not replace the route with a remote function.
Features
- ✅ Schema-agnostic: Zod v4, Valibot, and Effect Schema (Standard Schema based)
- ✅ Request validation for
params,query,headers, andbody - ✅ Multiple response statuses, checked at the type level via
respond(status, body) - ✅ Lazy
$endpointrequest objects: status unions and.raw() - ✅ Optional named aliases such as
$endpoint.getUser({ params }), while path and method stay canonical - ✅ Status-aware
useEndpointcomposable wired into Nuxt async data (key,lazy,watch, …) - ✅ SSR-correct without replacing Nuxt's transport:
useEndpointand Colada query options forward the incoming request - ✅ OpenAPI 3.1 generation, extensible via
document/extend - ✅ Importable path, method, request, and result helper types from
#endpoints - ✅ Typed Pinia Colada adapters for queries, mutations, and cursor pagination, with its official Nuxt SSR module
- ✅ Cursor pagination from one item contract through validation, OpenAPI, and
useInfiniteQuery - ✅ Optional
Idempotency-Keyreplay protection with an application-owned durable storage contract and a development-only memory adapter - ✅ Progressive enhancement:
useEndpointFormprojects GET query forms and POST mutation forms that work before hydration and with no JavaScript, from the same contract (docs)
Quick Start
Requires Nuxt 4.5 or newer — see Compatibility for the supported platform line.
npx nuxt module add nuxt-endpoints
Then install the schema library used by your endpoint definitions — zod, valibot, and effect are optional peer dependencies:
npm install zod
# or: npm install valibot
# or: npm install effect
That's it. Adding the module changes nothing by itself: only routes whose default export is a direct defineEndpoint({...}) call are affected, and existing routes keep working unchanged. Create a route like the one above and call it with $endpoint.
Nuxt 4 now, ready to follow upstream
Nuxt Endpoints brings the route-contract model being developed for the Nuxt 5 generation to Nuxt 4.5+ today. On Nuxt 4, the module currently supplies the missing build-time discovery, validation, and contract plumbing itself.
$endpoint and useEndpoint are Nuxt Endpoints' application-facing UX, not
temporary substitutes for an upstream client. They remain useful regardless of
where the lower-level primitives live. As H3, Nitro, and Nuxt gain suitable
contract APIs, this project will delegate to them and keep only the integration
and UX they do not provide. The goal is to avoid maintaining the same machinery
twice while preserving application code wherever practical.
Upstream APIs are still evolving, so an absolutely change-free migration cannot be promised. Compatibility at the Nuxt Endpoints boundary is nevertheless a design constraint, and any unavoidable change will be documented as a migration.
The work is public: follow the Nuxt 5 integration progress for the Nuxt Endpoints branch and the matching H3, Nitro, and fetchdts forks.
Module options (OpenAPI route and optional client methods) are configured under endpoints in nuxt.config.ts — see Getting Started.
Application or middleware responses shared by several routes can be declared by path in server/routes.config.ts; $endpoint and OpenAPI inherit those statuses without changing middleware execution.
Documentation
Guides:
- Getting Started — install, configure, and what gets generated
- Define Endpoints — request parts, multiple responses, non-JSON responses, response validation
- Generated Client —
$endpoint,useEndpoint, and helper types from#endpoints - Responses — status-aware and raw response shapes
- Pinia Colada — typed query, mutation, and cursor-pagination adapters with official Nuxt SSR setup
- OpenAPI — schema route, document metadata,
document/extend - Schema Libraries — Zod v4, Valibot, and Effect Schema specifics
- Idempotency — optional
Idempotency-Keyreplay protection - Low-level HTTP — files, redirects, proxies, and raw responses
- Incremental Adoption — convert routes one at a time
Concepts, for when you want the reasoning behind the design:
- Why Nuxt Endpoints? — the drift problem and the single-contract idea
- Comparison — vs plain
$fetchtyping, tRPC, and spec-first codegen - Mental Model — how the pieces fit together
- Limits — supported platform line, known constraints, and planned work
Maintainer design notes and the detailed roadmap live in docs/.
Contributing
See CONTRIBUTING.md for local setup, checks, and guidelines.
License
MIT. See Third-Party Notices for attributions that apply to bundled documentation assets.
Related Packages
Auto-generate Nitro defineRouteMeta (OpenAPI) for Nuxt server routes from JSDoc...
Type-safe Nuxt actions with advanced forms, declared business errors and mutatio...
Type-safe server actions for Nuxt with Standard Schema validation (Zod, Valibot,...
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 0.12.0 | ^3.0 | MIT | |
| 0.11.1 | ^3.0 | MIT | |
| 0.11.0 | ^3.0 | MIT | |
| 0.10.0 | ^3.0 | MIT | |
| 0.9.0 | ^3.0 | MIT | |
| 0.8.0 | ^3.0 | MIT | |
| 0.7.2 | ^3.0 | MIT | |
| 0.7.1 | ^3.0 | MIT | |
| 0.7.0 | ^3.0 | MIT | |
| 0.6.0 | ^3.0 | MIT | |
| 0.5.0 | ^3.0 | MIT | |
| 0.4.0 | ^3.0 | MIT | |
| 0.3.0 | ^3.0 | MIT | |
| 0.2.0 | ^3.0 | MIT | |
| 0.1.1 | ^3.0 | MIT | |
| 0.1.0 | ^3.0 | MIT |