惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

P
Proofpoint News Feed
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Microsoft Security Blog
Microsoft Security Blog
云风的 BLOG
云风的 BLOG
MongoDB | Blog
MongoDB | Blog
Know Your Adversary
Know Your Adversary
K
Kaspersky official blog
人人都是产品经理
人人都是产品经理
IT之家
IT之家
Recorded Future
Recorded Future
Recent Announcements
Recent Announcements
D
DataBreaches.Net
C
CXSECURITY Database RSS Feed - CXSecurity.com
U
Unit 42
博客园 - 【当耐特】
I
Intezer
Cisco Talos Blog
Cisco Talos Blog
Spread Privacy
Spread Privacy
L
LINUX DO - 热门话题
H
Help Net Security
L
LangChain Blog
T
The Exploit Database - CXSecurity.com
P
Palo Alto Networks Blog
Scott Helme
Scott Helme
The GitHub Blog
The GitHub Blog
博客园_首页
P
Proofpoint News Feed
大猫的无限游戏
大猫的无限游戏
S
Secure Thoughts
The Register - Security
The Register - Security
Engineering at Meta
Engineering at Meta
T
Threatpost
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Blog — PlanetScale
Blog — PlanetScale
C
Cybersecurity and Infrastructure Security Agency CISA
T
Troy Hunt's Blog
罗磊的独立博客
腾讯CDC
Simon Willison's Weblog
Simon Willison's Weblog
Security Latest
Security Latest
Last Week in AI
Last Week in AI
P
Privacy & Cybersecurity Law Blog
Latest news
Latest news
The Cloudflare Blog
Jina AI
Jina AI
小众软件
小众软件
Cloudbric
Cloudbric
TaoSecurity Blog
TaoSecurity Blog
F
Full Disclosure
V
V2EX

Clerk Blog

