End-to-end typesafe APIs with tRPC.io in Nuxt applications.
The client above is not importing any code from the server, only its type declarations.
npm i trpc-nuxt@0.3
// nuxt.config.ts
import { defineNuxtConfig } from 'nuxt'
export default defineNuxtConfig({
modules: ['trpc-nuxt'],
trpc: {
baseURL: '', // Set empty string (default) to make requests by relative address
endpoint: '/trpc', // defaults to /trpc
installPlugin: true, // defaults to true. Add @trpc/client plugin and composables
},
typescript: {
strict: true // required to make input/output types work
}
})
Expose your tRPC routes under ~/server/trpc/index.ts
:
// ~/server/trpc/index.ts
import type { inferAsyncReturnType } from '@trpc/server'
import * as trpc from '@trpc/server'
import { z } from 'zod' // yup/superstruct/zod/myzod/custom
export const router = trpc.router()
// queries and mutations...
.query('getUsers', {
async resolve(req) {
// use your ORM of choice
return await UserModel.all()
},
})
.mutation('createUser', {
// validate input with Zod
input: z.object({ name: z.string().min(5) }),
async resolve(req) {
// use your ORM of choice
return await UserModel.create({
data: req.input,
})
},
})
Use the client like so:
const client = useClient() // auto-imported
const users = await client.query('getUsers')
const newUser = await client.mutation('createUser', {
name: 'wagmi'
})
A thin wrapper around useAsyncData
and client.query()
.
The first argument is a [path, input]
-tuple - if the input
is optional, you can omit the, input
-part.
You'll notice that you get autocompletion on the path
and automatic typesafety on the input
.
const {
data,
pending,
error,
refresh
} = await useAsyncQuery(['getUser', { id: 69 }], {
// pass useAsyncData options here
lazy: false
})
A composable that lets you add additional properties to pass to the tRPC Client. It uses useState
from nuxt 3.
const headers = useClientHeaders()
const { data: token } = await useAsyncQuery(['auth.login', { username, password }])
headers.value.Authorization = `Bearer ${token}`
// All client calls will now include the Authorization header.
If your app needs transformers and other option that needs to be passed in @trpc/client
, you will need to opt-out of the plugin by setting installPlugin
to false in your nuxt.config.ts
file and create your own. You can just copy the contents of the client plugin here.
trpc-nuxt accepts the following options exposed under ~/server/trpc/index.ts
:
import * as trpc from '@trpc/server'
import type { inferAsyncReturnType } from '@trpc/server'
import type { H3Event } from 'h3'
import type { OnErrorPayload } from 'trpc-nuxt/api'
export const router = trpc.router<inferAsyncReturnType<typeof createContext>>()
// Optional
// https://trpc.io/docs/context
export const createContext = (event: H3Event) => {
// ...
return {
/** context data */
}
}
// Optional
// https://trpc.io/docs/caching#using-responsemeta--to-cache-responses
export const responseMeta = () => {
// ...
return {
// { headers: ... }
}
}
// Optional
// https://trpc.io/docs/error-handling#handling-errors
export const onError = (payload: OnErrorPayload<typeof router>) => {
// Do whatever here like send to bug reporting and stuff
}
Learn more about tRPC.io here.
MIT