Skip to content

feat: add API rate limiting to protect against request abuse#451

Open
Arijit429 wants to merge 9 commits intofireform-core:mainfrom
Arijit429:feat/api-rate-limiting
Open

feat: add API rate limiting to protect against request abuse#451
Arijit429 wants to merge 9 commits intofireform-core:mainfrom
Arijit429:feat/api-rate-limiting

Conversation

@Arijit429
Copy link
Copy Markdown

@Arijit429 Arijit429 commented Apr 17, 2026

Closes #442
Closes #272

Summary

Adds rate limiting middleware using slowapi to protect API endpoints
from abuse and denial-of-service via repeated requests.

Problem

The API has no rate limiting. A single client can send unlimited
requests per second to /forms/fill, which:

  • Overloads the Ollama instance with parallel LLM extraction requests
  • Exhausts server memory and CPU
  • Effectively blocks the service for other users

Changes

api/middleware/rate_limiter.py (NEW)

  • Created Limiter instance using client IP as key
  • Added register_rate_limiter() to attach middleware and error handler
  • Rate limit exceeded returns uniform 429 JSON response matching ErrorResponse schema

api/main.py

  • Registered rate limiter at app startup

api/routes/forms.py

  • Applied @limiter.limit("20/minute") to /forms/fill endpoint
  • Added Request parameter for IP-based limiting

Testing

  • Normal request → returns expected response
  • 25 rapid requests → rate limit triggered after 20th with 429 status
  • Health check → still returns healthy
  • Existing tests → all pass

Changes Summary

File Change Why
api/middleware/rate_limiter.py New rate limiter module Centralized rate limiting logic
api/main.py Register rate limiter Connect middleware to app
api/routes/forms.py 20/min limit on /forms/fill Protect LLM endpoint from abuse

- Add HTTPException handler for consistent error shape across all routes
- Add RequestValidationError handler with human-readable error messages
- Add catch-all Exception handler to prevent stack trace leakage
- Fix duplicate get_template() call in forms.py (was querying DB twice)
- Wrap Controller errors in AppError for safe client-facing messages
- All errors now return uniform {success, error: {code, message}} envelope
…file

- Add GET /health liveness probe for Docker and container orchestration
- Migrate database init from module-level to FastAPI lifespan context manager
- Fix Dockerfile: start uvicorn server instead of tail -f /dev/null
- Fix Dockerfile: correct PYTHONPATH from /app/src to /app
- Add Docker HEALTHCHECK directive using /health endpoint
- Add EXPOSE 8000 for container port documentation
- Add FastAPI metadata (title, description, version) for API docs
- Enforce 20 MB max upload size (returns 413 if exceeded)
- Validate PDF magic bytes to reject non-PDF files renamed to .pdf
- Reject empty file uploads with clear 400 error
- Add matching client-side size and empty file checks for instant UX feedback
- Server-side validation is the security authority, client checks are UX only
- Add 120s timeout to prevent indefinite request hangs
- Add retry logic (3 attempts) with exponential backoff (2s, 4s, 8s)
- Retry on timeouts, connection errors, and 5xx server errors
- Do not retry on 4xx client errors (permanent failures)
- Extract _call_ollama() method for testability
- Replace print() statements with structured logging
- Add per-field logging for extraction debugging
…itization

- Add min_length=1 and max_length=50000 to input_text field
- Add whitespace-only rejection via field_validator
- Auto-strip leading/trailing whitespace from input before LLM
- Add template name validation (min 1, max 200 chars)
- Add pdf_path minimum length validation
- Fix deprecated class Config to model_config in both schema files
- Prevents empty prompts and oversized payloads reaching LLM pipeline
- Run pytest on every push to main and every pull request
- Run ruff linter for Python code quality checks
- Cache pip dependencies for faster CI runs
- Set PYTHONPATH for correct module resolution
- Two parallel jobs: test and lint
…utc)

Closes fireform-core#444

- Replace datetime.utcnow() in models.py default_factory
- Replace datetime.utcnow() in templates.py timestamp generation
- Fixes DeprecationWarning visible in pytest output on Python 3.12+
Closes fireform-core#442

- Add slowapi-based rate limiting middleware
- Limit /forms/fill to 20 requests/minute per client IP
- Return uniform 429 response matching ErrorResponse schema
- Register rate limiter in app startup
- Prevents DoS via repeated LLM extraction requests
@Arijit429
Copy link
Copy Markdown
Author

Context

Closes #442. Added slowapi rate limiting — /forms/fill is limited to 20 requests/minute per client IP. Returns a uniform 429 JSON response matching the existing ErrorResponse schema from PR #434.

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.

[SECURITY] No rate limiting on API endpoints [BUG]: No Rate Limiting - API Accepts 500+ Requests/Second (DoS Risk)

1 participant