Going to production with Clerk Deploy Clerk Init: The fastest way to start a new project Introducing Clerk CLI Middleware-based route protection bypass Postmortem: Clerk System Outage (March 10, 2026) Clerk for the AI era Add API Key support to your SaaS in minutes Postmortem: Clerk System Outage (February 19, 2026) Using Clerk in a React Native app Postmortem: DNS Provider Outage (February 10, 2026) How do I implement passkeys in Next.js? Clerk ranked #4 fastest-growing software vendor on Ramp’s December 2025 list How do I handle JWT verification in Next.js? Committing to Agent Identity: Clerk raises $50m Series C from Menlo and Anthropic’s Anthology Fund What is the best way to handle authentication in Next.js App Router? Postmortem: Database Incident (September 14–18, 2025) How do I add authentication to a Next.js app? Introducing Free Trials in Clerk Billing Postmortem: August 28, 2025 - elevated API latency and errors Introducing Mosaic: Bring Your Brand to Every Authentication Flow Multi-tenant authentication: What you need to know (and how Clerk helps) What are the risks and challenges of multi-tenancy? Resilience in Practice: Regional Failover at Clerk Build a Cross-Platform B2B App with Clerk, Expo, and Supabase Highlights from the MiduDev/Clerk Hackathon Add multi-tenancy to an app built with Clerk, Lovable, and Supabase How to build an AI coding rules app with Clerk, Lovable, and Supabase How to Build Multi-Tenant Authentication with Clerk Choosing the right SaaS architecture: Multi-Tenant vs. Single-Tenant Postmortem: June 26, 2025 service outage How to Design a Multi-Tenant SaaS Architecture What is multi-tenancy and why it matters for B2B SaaS How OAuth Works Synchronize user data from Clerk to Supabase Add subscriptions to your SaaS with Clerk Billing Getting started with Clerk Billing Multi-tenant analytics with Tinybird and Clerk How Huntr Migrated 250K Users to Clerk: A Scalable Auth Solution for Startups How to take Clerk to Production How to take your Clerk application to production A practical guide to testing Clerk Next.js applications Implementing multi-tenancy into a Supabase app with Clerk How Clerk integrates with a Next.js application using Supabase How Clerk integrates with Supabase How to enrich PostHog events with Clerk user data How to build a secure project management platform with Next.js, Clerk, and Neon Validate your SaaS idea while building an audience Postmortem: February 6, 2025 service outage Implement Role-Based Access Control in Next.js 15 Build a Next.js sign-up form with React Hook Form Build a Next.js login page template How to implement Google authentication in Next.js 15 What is middleware in Next.js? How to customize Next.js metadata How to set environment variables in Node.js Building a React Login Page Template How to implement per-user OAuth scopes with Clerk Using Clerk SSO to access Google Calendar and other service data Streamline enterprise customer onboarding with SAML and Clerk Clerk launches EASIE SSO and eliminates SSO fees How to secure Liveblocks Rooms with Clerk in Next.js Securing Node.js Express APIs with Clerk and React Combining the benefits of session tokens and JWTs Build a task manager with Next.js, Supabase, and Clerk Comparing Clerk Webhooks vs Backend API Automate Neon schema changes with Drizzle and GitHub Actions A guide to reading authenticated user data from Clerk Role based access control with Clerk Organizations Mitigating OAuth’s recently discovered Open Response Type vulnerability Per-user B2B monetization with Stripe and Clerk Organizations Build a team-based task manager with Next.js, Neon, and Clerk Building a Hybrid Sign-Up/Subscribe Form with Stripe Elements Welcoming Colin from Zod as our inaugural Open Source Fellow Build a modern authenticated chat application with Next.js, Ably, and Clerk Build a waitlist with Clerk user metadata How to use Clerk with PostHog Identify in Next.js How to secure API Gateway using JWT and Lambda Authorizers with Clerk What are passkeys and how do they work? Comparing Authentication in React.js vs. Next.js How to Add an Onboarding Flow for your Application with Clerk Create Your Own Custom User Menu with Radix - Part 2 Introducing Webhook Workflows with Inngest & Svix Clerk raises $30M Series B from CRV and Stripe Clerk in 2023: A Year in Review Build a Movie Emoji Quiz App with Remix, Fauna, and Clerk Ultimate Guide to Magic Link Authentication Create Your Own Custom User Menu with Radix Introducing has(), protect(), and <Protect> Updated Pricing: 10,000 MAUs Free, and a new “Pro Plan” Next.js Authentication with Clerk: Streamlined SSR Handling Clerk Webhooks: Data Sync with Convex Exploring Clerk Metadata with Stripe Webhooks The Ultimate Guide to Next.js Authentication Empower Your Support Team With User Impersonation Clerk Webhooks: Getting Started A Complete Guide to Session Management in Next.js The Advanced Guide to Passwordless Authentication in Next.js How We Roll – Chapter 10: Roundup How We Roll – Chapter 9: Infrastructure Password-Based Authentication in Next.js
Build a blog with tRPC, Prisma, Next.js and Clerk
Alexis Aguilar · 2025-03-14 · via Clerk Blog

In this tutorial, you'll build a blog app from scratch using many modern and popular technologies such as Next.js, Clerk, tRPC, Prisma, and more. After reading this guide, you'll have a simple blog application that allows users to create and read posts.

Let's explore the various technologies used in the article:

  • Next.js is the React framework used throughout this guide, specifically using the App Router.
  • Clerk is used for user management and authentication.
  • Prisma will be the ORM used to connect to the database.
  • Vercel is used for hosting and automated deployments.
  • Neon is a serverless Postgres database, and you'll actually use Vercel to automate deployment of the database as well.
  • Tanstack Query simplifies the process of fetching and caching data.
  • tRPC provides a type-safe API endpoint wrapper around Tanstack Query.
  • Zod is used for schema validation.
  • Tailwind provides a simple and modern way to style your app with CSS.

You'll start by creating a Next.js app and integrating Clerk into it for authentication. Then you'll deploy the application to Vercel, where you will also create a Neon database that will be used by Prisma to access and manipulate data within that database. At this point the application will be fully functional, however the rest of the tutorial further enhances the type-safety of your app by adding Tanstack Query, tRPC, and zod. Finally, you'll learn how to create protected procedures using Clerk's authentication context.

To follow along, you should have Node.js installed on your computer and a Vercel account. Familarity with React and Next.js is recommended as well.

Setting up a Next.js application with Clerk

Start by opening your terminal and running the following command to create a new Next.js application. When prompted for the various configuration options, use the options specified below:

Once the application has been created, follow the quickstart in the docs to add Clerk to it.

Alternatively, you can clone the Clerk Next.js quickstart repository. This repository contains a pre-configured Next.js app with Clerk already added in keyless mode, which allows you to test the authentication features in your app locally without having to create an account.

