Skip to content

feat(chat): rate limit and validate /api/chat to protect the OpenAI budget#69

Open
Muawiya-contact wants to merge 1 commit into
codeforpakistan:mainfrom
Muawiya-contact:fix/chat-api-hardening
Open

feat(chat): rate limit and validate /api/chat to protect the OpenAI budget#69
Muawiya-contact wants to merge 1 commit into
codeforpakistan:mainfrom
Muawiya-contact:fix/chat-api-hardening

Conversation

@Muawiya-contact

Copy link
Copy Markdown

Fixes #68

What & why

POST /api/chat was public with no rate limiting and no input validation. Every request fans out to three billed OpenAI calls (the query-type router, an embedding, and the streaming completion), so a simple loop against the endpoint could run up unbounded cost — a real exposure for a nonprofit project. Malformed bodies also threw an unhandled 500 at messages[messages.length - 1].

This PR hardens the endpoint with the tools already in the stack.

Changes

  • Rate limiting (lib/ratelimit.ts): IP-based sliding window (20 req/min) via @upstash/ratelimit + Upstash Redis. Returns 429 when exceeded.
  • Input validation (app/api/chat/route.tsx): validates the body with Zod inside a try/catch and returns a clean 400 on malformed input instead of a 500. Message shape is preserved (.passthrough()), and the role enum matches the AI SDK's Message type, so streaming behavior is unchanged for valid requests.
  • Adds @upstash/ratelimit and @upstash/redis to dependencies.

Safe to merge as-is

The limiter is disabled unless both env vars are set, so this changes nothing in local dev, preview, or production until you opt in. Nothing downstream of the guards was touched.

Enabling in production

Create a Redis database in Upstash (separate from the existing QStash instance) and set:

UPSTASH_REDIS_REST_URL=
UPSTASH_REDIS_REST_TOKEN=

Tune the window in lib/ratelimit.ts (Ratelimit.slidingWindow(20, "1 m")) as needed.

Testing

  • POST /api/chat with {}400 (previously an unhandled 500).
  • With the Redis env vars set, exceeding 20 requests/min from one IP → 429.
  • Without the env vars, the endpoint behaves exactly as before.

Notes

Scope is endpoint hardening only. The retrieval-context fix is a separate PR/issue and doesn't overlap functionally.

…udget

Adds IP-based rate limiting (Upstash Redis, no-op until env vars are set) and Zod validation that returns a clean 400 on malformed bodies instead of an unhandled 500. Fixes codeforpakistan#68
@vercel

vercel Bot commented Jul 6, 2026

Copy link
Copy Markdown

@Muawiya-contact is attempting to deploy a commit to the Team BoostPanda Team on Vercel.

A member of the Team first needs to authorize it.

@Muawiya-contact

Copy link
Copy Markdown
Author

@aliirz Heads-up: the Vercel preview needs a maintainer to authorize the deploy on your side I don't have access to the team's Vercel project. Happy to adjust anything if the build surfaces an issue.

@aliirz

aliirz commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Let me test this locally @Muawiya-contact

@Muawiya-contact

Copy link
Copy Markdown
Author

@aliirz, any updates?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

/api/chat has no rate limiting, auth, or input validation — public endpoint can drain the OpenAI budget

2 participants