- read

Next.js 13 + tRPC: A Match Made in Heaven

Daniel Craciun 75

Next.js 13 + tRPC: A Match Made in Heaven

Daniel Craciun
Level Up Coding
Published in
4 min readAug 30

--

Fictional image of Heaven
Fictional image of Heaven

Dive into the powerful synergy between Next.js 13 and tRPC in this comprehensive guide.

Discover how this dynamic duo empowers web developers to create blazing-fast and highly interactive applications. From leveraging Next.js 13’s cutting-edge features to harnessing tRPC's facilities that provide a type-safe way to build APIs.

Without wasting any more of your time, lets get straight into it!

Table of Contents

  • Setup Next.js 13 Project Environment
  • Dependencies Installation
  • Creating a tRPC Server
  • Configuring tRPC for Client-Side and Server-Side
  • Server tRPC Example
  • Client tRPC Example

Setup Next.js 13 Project Environment

To setup a Next.js project, please check out the instructions here. The installation process generally consists of running the following command in the terminal.

npx create-next-app@latest

Dependencies Installation

Open a terminal within your project directory, and execute one of the following commands to get started:

npm install @trpc/client @trpc/server @trpc/react-query @tanstack/react-query zod
yarn add @trpc/client @trpc/server @trpc/react-query @tanstack/react-query zod
pnpm add @trpc/client @trpc/server @trpc/react-query @tanstack/react-query zod

Creating a tRPC Server

  1. Go into the src/ folder in your Next.js 13 project — or inside the root of your project if you didn’t select the src/ folder option — and create a new folder called server with a file called trpc.ts:
import { initTRPC } from "@trpc/server";

const t = initTRPC.create();

export const router = t.router;
export const publicProcedure = t.procedure;

This is simple boilerplate code to allow you to create API procedures and routers in your Next.js 13 backend.

2. Inside the src/server/ directory, create a new file called index.ts:

import { z } from "zod"

import { publicProcedure, router } from "./trpc"

export const appRouter = router({
getData: publicProcedure.query(async () => {
// Here you would fetch data from a database in a
// real-life application.
console.log("getData")
return "getData"
}),
setData: publicProcedure
.input(z.string())
.mutation(async ({ input }) => {
// Here you would update a database using the
// input string passed into the procedure
console.log(input)
return input
}),
})
// This type will be used as a reference later...
export type AppRouter = typeof appRouter
  • We import the publicProcedure and router functions defined inside the trpc.ts file.
  • Next we create an appRouter, which acts as a hub for your API procedures.
  • Inside the appRouter I defined a trivial getData and setData function for tutorial’s sake.
  • In the setData function, the code input(z.string()) ensures the input data is validated using zod maximizing endpoint security and developer experience.

3. Go inside the app/ directory, and create a sequence of files and directories as follows: api/trpc/[trpc]/route.ts.

import { appRouter } from "@/server"
import { fetchRequestHandler } from "@trpc/server/adapters/fetch"

const handler = (req: Request) =>
fetchRequestHandler({
endpoint: "/api/trpc",
req,
router: appRouter,
createContext: () => ({}),
})

export { handler as GET, handler as POST }

This creates an API endpoint that is called when a procedure is run.

Configuring tRPC for Client-Side and Server-Side

  1. Inside the app/ directory, create a folder called _trpc and with a file called client.ts. This defines a tRPC instance that is usable inside all your Next.js 13 client-components.
import { type AppRouter } from "@/server"
import { createTRPCReact } from "@trpc/react-query"

export const trpc = createTRPCReact<AppRouter>({})

2. Within the same folder, create a file called serverClient.ts to define a tRPC instance that works for all server-side components.

import { appRouter } from "@/server"
import { httpBatchLink } from "@trpc/client"

export const serverClient = appRouter.createCaller({
links: [
httpBatchLink({
url: "http://localhost:3000/api/trpc",
}),
],
})

NOTE: You will need to change the url http:localhost:3000 when deploying your code to production, In this case, you should conditionally select the url as follows:

import { appRouter } from "@/server"
import { httpBatchLink } from "@trpc/client"

const url =
process.env.NODE_ENV === "production"
? "your-production-url/api/trpc"
: "http://localhost:3000/api/trpc"

export const serverClient = appRouter.createCaller({
links: [
httpBatchLink({ url }),
],
})

Server tRPC Example

Lets run our procedures that we’ve defined earlier inside a server component! Open up app/page.tsx and copy the following code:

import { serverClient } from "./_trpc/serverClient"

export default async function page() {
const data = await serverClient.getData()
const dataSet = await serverClient.setData("test-data")

return (
<main>
<div>{data}</div>
<div>{dataSet}</div>
</main>
)
}

Run the application using npm run dev inside a terminal window, then visit localhost:3000/; you should see the data within the UI and within the server-side terminal window.

Client tRPC Example

Now lets run the same procedures inside a client component! Open up app/page.tsx and copy the following code:

"use client"

import { trpc } from "@/app/_trpc/client"

export default async function page() {
const getData = trpc.getData.useQuery({
// your react-query properties ...
})

const setData = trpc.setData.useMutation({
// your react-query properties ...
})

return (
<main>
<div>{getData.data}</div>
<div>{setData.data}</div>
</main>
)
}

Conclusion:

In both the server-side and client-side examples, you may have noticed that the data returned is 100% type-safe! This is the main reason tRPC is so great!

Your Next.js 13 project workflow should now increase tenfold as the data from external endpoints is 100% validated + type-safe, ready to bring your projects to life.

I hope you’ve enjoyed this article! You now have a fully functional and scalable tRPC backend within your Next.js 13 project!

If you have any questions and concerns, please be sure to let me know, and as always, stay tuned for more! 👍

Level Up Coding

Thanks for being a part of our community! Before you go:

🔔 Follow us: Twitter | LinkedIn | Newsletter

🚀👉 Join the Level Up talent collective and find an amazing job