Create a Clerk application

Since keyless mode only works for local development, you will want to create a Clerk account and an application in the dashboard to deploy your application to Vercel.

The Clerk Dashboard is where you, as the application owner, can manage your application's settings, users, and organizations. For example, if you want to enable phone number authentication, multi-factor authentication, social providers like Google, delete users, or create organizations, you can do all of this and more in the Clerk Dashboard.

Set your Clerk API keys

You need to set your Clerk API keys in your app so that your app can use the configuration settings that you set in the Clerk Dashboard.

  1. In the Clerk Dashboard, navigate to the API keys page.
  2. In the Quick Copy section, copy your Clerk Publishable and Secret Keys.
  3. In your .env file, set the NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY and CLERK_SECRET_KEY environment variables to the values you copied from the Clerk Dashboard.

Install dependencies and test your app

While developing, it's best practice to keep your project running so that you can test your changes as you work. So, let's make sure the app is working as expected.

  1. Run the following commands to install the dependencies and start the development server:
  2. Open your browser and navigate to the URL displayed in your terminal. The default is http://localhost:3000 and will be used through the remainder of the tutorial. It should render a new Next.js app, but with a "Sign in" and "Sign up" button in the top right corner.

    The development instance running.

  3. Select the "Sign in" button. You should be redirected to your Clerk Account Portal sign-in page, which renders Clerk's <SignIn /> component. The <SignIn /> component will look different depending on the configuration of your Clerk instance.

    A Clerk Account Portal sign-in page.

  4. Sign in to your Clerk application.
  5. You should be redirected back to your app, where you should see Clerk's <UserButton /> component in the top right corner.

Install Prisma ORM

Run the following command to install Prisma:

Then run npx prisma init to initialize Prisma in your project.

This will create a new prisma directory in your project, with a schema.prisma file inside of it. The schema.prisma file is where you will define your database models.

The prisma init command will also update your .env file to include a DATABASE_URL environment variable, which is used to store your database connection string. If you have a database already, great! If not, let's spin one up using Vercel.

Deploy to Vercel

Before you can create a database using Vercel, you first need to deploy your app to Vercel.

  1. Create a repository on GitHub for your app. If you're not sure how to do this, follow the GitHub docs.
  2. Go to Vercel and add a new project. While going through the process, select the Environment Variables dropdown, and add your Clerk Publishable and Secret Keys.

    Vercel dashboard showing where to input environment variables

  3. Select Deploy to deploy your app to Vercel.
  4. Select the Settings tab.
  5. In the left sidenav, select Functions.
  6. Under Function Region, there should be a tag next to one of the continents. Select the continent where the tag is, and the dropdown will reveal what regions on Vercel's network that your Vercel Functions will execute in. Take note of the region. Keep the Vercel dashboard open.

    Vercel dashboard with an arrow pointing to a tag that says "iad1", and an arrow pointing to a highlighted element that says "Washington, D.C., USA (EAST) - us-east-1 - iad1"

Spin up a database

  1. While still in Vercel's dashboard, select the Storage tab.
  2. Select Create Database.
  3. Select Neon as the database provider and select Continue.
  4. Select the Region dropdown and select the region you noted earlier. You want your database's region to match your Vercel Functions region for optimal performance.
  5. Select Continue.
  6. Copy the environment variables and add them to your .env file. They should look something like this:

Create a database model

Now that your database is created and connected to your app, it's time to create a database model. The main entity of the application is a Post that represents each entry in the blog app, so add the following Post model to your schema.prisma file:

Update your database schema

Run the following command to apply your schema to your database:

This creates an initial migration creating the Post table and applies that migration to your database.

Set up Prisma Client

Now it's time to set up the Prisma Client and connect it to your database. You'll want to create a single client and bind it to the global object so that only one instance of the client is created in your application. This helps resolve issues with hot reloading that can occur when using Prisma with Next.js in development mode.

Create the lib/prisma.ts file and add the following code to it:

lib/prisma.ts

Query your database

Now that all of the set up is complete, it's time to start building out your app!

Let's start with your homepage. Replace the contents of app/page.tsx with the following code:

app/page.tsx

