Next.js Adapter
Next.js is a leading React framework for server-rendered apps. oRPC works with both the App Router and Pages Router. For additional context, refer to the HTTP Adapter guide.
INFO
oRPC also provides out-of-the-box support for Server Action with no additional configuration required.
Server
You set up an oRPC server inside Next.js using its Route Handlers.
import { RPCHandler } from '@orpc/server/fetch'
const handler = new RPCHandler(router)
async function handleRequest(request: Request) {
const { response } = await handler.handle(request, {
prefix: '/rpc',
context: {}, // Provide initial context if needed
})
return response ?? new Response('Not found', { status: 404 })
}
export const HEAD = handleRequest
export const GET = handleRequest
export const POST = handleRequest
export const PUT = handleRequest
export const PATCH = handleRequest
export const DELETE = handleRequest
INFO
The handler
can be any supported oRPC handler, such as RPCHandler, OpenAPIHandler, or another custom handler.
Pages Router Support?
import { RPCHandler } from '@orpc/server/node'
const handler = new RPCHandler(router)
export const config = {
api: {
bodyParser: false,
},
}
export default async (req, res) => {
const { matched } = await handler.handle(req, res, {
prefix: '/rpc',
context: {}, // Provide initial context if needed
})
if (matched) {
return
}
res.statusCode = 404
res.end('Not found')
}
WARNING
Next.js body parser may handle common request body types, and oRPC will use the parsed body if available. However, it doesn't support features like Bracket Notation, and in case you upload a file with application/json
, it may be parsed as plain JSON instead of a File
. To avoid these issues, disable the body parser:
export const config = {
api: {
bodyParser: false,
},
}
Client
By leveraging headers
from next/headers
, you can configure the RPC link to work seamlessly in both browser and server environments:
import { RPCLink } from '@orpc/client/fetch'
const link = new RPCLink({
url: `${typeof window !== 'undefined' ? window.location.origin : 'http://localhost:3000'}/rpc`,
headers: async () => {
if (typeof window !== 'undefined') {
return {}
}
const { headers } = await import('next/headers')
return await headers()
},
})
INFO
This only shows how to configure the link. For full client examples, see Client-Side Clients.
Optimize SSR
To reduce HTTP requests and improve latency during SSR, you can utilize a Server-Side Client during SSR. Below is a quick setup, see Optimize SSR for more details.
import type { RouterClient } from '@orpc/server'
import { RPCLink } from '@orpc/client/fetch'
import { createORPCClient } from '@orpc/client'
declare global {
var $client: RouterClient<typeof router> | undefined
}
const link = new RPCLink({
url: () => {
if (typeof window === 'undefined') {
throw new Error('RPCLink is not allowed on the server side.')
}
return `${window.location.origin}/rpc`
},
})
/**
* Fallback to client-side client if server-side client is not available.
*/
export const client: RouterClient<typeof router> = globalThis.$client ?? createORPCClient(link)
import 'server-only'
import { headers } from 'next/headers'
import { createRouterClient } from '@orpc/server'
globalThis.$client = createRouterClient(router, {
/**
* Provide initial context if needed.
*
* Because this client instance is shared across all requests,
* only include context that's safe to reuse globally.
* For per-request context, use middleware context or pass a function as the initial context.
*/
context: async () => ({
headers: await headers(), // provide headers if initial context required
}),
})
export async function register() {
// Conditionally import if facing runtime compatibility issues
// if (process.env.NEXT_RUNTIME === "nodejs") {
await import('./lib/orpc.server')
// }
}
import '../lib/orpc.server' // for pre-rendering
// Rest of the code