Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .github/workflows/benchmark-api-smoke.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: API benchmark smoke

on:
pull_request:
paths:
- "apps/api/**"
- "benchmarks/**"
- "package.json"
- "package-lock.json"
- ".github/workflows/benchmark-api-smoke.yml"
push:
branches:
- main
paths:
- "apps/api/**"
- "benchmarks/**"
- "package.json"
- "package-lock.json"

jobs:
benchmark-smoke:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: npm

- name: Install dependencies
run: npm ci

- name: Run API test suite
run: npm run test

- name: Run smoke benchmark gate
run: npm run benchmark:smoke
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ node_modules
dist
.env
.env.*
!benchmarks/.env.benchmark.example
coverage
*.log
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ Backend architecture follows:
```bash
npm install
npm run test
npm run benchmark
```

## AI Agent Contribution Instruction
Expand Down Expand Up @@ -86,3 +87,14 @@ Prisma schema is available in `packages/db/prisma/schema.prisma` with models for
## Environment Variables

Each app/package expects its own `.env` values for DB, auth, and integrations.

## Benchmarking

The repository includes a committed benchmark suite under [`benchmarks`](./benchmarks)
that exercises every `/api/` endpoint with schema-shaped payloads, records p50/p95/p99
latency, peak and sustained request throughput, error rate, and TTFB, and writes JSON
plus markdown reports to [`benchmarks/results`](./benchmarks/results).

- Full suite: `npm run benchmark`
- CI smoke gate: `npm run benchmark:smoke`
- Configuration template: `benchmarks/.env.benchmark.example`
2 changes: 1 addition & 1 deletion apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"scripts": {
"dev": "node src/server.js",
"start": "node src/server.js",
"test": "node --test src/tests"
"test": "node --test \"src/tests/*.test.js\""
},
"dependencies": {
"cors": "^2.8.5",
Expand Down
1 change: 1 addition & 0 deletions apps/api/src/config/env.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export const env = {
benchmarkMode: process.env.BENCHMARK_MODE === "1",
nodeEnv: process.env.NODE_ENV ?? "development",
port: Number(process.env.PORT ?? 4000),
jwtSecret: process.env.JWT_SECRET ?? "development-secret",
Expand Down
15 changes: 9 additions & 6 deletions apps/api/src/middleware/rateLimit.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import rateLimit from "express-rate-limit";
import { env } from "../config/env.js";

export const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
limit: 200,
standardHeaders: "draft-7",
legacyHeaders: false
});
export const apiLimiter = env.benchmarkMode
? (req, res, next) => next()
: rateLimit({
windowMs: 15 * 60 * 1000,
limit: 200,
standardHeaders: "draft-7",
legacyHeaders: false
});
10 changes: 10 additions & 0 deletions benchmarks/.env.benchmark.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
BENCHMARK_TARGET=local
BENCHMARK_BASE_URL=
BENCHMARK_PORT=4010
BENCHMARK_PROFILE=full
BENCHMARK_CONCURRENCY=
BENCHMARK_AMOUNT=
BENCHMARK_TTFB_SAMPLES=5
BENCHMARK_ADMIN_TOKEN=
BENCHMARK_MODE=1
JWT_SECRET=benchmark-secret
21 changes: 21 additions & 0 deletions benchmarks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# API Benchmarks

The benchmark suite covers every `/api/` endpoint in the repository and writes
JSON plus markdown summaries to [`benchmarks/results`](./results).

## Setup

1. Copy [`benchmarks/.env.benchmark.example`](./.env.benchmark.example) to `benchmarks/.env.benchmark`.
2. Adjust `BENCHMARK_TARGET` and `BENCHMARK_BASE_URL` if you want to hit a staging host instead of the local API.
3. Run `npm run benchmark` for the full suite or `npm run benchmark:smoke` for the low-concurrency CI profile.

When `BENCHMARK_TARGET=local`, the runner starts the Express app in benchmark mode,
uses a dedicated JWT benchmark token for `/api/admin/metrics`, and bypasses API
rate limiting so latency numbers are not polluted by 429 responses.

If you benchmark a remote or staging deployment, set `BENCHMARK_ADMIN_TOKEN` in
`benchmarks/.env.benchmark` so the protected admin endpoint is exercised with a
real dedicated benchmark token.

For bounty submissions, paste the generated `benchmarks/results/latest-full.md`
summary into the PR description.
4 changes: 4 additions & 0 deletions benchmarks/fixtures/upload-sample.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
FreelanceFlow benchmark upload fixture

This text fixture is used to exercise the multipart upload endpoint during
local and CI smoke benchmark runs.
1 change: 1 addition & 0 deletions benchmarks/results/.gitkeep
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Loading
Loading