Next.js Discord

Discord Forum

Help middleware with express-rate-limit

Answered
Alex.Mazaltov Developer๛ posted this in #help-forum
Open in Discord
i want to apply rateLimiter middleware in file: middleware/rateLimiter.ts to my main middleware.ts file inside root folder for NEXT 14 app taken from vercel template. [See my latest commits](https://github.com/alexmazaltov/ai-chatbot/commits/main/)

I'm stuck with on the following state:
middleware/rateLimiter.ts

import rateLimit from 'express-rate-limit';
import { kv } from '@vercel/kv';
import { getSession } from 'next-auth/react';

const createRateLimiter = (limit: number, windowMs: number) => {
  return rateLimit({
    windowMs,
    max: limit,
    standardHeaders: true,
    legacyHeaders: false,
    keyGenerator: async (req, res) => {
      const session = await getSession({ req: req as any});
      const ip = req.ip;
      return session?.user?.id ? `user:${session.user.id}` : `ip:${ip}`;
    },
    store: {
      async increment(key) {
        const count = await kv.incr(key);
        await kv.expire(key, windowMs / 1000);
        return { totalHits: count, resetTime: Date.now() + windowMs };
      },
      async decrement(key) {
        return kv.decr(key);
      },
      async resetKey(key) {
        return kv.del(key);
      }
    }
  });
};

export const rateLimiter = async (req, res, next) => {
  try {
    const session = await getSession({ req: req as any });
    const limit = session?.user ? 30 : 10;
    const windowMs = 24 * 60 * 60 * 1000; // 24 hours

    const limiter = createRateLimiter(limit, windowMs);
    return limiter(req, res, next);
  } catch (error) {
    next(error);
  }
};

I understand that there may be better way to limit amount of requests to openai that will be possible to perform for visitors.
To prevent token spending
Answered by Texas leafcutting ant
but this solution is not designed to be used as middleware.

You can adjust lib/chat/actions.tsx

diff --git a/lib/chat/actions.tsx b/lib/chat/actions.tsx
index ca6de3d..e4c41cb 100644
--- a/lib/chat/actions.tsx
+++ b/lib/chat/actions.tsx
@@ -35,6 +35,17 @@ import { saveChat } from '@/app/actions'
 import { SpinnerMessage, UserMessage } from '@/components/stocks/message'
 import { Chat, Message } from '@/lib/types'
 import { auth } from '@/auth'
+import { RateLimiterMemory } from 'rate-limiter-flexible'
+
+const rateLimiterUnauthenticated = new RateLimiterMemory({
+  points: 3, // 3 requests
+  duration: 3600, // per hour
+});
+
+const rateLimiterAuthenticated = new RateLimiterMemory({
+  points: 6, // 6 requests
+  duration: 3600, // per hour
+});
 
 async function confirmPurchase(symbol: string, price: number, amount: number) {
   'use server'
@@ -109,6 +120,20 @@ async function confirmPurchase(symbol: string, price: number, amount: number) {
 async function submitUserMessage(content: string) {
   'use server'
 
+  const session = await auth()
+  const isAuthenticated = session && session.user
+
+  try {
+    if (isAuthenticated && session && session.user) {
+      await rateLimiterAuthenticated.consume(session.user.id)
+    } else {
+      await rateLimiterUnauthenticated.consume('unauthenticated')
+    }
+  } catch (rateLimiterRes) {
+    // Handle rate limit exceeded
+    throw new Error('Rate limit exceeded. Please try again later.')
+  }
+
   const aiState = getMutableAIState<typeof AI>()
 
   aiState.update({
View full answer

4 Replies

 middleware.ts

import { rateLimiter } from './middleware/rateLimiter';
import NextAuth from 'next-auth';
import { authConfig } from './auth.config';
import { NextApiHandler } from 'next';

export default async function middleware(req, res, next: NextApiHandler) {
  await rateLimiter(req, res, async () => {
    await NextAuth(authConfig).auth(req, res, next);
  });
}

export const config = {
  matcher: ['/((?!api|_next/static|_next/image|.*\\.png$).*)']
};
Any suggestions how to properly implement such middleware?
Texas leafcutting ant
instead of using express-rate-limit use the rate-limiter-flexible
Texas leafcutting ant
but this solution is not designed to be used as middleware.

You can adjust lib/chat/actions.tsx

diff --git a/lib/chat/actions.tsx b/lib/chat/actions.tsx
index ca6de3d..e4c41cb 100644
--- a/lib/chat/actions.tsx
+++ b/lib/chat/actions.tsx
@@ -35,6 +35,17 @@ import { saveChat } from '@/app/actions'
 import { SpinnerMessage, UserMessage } from '@/components/stocks/message'
 import { Chat, Message } from '@/lib/types'
 import { auth } from '@/auth'
+import { RateLimiterMemory } from 'rate-limiter-flexible'
+
+const rateLimiterUnauthenticated = new RateLimiterMemory({
+  points: 3, // 3 requests
+  duration: 3600, // per hour
+});
+
+const rateLimiterAuthenticated = new RateLimiterMemory({
+  points: 6, // 6 requests
+  duration: 3600, // per hour
+});
 
 async function confirmPurchase(symbol: string, price: number, amount: number) {
   'use server'
@@ -109,6 +120,20 @@ async function confirmPurchase(symbol: string, price: number, amount: number) {
 async function submitUserMessage(content: string) {
   'use server'
 
+  const session = await auth()
+  const isAuthenticated = session && session.user
+
+  try {
+    if (isAuthenticated && session && session.user) {
+      await rateLimiterAuthenticated.consume(session.user.id)
+    } else {
+      await rateLimiterUnauthenticated.consume('unauthenticated')
+    }
+  } catch (rateLimiterRes) {
+    // Handle rate limit exceeded
+    throw new Error('Rate limit exceeded. Please try again later.')
+  }
+
   const aiState = getMutableAIState<typeof AI>()
 
   aiState.update({
Answer