This code fetches all posts from your database and displays them on the homepage, showing the title and author ID for each post. It uses the prisma.post.findMany() method, which is a Prisma Client method that retrieves all records from the database.

That shows how to query for all records, but how do you query for a single record?

Query a single record

Let's add a page that displays a single post. This page uses the URL parameters to get the post's ID, and then fetches it from your database and displays it on the page, showing the title, author ID, and content. It uses the prisma.post.findUnique() method, which is a Prisma Client method that retrieves a single record from the database.

Create the app/posts/[id]/page.tsx file and paste the following code in it:

app/posts/[id]/page.tsx

Test the page by navigating to a post's URL. For example, http://localhost:3000/posts/1. For now, it should show a "No post found" message because you haven't created any posts yet. Let's add a way to create posts.

Create a new post

Next you'll create a page that allows users to create new posts. This page uses Clerk's auth() helper to get the user's ID. It is a helper that is specific to Next.js App Router, and it provides authentication information on the server side.

  • If there is no user ID, the user is not signed in, so a sign in button is displayed.
  • If the user is signed in, the "Create New Post" form is displayed. When the form is submitted, the createPost() function is called. This function creates a new post in the database using the prisma.post.create() method, which is a Prisma Client method that creates a new record in the database.

Create the app/posts/create/page.tsx file and paste in the following code:

app/posts/create/page.tsx

