Konabos

Building a Full-Stack App with Next.js, tRPC, Drizzle ORM & Neon Database

Jose Raimondi - Front-End Developer

3 Mar 2025

Share on social media

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?

  • Next.js: A powerful React framework with built-in API routes.
  • tRPC: Type-safe APIs without needing REST or GraphQL.
  • Drizzle ORM: A TypeScript-first ORM with a great developer experience.
  • Neon Database: A serverless Postgres database that will work for our example.

Setting Up the Project

  1. Initialize Next.js with typescript:

    npx create-next-app@latest recipes-app --typescript
    cd recipes-app
  2. Install dependencies

    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

Creating a Neon Database

1. Sign Up for Neon

Go to Neon.tech and sign up for an account. Once logged in, create a new project and select PostgreSQL as your database.

2. Retrieve Your Database Connection String

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

3. Set Up Environment Variables

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

Setting up tRPC`

1. Create the tRPC Router

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

2. Create the Recipes Router

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.

3. Create TRPC handler:

you can add this inside inside api/trpc/[trpc]/route.ts file


This will ensure each tRPC route is handled correctly.

Configuring Drizzle ORM with Neon Database

1. Setup Database Connection

Create db/drizzle.ts:


Drizzle ORM allows us to interact with our Neon database using a clean, type-safe API.

2. Define Recipe Model

Create db/schema.ts:


This schema defines a recipes table with an unique id, a title, a JSON array of ingredients, and a textfield for instructions.

3. Create drizzle config

Create drizzle.config.ts on the root of your project:


Ensure that schema path is pointing to the correct file 

4. Run Database Migrations

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

Using tRPC in the Frontend

1. Setup tRPC Provider

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.

2. Fetch Recipes in a Component

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

3. Create form to edit and create recipes

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.

Sign up to our newsletter

Share on social media

Jose Raimondi

Jose Raimondi

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.


Subscribe to newsletter