# Add Rate Limiting to an Express API from a Linear Issue

Yes. An agent picks up the Linear issue, adds per-route rate limits to your Express API, proves them with Jest tests and opens a pull request.

[Run this on Rush](https://cloud.getrush.ai/run/add-rate-limiting/express/), in a Project that has your repository.

## What the agent does

The run starts from the repository URL in `resources`: the agent clones it into its own cloud sandbox and reads the app the way a new maintainer would. In an Express project like `hagopj13/node-express-boilerplate` it opens `package.json` and finds `yarn test` mapped to `jest -i --colors --verbose --detectOpenHandles`, Express 4.17, and `express-rate-limit` already pinned at 5.3.0 in `yarn.lock`, so no new dependency is needed. Tests live under `tests/`, with `tests/integration/auth.test.js` driving the app through supertest.

The boilerplate already has one limiter, and the agent reads it before writing anything. `src/middlewares/rateLimiter.js` exports a single `authLimiter`: a 15 minute window, 20 requests, skipping successful ones, and `src/app.js` mounts it on `/v1/auth` only when `config.env === 'production'`. The issue asks for something stricter and always on: 5 requests per minute per IP on login and register, 100 per minute on every other route, with 429 and a Retry-After header when a limit trips.

The agent extends that middleware file rather than adding a second one. It adds a 60 second limiter with `max: 5` and mounts it on `POST /v1/auth/register` and `POST /v1/auth/login` in `src/routes/v1/auth.route.js`, where the auth routes are declared. A second limiter, 100 requests per 60 seconds, goes in `app.js` ahead of `app.use('/v1', routes)` so it covers users, docs and health checks alike. `express-rate-limit` answers 429 and sets Retry-After itself, so the response shape comes from the library the project already ships.

Proving it needs a database: `tests/utils/setupTestDB.js` connects Mongoose to `MONGODB_URL` with a `-test` suffix, and `docker-compose.test.yml` runs `mongo:4.2.1-bionic` for exactly that. The agent starts MongoDB in the sandbox, adds `tests/integration/rateLimiter.test.js` with a case that sends six register requests from one IP inside a minute and asserts the sixth answers 429 with a Retry-After header, plus a case that a plain GET under `/v1` is still allowed below the general limit. Then it runs `yarn test` and fixes what fails.

## Start it with one API call

`POST https://api.prix.dev/o/{org}/sessions`

```json
{
  "agent": "claude",
  "input": "A Linear issue labeled agent says: \"Add Rate Limiting to an Express API. Limit login and register to 5 requests per minute per IP and every other route to 100, returning 429 with a Retry-After header.\" Do the work in this repository, add tests for it, run yarn test with a MongoDB started in the sandbox, and open a pull request that links back to the issue.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app"
    }
  ]
}
```

1. **Get an API key.** In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
2. **Send the request.** POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
3. **Read what came back.** Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

## What comes back

What comes back is a pull request. Its diff touches `src/middlewares/rateLimiter.js` with the two new limiters, `src/app.js` with the general mount, `src/routes/v1/auth.route.js` with the per-route mounts on register and login, and a new `tests/integration/rateLimiter.test.js`. The description quotes the issue and links back to it.

The checks on the pull request run the same `yarn test` command a developer would run on a laptop. A reviewer reads the diff for three things: the limit values match the issue, 429 responses carry Retry-After, and the tests fail if either limiter is removed from the middleware stack. Nothing else in the app changes: the existing production-only `authLimiter` and the security headers from `helmet()` stay where they are.

The session read keeps the record behind the pull request: every command the agent ran with its output, including the jest run against the sandbox MongoDB and the requests that proved a 429. Anyone who questions the checks can read exactly how the suite was run.

## Run it on every Linear issue labelled agent

The Project's automation turns the label into the run. In the console, add an automation with a Linear trigger on `linear.issue.labeled` filtered to the `agent` label and this prompt; each labelled issue starts its own run in its own sandbox and opens its own pull request. The issue stays the queue and the label stays the only action a person takes.

If the automation is not wanted, a small server can do the same: receive Linear's issue webhook, put the issue text into `input` in place of the quoted text, and POST to `/o/{org}/sessions`. A `run.completed` webhook on the API key then posts the result URL to that server when each run finishes, so nothing polls.

## Why run it on Rush?

On a laptop this job is a chain of setup: a checkout, `yarn install` against the lockfile, and a MongoDB on port 27017 before jest will even connect. Each run on Rush boots its own sandbox with that already in reach, starts MongoDB itself, and keeps every command and its output, so the proof of the 429 tests is the run's own record rather than a paste into a pull request comment.

The label scales to however many issues land at once. Issues labelled in one evening become one run each, in one sandbox each, each on its own branch with its own pull request, instead of a queue of checkouts competing for a single laptop's MongoDB and a single person's attention.

## Frequently asked questions

### The boilerplate already ships a rate limiter. What changes?

The existing `authLimiter` in `src/middlewares/rateLimiter.js` stays; it limits all of `/v1/auth` to 20 requests in 15 minutes, only in production. The new limiters enforce the issue's numbers, 5 per minute on register and login and 100 per minute elsewhere, in every environment, and they answer 429 with Retry-After.

### Does it work with yarn?

Yes. The agent installs with the lockfile the repository already has and runs the `test` script from `package.json`, so the checks on the pull request run exactly what a developer running `yarn test` would run.

### The tests need MongoDB. Where does it come from?

`tests/utils/setupTestDB.js` connects Mongoose to `MONGODB_URL` with a `-test` suffix, and `docker-compose.test.yml` documents the `mongo:4.2.1-bionic` image the suite expects. The sandbox starts its own MongoDB, runs the suite against it, and the session keeps the output.

### What does a limited request get back?

`express-rate-limit` answers 429 and sets a Retry-After header with the number of seconds to wait. The integration tests assert both, so the header cannot silently disappear in a later change.

### Can I change the limits without editing the code myself?

Yes. Edit the issue text: the limits and the routes they apply to are the spec the agent works from, so a new run picks up the new numbers and updates the tests to match.