Test the page by navigating to the /posts/create page (ex: http://localhost:3000/posts/create) and create a new post. You should be redirected to the homepage, where you should see the new post.

Configure tRPC, @tanstack/react-query, and zod

Now, you've got a Next.js, Clerk, and Prisma app that can create and display posts. You could stop here and have a perfectly functional app. But let's take it a step further and add tRPC to your app for type-safe API endpoints.

Install the dependencies

Let's start by installing the following dependencies:

  • trpc is a wrapper around your API endpoints to make them type-safe and easier to use.
  • zod is a schema validation library, also used to enhance your app's type safety.
  • @tanstack/react-query is a library for data fetching and caching.

Run the following command to install the packages:

At the time of writing, clerk-next-app includes React 19 as a peer dependency, but @tanstack/react-query does not. So, you'll need to use the --force flag when running the command above. You may not need the --force flag in the future.

Create a tRPC server

Now, you'll configure tRPC for your app. You'll start by initializing a tRPC server that creates a router and publicProcedure that you can use to create your API endpoints.

Create the app/server/trpc.ts file and paste in the following code

app/server/trpc.ts

Create a tRPC endpoint

Now, you'll create a router that's going to have your procedures on it. The following code creates a router with a getPosts procedure that uses the tRPC publicProcedure you created in the previous step to make a query using tRPC's query() method. The query then uses Prisma to query the Post model in your database. That part should look familiar, because you've used prisma.post.findMany() in your app earlier!

Create the app/server/routers/posts.ts file and paste in the code below:

app/server/routers/posts.ts

This is the file where you'll add all of your queries and mutations, so you'll probably update this file frequently as you build out your app.

Connect the tRPC router to your App Router

Now you need to connect the tRPC router to your App Router. You'll use a Route Handler that uses tRPC's fetchRequestHandler() method to pass requests from Next.js to the tRPC router.

Create the app/api/trpc/[trpc]/route.ts file and paste in the code below:

app/api/trpc/[trpc]/route.ts

At this point, your API endpoint should be working. You can test it by navigating to http://localhost:3000/api/trpc/getPosts. You should see a JSON response with the posts from your database.

Create a tRPC client

So far, your app is entirely server-side and static. You need a way to mutate data, which is where @tanstack/react-query comes in. But to use tRPC with @tanstack/react-query, you need to create a tRPC client.

Create the app/_trpc/client.ts file and paste in the code below:

app/_trpc/client.ts

Create a Tanstack Query + tRPC provider

To use Tanstack Query and tRPC together, you need to create a provider using the React Context API. This provider will make both the Tanstack Query client and the tRPC client available to your app using the trpc.Provider and QueryClientProvider components.

Create the app/_trpc/Provider.tsx file and paste in the code below:

app/_trpc/Provider.tsx

Now, wrap your app in the provider. Update the main layout to import the provider as TRPCProvider and wrap your app in it. It's very important that <ClerkProvider> is wrapped around <TRPCProvider>, and not the other way around, because the <TRPCProvider> needs to have access to the Clerk authentication context.

In app/layout.tsx, add the following code:

app/layout.tsx

Now, you can use the trpc client to fetch and mutate data in your app! Let's update the functionality of your app to use the trpc client.

Use the tRPC client to fetch and mutate data

Let's start by updating the homepage where the list of posts is rendered. Since the page is still rendered server-side, you'll create a client component that uses the trpc client to fetch posts.

Create the app/components/Posts.tsx file and paste in the following code:

app/components/Posts.tsx

Then, update the homepage to use the <Posts /> component:

app/page.tsx

Notice that the prisma.post.findMany() function is no longer used. Instead, your app is using trpc.getPosts.useQuery() in the <Posts /> component to fetch the posts, because remember, you created a tRPC postRouter with a getPosts procedure that uses prisma.post.findMany(). So now, you don't need to use Prisma directly, you can use tRPC in order to have type safety and a better developer experience. Let's update the rest of your app to use tRPC.

Of course, let's test and make sure the new logic is working. Navigate to the homepage and make sure you can see the posts.

Once you've verified everything's working, let's go back to your postRouter and create more procedures to handle your other queries.

Use tRPC to fetch a single post

In app/server/routers/posts.ts, update the code to match the following:

app/server/routers/posts.ts

This adds a getPost procedure to fetch a single post by ID.

In app/posts/[id]/page.tsx, update the code to match the following:

app/posts/[id]/page.tsx

This replaces prisma.post.findUnique() with trpc.getPost.useQuery(). Because tRPC is using Tanstack Query to fetch the data, the query result includes the data and other states, such as loading and error. You can learn more about in the Tanstack Query docs.

And before you go any further, test to make sure the new logic is working. Navigate to a post's URL, such as http://localhost:3000/posts/1, and make sure you can see the post.

If that's working, let's go back to your postRouter and add the last procedure you need to handle your create post functionality.

Use tRPC to create a new post

In app/server/routers/posts.ts, add the following code:

app/server/routers/posts.ts

This adds a createPosts procedure that creates a new post.

In app/posts/create/page.tsx, replace the existing code with the following:

app/posts/create/page.tsx

This updates a few things. First, it turns this page into a client component, because Tanstack Query and the tRPC client are client-side. So now, the Server Action that you created before can no longer be used. Instead, the form data is handled using state. When the form is submitted, the createPost() function no longer uses prisma.post.create(), but instead uses trpc.createPosts.useMutation() from the tRPC client. Also, because the page is now a client component, Clerk's auth() helper no longer works, so it's replaced with Clerk's useAuth() hook. This introduces the benefit of having access to Clerk's loading state, so a loading UI is added.

And don't forget, test your changes. Navigate to the create post page, such as http://localhost:3000/posts/create, and make sure you can create a new post.

Once you've confirmed everything's working, you're almost done...

Create protected procedures

In many applications, it's essential to restrict access to certain routes based on user authentication status. This ensures that sensitive data and functionality are only accessible to authorized users.

The benefit of using Clerk with tRPC is that you can create protected procedures using Clerk's authentication context. Clerk's Auth object includes important authentication information like the current user's session ID, user ID, and organization ID. It also contains methods to check for the current user's permissions and to retrieve their session token. You can use the Auth object to access the user's authentication information in your tRPC queries.

Create the tRPC context

In your server directory, create a context.ts file with the following code:

app/server/context.ts

This creates a context that will be used to create the context for every tRPC query sent to the server. The context will use the auth() helper from Clerk to access the user's Auth object.

Pass the context to the tRPC server

Then, in your tRPC server (app/api/trpc/[trpc]/route.ts), pass the context:

app/api/trpc/[trpc]/route.ts

Access the context data in your procedures

The tRPC context, or ctx, should now have access to the Clerk Auth object.

In your server/trpc.ts file, create a protected procedure:

app/server/trpc.ts

Use your protected procedure

Once you have created your procedure, you can use it in any router. In this case, you don't want unauthenticated users to be able to create posts, so let's update the createPosts mutation to be protected by swapping the publicProcedure with the protectedProcedure:

app/server/routers/posts.ts

Finished!

At this point, you've got a fully functional app for creating and displaying posts. You can now add more features to your app, such as updating and deleting posts, adding comments, storing more author information from the Clerk User object, and more.