Auto API Tester
Made by rAvi
Test any Express route straight from your source code. Put the cursor on a route, run Auto API Tester: Test This Route (or click ▶ Test above the route) and you get:
- the route's full URL, with
app.use('/prefix', router) mounts resolved across files
- a request body, query, path params and headers generated from the route's Zod/Joi schema, TypeScript types or inline validation
- a real request to your local server (after confirmation for anything but GET/HEAD/OPTIONS)
- the response checked for status, valid JSON, the shape the handler's TypeScript types promise, and timing
No configuration is needed to start. The base URL comes from your .env, package.json scripts or app.listen(port), and you're asked once if none of those turn it up.
Usage
| Action |
How |
| Test the route under the cursor |
Cmd+Alt+T / Ctrl+Alt+T, the editor context menu, or ▶ Test CodeLens |
| Edit the generated body and re-run |
Click the body in the result panel, edit, Run with edited body |
| New random data |
New data in the panel (seeded mode always regenerates the same data) |
| Copy the request |
Copy as curl (credentials are masked) |
| Save a test / reopen results |
Save test, then Auto API Tester: Show Test History |
| Base URL, auth, dry-run, data mode |
Auto API Tester: Configure Project |
| Turn the confirmation prompt on/off |
Auto API Tester: Toggle Dry-Run Guard |
If the cursor is not on a route, you get a list of the routes in the file.
What is detected
Routes: app.get/post/put/patch/delete(path, …handlers), router.x(...), element access like app['post'], app.route('/x').get(h).post(h), plus Express 5 path syntax.
Mount prefixes: app.use('/api', router) in the same file, across ES imports, CommonJS require, barrel re-exports (routes/index.ts), nested routers, app.use(['/a', '/b'], router), class routers (new UserRoutes().router) and factories (app.use('/x', createRouter())). If a router is mounted at several paths, you choose one.
Request fields, in strict priority order. The first source that finds fields wins, and nothing is merged silently:
- Zod: schemas passed to middleware (
validate(schema), validate({ body, query, params })) or parsed in the handler (schema.parse(req.body)), including .extend/.merge/.partial/.pick/.omit, imported across files
- Joi:
celebrate({ [Segments.BODY]: Joi.object(...) }), schema.validate(req.query)
- TypeScript:
Request<Params, ResBody, ReqBody, Query>, req.body as Dto, const dto: Dto = req.body
- Inline validation:
if (!email.includes('@')), typeof age !== 'number', password.length < 8, validator.isEmail(x), ['a','b'].includes(role)
- Field names:
email, userId, phone, createdAt, isActive, page…
- Fallback: an empty body
When sources disagree (for example, the handler reads req.body.nickname but the Zod schema has no such field), the panel's Analysis notes say so.
Response: res.status(201).json(user) sets the expected status, and the TypeScript type of user becomes the shape the response is validated against.
Safety
- Dry-run guard (on by default). POST, PUT, PATCH and DELETE show "This will send a real POST to http://localhost:3000/api/users" with Run once / Run and remember before anything is sent.
- Secrets (tokens, passwords, API keys) live in VS Code SecretStorage, scoped to the workspace. They are never written to settings or project files, and they are masked in the panel, in history and in curl output.
- History keeps the last 100 results, and bodies over 100 KB are not stored.
Configuration
Optional .vscode/auto-api-tester.json (validated with IntelliSense). It overrides VS Code settings:
{
"baseUrl": "http://localhost:3000",
"timeout": 5000,
"headers": { "X-Tenant": "dev" },
"auth": { "type": "bearer" },
"dryRun": { "default": true, "allowedMethods": ["GET", "HEAD", "OPTIONS"] },
"generator": { "mode": "seeded", "locale": "en-IN" }
}
auth.type is one of none, bearer, basic (with username), apiKey (with name and in: "header" | "query") or cookie. Set the secret with Configure Project → Authentication, or you'll be asked for it on the first request.
Base URL order: project file → autoApiTester.baseUrl (if you set it) → .env / .env.local (API_URL, BASE_URL or PORT + HOST) → package.json scripts → app.listen(port) in src/server|index|app → ask once.
| Setting |
Default |
|
autoApiTester.baseUrl |
http://localhost:3000 |
Used when set explicitly |
autoApiTester.timeout |
5000 |
ms |
autoApiTester.dryRun.default |
true |
Confirm non-GET requests |
autoApiTester.generator.mode |
seeded |
seeded: same data every run; random: new data every run |
autoApiTester.allowSelfSignedCertificates |
false |
Local HTTPS only |
autoApiTester.codeLens.enabled |
true |
|
autoApiTester.debug |
false |
Verbose log in the Auto API Tester output channel |
Seeded data
Each field's value comes from hash(file:METHOD:/full/path:fieldPath), so the same route always gets the same data, and adding a field doesn't change the others. Random mode records its seed in every result, so any run can be replayed exactly.
Limitations
- Express only. Fastify is detected as a preview (no
register prefixes yet). NestJS is not supported.
- Routes whose paths are built at runtime (
app.get(BASE + '/x')) are not detected.
- Response validation needs a TypeScript type on what the handler sends.
any payloads are shown but not validated.
Development
npm install
npm run build # dist/extension.js, dist/engine.js (lazy-loaded), dist/webview.js
npm test # vitest: unit + fixture + end-to-end tests
npm run test:coverage
npm run lint && npm run typecheck && npm run format:check
npm run package # produces auto-api-tester-<version>.vsix
Press F5 to launch an Extension Development Host with test/fixtures/express-app open.
Layout: src/core holds the framework-independent pipeline (parser → route → analyzer → generator → request → response → testing) and has no VS Code imports. src/frameworks holds the adapters, src/commands, src/ui, src/config and src/storage hold the VS Code layer, and webview/ is the React result panel. Everything that needs ts-morph is bundled into engine.js and loaded on the first analysis, so the extension activates without loading the TypeScript compiler.
License
MIT © rAvi