Learn how to use Unkey to ratelimit tRPC routes in your Next.js application.
Written by
James Perkins
Published on
Ratelimiting is not just a feature; it's a lifeline for production applications. Without it, you could face a skyrocketing bill. Your server could be pushed to its limits, leaving real users stranded and your application's reputation at stake.
Unkey provides ratelimiting that is distributed globally and can be easily added to any server, ensuring your protection. We will discuss the features of our service, including synchronous and asynchronous protection, identifier overrides, and how our analytical data can help you identify spikes in usage and how it can be used in a tRPC application.
To get up and running with the app and follow along, you need:
UNKEY_ROOT_KEY
, which you can get by signing up for a free accountIn this post, we will use create-t3-app for the demo, so feel free to use that if you want an easy way to use tRPC + Auth.js in a Next.js application.
@unkey/ratelimit
packageBefore we start coding, we need to install the @unkey/ratelimit
package. This package gives you access to Unkey's API with type safety.
1npm install @unkey/ratelimit
We need to use the UNKEY_ROOT_KEY
to run our ratelimiting package, so we must first update the env.js
file in the src
directory. Add UNKEY_ROOT_KEY: z.string()
to the server
object and UNKEY_ROOT_KEY: process.env.UNKEY_ROOT_KEY
to the runtimeEnv
object.
Now that it is updated add your Unkey root key to your .env as UNKEY_ROOT_KEY
which can be found in the Unkey dashboard under settings Root Keys.
Now that the package is installed and our .env
has been updated, we can configure our ratelimiter. Inside the server/api/routers/post
file, we have a create
procedure. This procedure allows users to create posts; currently, users can create as many as they like and as quickly as they like.
In this example, we will configure our ratelimiter in the procedure itself. Of course, you can abstract this into a utility file if you prefer. First, we must import Ratelimit
from the @unkey/ratelimit
package and TRPCError
and env
.
1import { z } from "zod";
2
3import {
4 createTRPCRouter,
5 protectedProcedure,
6 publicProcedure,
7} from "~/server/api/trpc";
8import { posts } from "~/server/db/schema";
9import { env } from "~/env";
10import { TRPCError } from "@trpc/server";
11import { Ratelimit } from "@unkey/ratelimit";
To configure the Ratelimiter, we need to pass four things along, the root key, the namespace, the limit, and the duration of our ratelimiting. Inside the mutation, add the following:
1const unkey = new Ratelimit({
2 rootKey: env.UNKEY_ROOT_KEY,
3 namespace: "posts.create",
4 limit: 3,
5 duration: "5s",
6});
The namespace can be anything, but we are using the tRPC route and procedure to make it easier to track in Unkey's analytics. We now have the ability to rate-limit this procedure, allowing only three requests per five seconds.
To use the ratelimit, we need an identifier. This can be anything you like, such as a user ID or an IP address. We will be using our user's ID as they are required to be logged in to create a new post. Then, we can call unkey.limit
with the identifier, and unkey will return a boolean of true or false, which we can use to make a decision.
1const { success } = await unkey.limit(ctx.session.user.id);
So now we have the boolean we can check if it's false and then throw a TRPCError telling the user they have been ratelimited and stop any more logic running.
1const { success } = await unkey.limit(ctx.session.user.id);
2
3if (!success) {
4 throw new TRPCError({ code: "TOO_MANY_REQUESTS" });
5}
At this point, our code is ready to test. Give it a whirl, and try posting multiple times. You will see that the posts won't update anymore after you are rate-limited.
Unkey allows you to tell us how expensive a request should be. For example, maybe you have an AI route that costs you a lot more than any other route, so you want to reduce the number of requests that can be used.
1const { success } = await unkey.limit(ctx.session.user.id, {
2 cost: 3,
3});
This request costs three instead of one, giving you extra flexibility around expensive routes.
Although Unkey response times are fast, there are some cases where you are willing to give up some accuracy in favor of quicker response times. You can use our async
option, which has 98% accuracy, but we don't need to confirm the limit with the origin before returning a decision. You can set this either on the limit
request or on the configuration itself.
1const unkey = new Ratelimit({
2 rootKey: env.UNKEY_ROOT_KEY,
3 namespace: "posts.create",
4 limit: 3,
5 duration: "5s",
6 async: true,
7});
8
9// or
10
11const { success } = await unkey.limit(ctx.session.user.id, {
12 async: true,
13});
While this is a small overview of using Unkey's ratelimiting with tRPC, we also offer other features that aren't covered here, including:
You can read more about those features in our documentation on Ratelimiting.
2500 verifications and 100K successful rate‑limited requests per month. No CC required.