Skip to content
Go back

Implementing Rate Limiting in Fastify with Redis

Implementing Rate Limiting in Fastify with Redis

Introduction

Excessive requests can degrade API performance or lead to denial-of-service attacks. This guide shows how to integrate Redis-backed rate limiting in Fastify to ensure fair usage and protect your services.

Prerequisites

Step 1: Initialize Project and Install Dependencies

mkdir fastify-rate-limit
cd fastify-rate-limit
npm init -y
npm install fastify fastify-rate-limit ioredis

Step 2: Configure Redis Connection

Create src/redis.ts:

import Redis from "ioredis";

const redis = new Redis(process.env.REDIS_URL || "redis://127.0.0.1:6379");

export default redis;

Step 3: Set Up Fastify Server with Rate Limiting

Create src/server.ts:

import Fastify from "fastify";
import rateLimit from "fastify-rate-limit";
import redis from "./redis";

async function buildServer() {
  const app = Fastify({ logger: true });

  app.register(rateLimit, {
    global: false, // disable global by default
    redis: redis,
    max: 100, // max requests
    timeWindow: "1 minute",
  });

  app.get(
    "/public",
    {
      config: { rateLimit: { max: 30, timeWindow: "1 minute" } },
    },
    async (request, reply) => {
      return { hello: "world" };
    }
  );

  app.get(
    "/auth",
    {
      config: { rateLimit: { max: 10, timeWindow: "1 minute" } },
    },
    async (request, reply) => {
      return { secure: true };
    }
  );

  return app;
}

if (require.main === module) {
  buildServer().then(app => app.listen({ port: 3000 }));
}

export default buildServer;

Step 4: Customize Rate Limits per Route

To disable rate limiting on a route, set config.rateLimit = false.

Step 5: Test Rate Limiting

Use curl to test limits:

# Exceed public route limit
for i in {1..35}; do curl -s -o /dev/null -w "%{http_code}\n" http://localhost:3000/public; done

HTTP 200 for first 30, then HTTP 429 Too Many Requests.

Step 6: Handling 429 Errors Gracefully

Fastify automatically sets Retry-After header. You can customize error response:

import fp from "fastify-plugin";

export default fp(async app => {
  app.setErrorHandler((error, request, reply) => {
    if (error.statusCode === 429) {
      reply
        .status(429)
        .send({ error: "Rate limit exceeded, please try again later." });
    } else {
      reply.send(error);
    }
  });
});

Register plugin before routes.

Summary

Integrating fastify-rate-limit with Redis safeguards your API from abuse while allowing dynamic, per-route limits. Customize responses to improve client experience.


Share this post on:

Previous Post
Handling File Uploads in Fastify Using Multipart
Next Post
Comparing Fastify vs Express Performance in Production