Jose Raimondi - Front-End Developer
3 Mar 2025
In this blog post, we’ll walk through setting up a modern full-stack application using tRPC, Next.js, Drizzle ORM, Neon Database, and Zod. Our project will be a simple recipes app, where users can add and retrieve recipes with structured validation.
Why These Technologies?
npx create-next-app@latest recipes-app --typescript
cd recipes-app
npm install @trpc/server @trpc/client @trpc/react-query @trpc/next superjson
npm install drizzle-orm pg neon zod @hookform/resolvers
npm install -D @types/node @types/react
Go to Neon.tech and sign up for an account. Once logged in, create a new project and select PostgreSQL as your database.
Once your database is created, go to the Connection tab and copy the PostgreSQL connection string. It should look like this:
postgres://user:password@your-neon-db.neon.tech/dbname
Create a .env
file in the root of your project and add your database URL:
DATABASE_URL=postgres://user:password@your-neon-db.neon.tech/dbname
Create a new folder server/trpc
and inside, add trpc.ts
:
Here, initTRPC.create()
initializes the tRPC instance with SuperJSON for automatic serialization and deserialization of complex data structures. We can also format errors to the structure that we want
Inside server/index
, create index.ts
file where we can add our router:
A router in tRPC acts as a central hub for defining API endpoints. Each router groups related procedures (functions) that handle different operations, such as retrieving or modifying data. It allows us to define queries (data fetching) and mutations (data modifications) in a type-safe manner.
you can add this inside inside api/trpc/[trpc]/route.ts
file
This will ensure each tRPC route is handled correctly.
Create db/drizzle.ts
:
Drizzle ORM allows us to interact with our Neon database using a clean, type-safe API.
Create db/schema.ts
:
This schema defines a recipes
table with an unique id, a title
, a JSON array of ingredients
, and a text
field for instructions
.
Create drizzle.config.ts on the root of your project:
Ensure that schema path is pointing to the correct file
To ensure our database is in sync with our schema, we generate and apply migrations. After you do this you should be able to see the tables in your neon DB
drz --config drizzle.config.ts generate
drz --config drizzle.config.ts push
Create _trpc
folder and define clients.ts
file:
This initializes a tRPC client that can be used throughout the app.
Then we can create Provider.tsx
(Notice how trpc takes advantage of tanstack react-query as well)
And add the Provider to your root layout:
This setup ensures that our entire application has access to the tRPC client and React Query for caching and state management.
Create components/Recipes.tsx
:
and Recipes.hooks.ts
for handling logic
If you have every used tanstack react-query, you will find this code very familiar as it works on a similar way. Also you will be able to see how every request method is strongly typed!
You should as well add an individual Recipe card component
Add CreateOrEditRecipe.tsx
As well as CreateOrEditREcipe.hooks.ts
to handle the logic (here you can define the behaviour for successful mutations as well as errored ones):
And there you have it! by combining Next.js, tRPC, Drizzle ORM, Neon Database, and Zod, we built a fully type-safe, modern recipes app with great developer experience. This stack provides scalability and maintainability while avoiding unnecessary complexity.
If you have any questions or improvements, don't be shy to reach out! and please feel free to play with the structure and the logic as you like, since this is just an example.
Jose is a Front-End Developer with a love for building new things. He finds the idea of working with the latest technologies such as React.js, Next.js, Kentico Kontent, Tailwind CSS, etc, very
thrilling. Coming from a musical background, he sees that software engineering shares something with music, which is creativity. Embracing the challenge of learning more every day, as technology evolves there will always be excitement for him in the field.
Share on social media