nuxt-api-sdk
v0.1.1
Published
Turn nuxt server routes into an SDK.
Readme
Nuxt API SDK
🚧 This module is under active development and may be subject to breaking changes. 🚧
Tired of manually typing your API routes? Say goodbye to string-based API calls and hello to a fully-typed SDK, automatically generated from your Nuxt server routes!
This module introspects your server/api directory and creates a chainable, fully-typed SDK that you can use anywhere in your Nuxt app.
Instead of this:
await $fetch(`/api/user/${uid}`, { method: 'get' })You can now do this, with full autocompletion and type-safety:
await useApi().user.uid(uid).get()Features
- ✅ Zero-config: Drop it in and it just works.
- 🤖 Automatic SDK Generation: Your
useApi()composable is always in sync with your API routes. - 🦾 End-to-end Type-Safety: Full type inference for route parameters and request bodies.
- ⛓️ Chainable API: A fluent, easy-to-read API for interacting with your server.
- 🔀 Dynamic Routes: Handles route parameters (
[uid]) and catch-alls ([...slug]), URL-encoded for you. - 🧭 Route-typed fetch: Uses Nuxt 4.6's
experimental.routeTypedFetchtypes when you turn it on.
Quick Setup
Install the module to your Nuxt application with one command:
npx nuxi module add nuxt-api-sdkThat's it! You can now use the useApi() composable in your Nuxt app ✨
Usage
1. Create your API routes
Create your API endpoints in the server/api directory as you normally would.
server/api/user/[uid].get.ts
export default defineEventHandler(async (event) => {
const uid = getRouterParam(event, 'uid')
// ... your logic here
})2. Add types to your routes (optional)
Response types are inferred from your handlers automatically.
With experimental.routeTypedFetch (Nuxt 4.6+)
If you've turned on Nuxt's route-typed fetch, the SDK uses the same types as Nuxt's $fetch, so a body or query you declare on the handler is typed (and a required body is required) with nothing extra:
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['nuxt-api-sdk'],
experimental: {
routeTypedFetch: true
}
})server/api/user/[uid]/index.patch.ts
export default defineEventHandler<{ body: Partial<User>; query: { notify?: 'yes' | 'no' } }>(
async (event) => {
// ...
}
)await useApi()
.user.uid(uid)
.patch({ body: { name: 'Jamie' }, query: { notify: 'yes' } })With defineRouteType (any Nuxt 4 version)
You can also export a body and/or query type from your route handler file using the defineRouteType helper. These take priority over the route-typed ones when both are present.
server/api/user/index.post.ts
import type { User } from '~/shared/types/User'
type Body = Omit<User, 'uid'>
export const body = defineRouteType<Body>() // <-- This defines the type for the request body
// export const query = defineRouteType<{ notify?: string }>() // <-- and this the query
export default defineEventHandler(async (event) => {
const body = await readBody<Body>(event)
// ... your logic here
})3. Use the SDK
Now you can use the useApi() composable in your components and pages. The SDK is automatically generated and will mirror your API structure.
<script setup lang="ts">
const user = ref(null)
const userUid = ref('some-user-id')
// GET /api/user/:uid
user.value = await useApi().user.uid(userUid.value).get()
// POST /api/user
const newUser = await useApi().user.post({
body: {
email: '[email protected]',
name: 'some name'
}
})
</script>The SDK will automatically update as you add, remove, or modify your API routes. Happy coding!
How routes map to the SDK
| File | SDK call |
| ---------------------------------------- | ------------------------------------------------------------------------- |
| server/api/user/index.post.ts | useApi().user.post() |
| server/api/user/[uid]/index.get.ts | useApi().user.uid(uid).get() |
| server/api/org/[org-id]/members.get.ts | useApi().org.orgId(id).members.get() |
| server/api/docs/[...slug].get.ts | useApi().docs.slug('a/b').get() |
| server/api/blog/[...].ts | useApi().blog.catchAll('a/b').get() |
| server/api/health.ts (no method) | useApi().health.get() / .post() / .put() / .patch() / .delete() |
Segments are camelCased (my-route becomes myRoute). Only routes under server/api are included; server/routes and routes starting with _ are skipped.
Advanced Usage
Bring Your Own Fetch
By default, the SDK uses Nuxt's useRequestFetch(), so calls made during SSR go straight to your server handlers and forward the incoming request's headers and cookies (falling back to the global $fetch outside a Nuxt context). If you want to use a custom fetch implementation, you can provide your own fetch function when calling the useApi() composable, as long as it has the same function signature as $fetch.
Example:
import { $fetch } from 'ofetch'
const myfetch = $fetch.create({
baseURL: 'https://api.example.com',
headers: {
Authorization: 'Bearer my-token'
}
})
const api = useApi({ fetch: myfetch })
await api.user.uid(userUid.value).get()Contribution
# Install dependencies
npm install
# Generate type stubs
npm run dev:prepare
# Develop with the playground
npm run dev
# Build the playground
npm run dev:build
# Run ESLint
npm run lint
# Run Vitest
npm run test
npm run test:watch
# Release new version
npm run release