Skip to content

Using rate-limit-postgresql in Next.js #36

@nabby27

Description

@nabby27

I have been using this library in Next.js and I would like to share how to do it as I wish this information had been somewhere to save me some time.

First of all, I created a file with a function that will act as middleware in the routes I put it in. I could have put it directly in the middleware file that Next already offers, but I wanted to have more flexibility. It is important to add the keyGenerator parameter since by default the library will look at req.ip and in Next.js that field does not exist.

export function apiRateLimiter(
     handler: (
        req: NextRequest,
        params: { params: Promise<Record<string, string>> }
    ) => Promise<NextResponse>
) {
    return async (req: NextRequest, params: { params: Promise<Record<string, string>> }): Promise<NextResponse> => {
        const limiter = rateLimit({
            windowMs: 60 * 1000,
            limit: 20,
            standardHeaders: 'draft-7',
            legacyHeaders: false,
            store: new PostgresStore(
                {
                    user: SERVER_CONFIG.ENV.DB_USER,
                    password: SERVER_CONFIG.ENV.DB_PASSWORD,
                    host: SERVER_CONFIG.ENV.DB_HOST,
                    database: SERVER_CONFIG.ENV.DB_NAME,
                    port: SERVER_CONFIG.ENV.DB_PORT,
                },
                'rate_limit_requests',
            ),
            keyGenerator: (req) => {
                return req.headers["x-forwarded-for"] ?? '0.0.0.0'
            },
            handler: (req) => {
                if (req.rateLimit.remaining === 0) {
                    throw new Error;
                }
            }
        })
    
        try {
            const mockRes = {
                setHeader: () => {},
                status: () => ({ send: () => {} })
            };

            await new Promise<void>((resolve, reject) => {
                limiter(req, mockRes, (error: Error | undefined) => {
                    if (error) reject(error);
                    resolve();
                });
            });
            
            return await handler(req, params);
        } catch (error) {
            return NextResponse.json({ error: "Too many requests" }, { status: 429 });
        }
    }
}

Using it on a route it would look something like this:

export const POST = apiRateLimiter(async (request, route) => {
    ...
});

On the other hand, you have to configure webpack in next.config.ts to add the migration files in the build, since if it is not done explicitly, they are not detected and therefore the schema with the tables is not generated. You need to add migrations for both @acpr/rate-limit-postgresql and postgres-migrations

import type { NextConfig } from "next";
import CopyPlugin from 'copy-webpack-plugin';

const nextConfig: NextConfig = {
  webpack: (config, { isServer }) => {
    if (isServer) {
      config.plugins.push(
        new CopyPlugin({
          patterns: [
            {
              from: 'node_modules/postgres-migrations/dist/migrations',
              to: 'vendor-chunks/migrations'
            },
            {
              from: 'node_modules/@acpr/rate-limit-postgresql/dist/migrations',
              to: 'vendor-chunks/migrations'
            },
          ],
        })
      );
    }
    return config;
  },
};

export default nextConfig;

And with this the library would already be working in Next.js, I hope it is useful to someone or it would even be nice if this info was in the documentation. I understand that this library is explicitly made for express but since Next.js uses express underneath and it is so popular today I wouldn't find it strange to see some support.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions