TypeScript-first API testing for teams that ship.
Write API tests in TypeScript. Run them from the CLI. Validate against your OpenAPI spec. Ship with confidence.
Traditional GUI-based tools like Postman or Insomnia are built for manual exploration. reqprobe is a code-first API testing framework built for automation. Tests live in your repo as .ts files — versioned, diff-able, and reviewable like any other code.
| GUI-based Tools | reqprobe | |
|---|---|---|
| Lives in Git | ❌ JSON exports, not code | ✅ .ts files, full diff history |
| TypeScript | ❌ Proprietary scripting | ✅ Native, typed, IDE-complete |
| CI/CD | ✅ npx reqprobe run — done |
|
| OpenAPI validation | ❌ Manual schema checks | ✅ Automatic per-request |
| Code review | ❌ Proprietary state, no diffs | ✅ PRs, blame, history |
| Cost | 💸 Subscriptions required | ✅ Free, open-source |
If your tests live in a GUI, they don't belong to your team — they belong to a vendor. reqprobe puts your tests back in your codebase.
| Feature | Description | |
|---|---|---|
| 📝 | TypeScript Native | Tests are written in fully typed .ts files with full IDE support. |
| 🛡️ | OpenAPI Contract Validation | Automatically validate response payloads against your OpenAPI 3.x spec using Ajv — no extra assertion code needed. |
| 📊 | Rich Reporting | Self-contained HTML + JSON reports. No external dependencies. |
| 🌱 | Git-Native | Full diff history, PR reviews, blame — your tests are real code. |
| ⚙️ | CI/CD Ready | Exits with code 1 on failure. Works with GitHub Actions, GitLab CI, and any CI runner out of the box. |
| 🏗️ | Monorepo Support | Per-package config. Each service owns its own tests. |
| 🔜 | Watch Mode | (Coming soon) reqprobe run --watch — re-run tests on file save. |
| 🔜 | Scaffold Generator | (Coming soon) Generate typed test stubs from your OpenAPI spec. |
| 🔜 | beforeAll / afterAll hooks | (Coming in v0.2) Shared setup and teardown across tests. |
reqprobe is not yet published to npm. Install it directly from GitHub — see the full guide in INSTALL_FROM_GITHUB.md.
npm install github:shashi089/reqprobe// reqprobe.config.ts
import type { Config } from 'reqprobe';
const config: Config = {
baseUrl: 'https://your-api.com',
timeout: 10_000,
headers: {
Authorization: `Bearer ${process.env.API_TOKEN}`,
},
};
export default config;// tests/users.test.ts
import { test } from 'reqprobe/dsl';
test('GET /users — returns 200', async (ctx) => {
const res = await ctx.request({ url: '/users', method: 'GET' });
ctx.expect(res).toHaveStatus(200);
ctx.expect(res.body).toHaveProperty('data');
});npx reqprobe run "tests/**/*.test.ts"Output:
❯ users.test.ts
✓ GET /users — returns 200 312ms
────────────────────────────────────────
PASSED 312ms
────────────────────────────────────────
✓ Passed 1
✖ Failed 0
Total 1
────────────────────────────────────────
Exit code 1 on failure — CI-ready out of the box.
Want to see reqprobe working immediately? Run this against the free PokéAPI — no auth required.
// tests/pokeapi.test.ts
import { test } from 'reqprobe/dsl';
test('GET /pokemon/pikachu — returns correct name', async (ctx) => {
const res = await ctx.request({ url: '/pokemon/pikachu', method: 'GET' });
ctx.expect(res).toHaveStatus(200);
ctx.expect(res.body.name).toBe('pikachu');
});
test('GET /pokemon/1 — returns bulbasaur', async (ctx) => {
const res = await ctx.request({ url: '/pokemon/1', method: 'GET' });
ctx.expect(res).toHaveStatus(200);
ctx.expect(res.body.name).toBeTruthy();
});// reqprobe.config.ts
const config = {
baseUrl: 'https://pokeapi.co/api/v2',
timeout: 10_000,
};
export default config;npx reqprobe run "tests/pokeapi.test.ts"// reqprobe.config.ts
import type { Config } from 'reqprobe';
const config: Config = {
baseUrl: 'https://api.yourservice.com',
timeout: 10_000,
headers: {
Authorization: `Bearer ${process.env.API_TOKEN}`,
},
openapi: {
specPath: './openapi.json',
strict: false, // true = fail on missing schemas
},
reporters: {
outDir: './reqprobe-reports',
json: true,
html: true,
},
};
export default config;// users.test.ts
import { test } from 'reqprobe/dsl';
test('GET /users — returns a list', async (ctx) => {
const res = await ctx.request({ url: '/users', method: 'GET' });
ctx.expect(res).toHaveStatus(200);
ctx.expect(res.body).toHaveProperty('data');
});
test('POST /users — creates a user', async (ctx) => {
const res = await ctx.request({
url: '/users',
method: 'POST',
body: { name: 'Alice', email: 'alice@example.com' },
});
ctx.expect(res).toHaveStatus(201);
ctx.expect(res.body.name).toBe('Alice');
});// auth.test.ts
import type { TestSuite } from 'reqprobe';
const suite: TestSuite = {
name: 'Auth API',
tests: [
{
name: 'POST /auth/login — returns token',
run: async (ctx) => {
const res = await ctx.request({
url: '/auth/login',
method: 'POST',
body: { email: 'admin@example.com', password: 'secret' },
});
ctx.expect(res).toHaveStatus(200);
ctx.expect(res.body.token).toBeTruthy();
},
},
],
};
export default suite;ctx.expect(res).toHaveStatus(200);
ctx.expect(res.body.name).toBe('Alice');
ctx.expect(res.body.items).toEqual([1, 2, 3]);
ctx.expect(res.body.message).toContain('success');
ctx.expect(res.body.token).toBeTruthy();
ctx.expect(res.body).toHaveProperty('id');This feature is entirely optional. reqprobe works perfectly without a spec — OpenAPI validation is an additive layer you enable when you're ready.
Point reqprobe at your OpenAPI 3.x spec and every response is automatically validated against its schema using Ajv — no extra assertions needed in your tests.
// reqprobe.config.ts
const config: Config = {
baseUrl: 'https://api.yourservice.com',
openapi: {
specPath: './openapi.json',
strict: false,
},
};// products.test.ts
test('GET /products/:id — response matches schema', async (ctx) => {
const res = await ctx.request({ url: '/products/42', method: 'GET' });
// reqprobe automatically validates res.body against
// the GET /products/{id} → 200 schema in your spec.
// No extra assertion needed.
ctx.expect(res).toHaveStatus(200);
});If the response body doesn't match the schema, reqprobe throws a detailed error:
✖ GET /products/42 — response matches schema (67ms)
├ [reqprobe/openapi] Response body failed schema validation:
├ • body.price: must be number
└ • body.stock: must have required property 'stock'
Supported:
- OpenAPI 3.x JSON specs
- Local
$refresolution - Path template matching (
/users/{id}) defaultresponse fallbackstrict: falsesilently skips missing schemas (good for partial specs)
This feature is entirely optional. If no
reportersconfig is set, reqprobe simply prints results to the terminal and exits — no files are written.
reporters: {
outDir: './reqprobe-reports',
json: true, // reqprobe-reports/report.json
html: true, // reqprobe-reports/report.html
}The HTML report is a fully self-contained, dependency-free file — open it in any browser, attach it to a PR, or upload it as a CI artifact. It includes:
- Pass/fail summary with total duration
- Per-suite test tables with badges
- Expandable error rows showing expected vs received + response body
The JSON report is machine-readable — suitable for dashboards, downstream tooling, or Slack bots.
# .github/workflows/api-tests.yml
name: API Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- name: Run API tests
run: npx reqprobe run "tests/**/*.test.ts"
env:
API_TOKEN: ${{ secrets.API_TOKEN }}
BASE_URL: ${{ vars.STAGING_URL }}
- name: Upload HTML report
if: always()
uses: actions/upload-artifact@v4
with:
name: reqprobe-report
path: reqprobe-reports/api-tests:
image: node:20-alpine
script:
- npm ci
- npx reqprobe run "tests/**/*.test.ts"
artifacts:
when: always
paths:
- reqprobe-reports/
expire_in: 7 days
variables:
API_TOKEN: $API_TOKENreqprobe exits with code 1 when any test fails — no extra configuration needed.
reqprobe reads config from the nearest reqprobe.config.ts relative to where you run it. Each package can have its own config:
apps/
users-service/
reqprobe.config.ts # baseUrl: http://users-service
tests/
users.test.ts
orders-service/
reqprobe.config.ts # baseUrl: http://orders-service
tests/
orders.test.ts
# Run tests for a specific service from monorepo root
npx reqprobe run "apps/users-service/tests/**/*.test.ts"
# Or from within the package
cd apps/users-service
npx reqprobe run "tests/**/*.test.ts"Shared base config with per-service overrides:
// apps/users-service/reqprobe.config.ts
import baseConfig from '../../reqprobe.base.config.ts';
export default {
...baseConfig,
baseUrl: process.env.USERS_SERVICE_URL ?? 'http://localhost:3001',
};src/
├── types/ shared TypeScript contracts
├── config/ config file loader
├── request/ HTTP client (fetch-based, Node 18+)
├── assertions/ assertion library (toBe, toHaveStatus, …)
├── dsl/ global test() + expect() DSL
├── runner/ test orchestrator
├── openapi/ spec loading, $ref resolution, Ajv validation
├── reporters/ JSON + HTML report generation
├── cli/ CLI commands (run, init)
└── utils/ logger
Dependency rules: each module only imports from modules below it. No circular dependencies. No DI framework.
| Status | Feature |
|---|---|
| ✅ | TypeScript-first test runner |
| ✅ | DSL (test()) + suite (TestSuite) patterns |
| ✅ | Full assertion library |
| ✅ | OpenAPI 3.x response validation |
| ✅ | HTML + JSON reports |
| ✅ | CI exit codes |
| ✅ | .env support |
| 🔜 | beforeAll / afterAll hooks (v0.2) |
| 🔜 | Watch mode (reqprobe run --watch) |
| 🔜 | Scaffold from OpenAPI spec |
| 🔜 | Parallel test execution |
| 🔜 | Response time assertions (toRespondWithin) |
| 🔜 | JUnit XML report (Jenkins / Azure DevOps) |
| 🔜 | gRPC support |
| 🔜 | VS Code extension |
Contributions are welcome. reqprobe is intentionally small — please keep PRs focused.
git clone https://github.com/shashi089/reqprobe.git
cd reqprobe
npm install
npm run build- No new runtime dependencies without discussion — current footprint is intentionally minimal (
ajv,commander,dotenv,fast-glob,picocolors,tsx) - Single responsibility — each module in
src/has one job - No circular imports — dependency graph is strictly one-way
- TypeScript strict mode —
tscmust exit 0 before any PR is merged
- Fork the repo and create a feature branch
- Make your change + update or add examples in
examples/ - Run
npm run build— must exit 0 - Open a PR with a clear description of what and why
Open an issue with:
- reqprobe version (
npx reqprobe --version) - Node version (
node --version) - Minimal reproduction (test file + config)
- Actual vs expected output
MIT © Shashidhar Naik