API Testing, Supercharged.
Napper is a free, open-source API testing tool for anyone testing APIs. It runs from the command line and edits natively in VS Code, Zed, and any editor via a portable language server.
Define HTTP requests as plain text .nap files, add declarative assertions, chain them into test suites, and run everything in CI/CD with JUnit output.
As simple as curl for quick requests. As powerful as your own code — script in JavaScript, Python, F#, or C#.
VS Code Marketplace · Website · Documentation · Releases
Everything you need for API testing. Nothing you don't.
- CLI First (
cli-run) — The command line is the product. Run requests, execute test suites, and integrate with CI/CD pipelines from your terminal. Napper ships as a self-contained native binary — not a .NET DLL — with zero runtime dependencies. - Editor-Native, LSP-Powered (
vscode-extension,lsp) — First-class extensions for VS Code and Zed — plus every VS Code-compatible editor (Cursor, Windsurf, Antigravity, VSCodium) via the Open VSX Registry — and a portable language server that brings completions, diagnostics, and hover to any LSP editor. Syntax highlighting (vscode-syntax), request explorer (vscode-explorer), environment switching (vscode-env-switcher), and Test Explorer integration (vscode-test-explorer). Never leave your editor. - Script in Any Language (
script-js,script-py,script-fsx,script-csx) — Add playlist steps and[script]pre/post hooks in JavaScript, Python, F#, or C# — whatever your team already runs, mixed freely in a single playlist. Real runtimes (Node.js, Python 3, .NET), full ecosystem access (npm, PyPI, NuGet), no sandbox. Scripts pass/fail by exit code; JavaScript and Python additionally get an injectedctxobject (response, variables,ctx.set/ctx.fail/ctx.log). - Declarative Assertions (
nap-assert) — Assert on status codes (assert-status), JSON paths (assert-equals,assert-exists), headers (assert-contains), and response times (assert-lt) with a clean, readable syntax. No scripting required for simple checks. - Composable Playlists (
naplist-file) — Chain requests into test suites with.naplistfiles. Nest playlists (naplist-nested), reference folders (naplist-folder-step), pass variables between steps (naplist-var-scope). - OpenAPI Import (
openapi-generate) — Generate test files from any OpenAPI spec. Point it at a file, and Napper creates.napfiles with requests, headers, bodies, and assertions. Optionally enhance with AI via GitHub Copilot (vscode-openapi-ai). - Plain Text, Git Friendly (
nap-file) — Every request is a.napfile. Every environment is a.napenvfile (env-file). Version control everything. No binary blobs, no lock-in.
VS Code — install from the Marketplace in one command:
code --install-extension nimblesite.napperOr search "Napper" in the Extensions panel (Ctrl+Shift+X / Cmd+Shift+X) and click Install.
VS Code forks — Cursor, Windsurf, Antigravity, VSCodium — open the editor's Extensions panel and search "Napper". The extension is published to the Open VSX Registry that these editors use. You can also grab the .vsix from Releases and use Install from VSIX....
Zed — install the Napper extension from Zed's extension registry.
Any other editor — the CLI ships a language server (napper lsp); point your editor's LSP client at it for completions, diagnostics, and hover.
Requirements: VS Code 1.99.0 or later (or an equivalent fork). The extension shells out to the CLI, so install it too (below).
The CLI is a self-contained native binary with no runtime dependencies — no .NET, Node, or Python required.
macOS / Linux — Homebrew:
brew tap Nimblesite/tap
brew install napperWindows — Scoop:
scoop bucket add Nimblesite https://github.com/Nimblesite/scoop-bucket
scoop install napperVerify the install:
napper --versionOther install options — direct binary, install script, build from source
Direct download — grab the binary for your platform from the latest release (napper-osx-arm64, napper-osx-x64, napper-linux-x64, napper-win-x64.exe). On macOS / Linux, make it executable and put it on your PATH:
chmod +x napper-osx-arm64 && mv napper-osx-arm64 /usr/local/bin/napperInstall script (macOS / Linux):
curl -fsSL https://raw.githubusercontent.com/Nimblesite/napper/main/scripts/install.sh | bashBuild from source (requires the .NET SDK + make):
git clone https://github.com/Nimblesite/napper.git && cd napper && make install-binariesNote: Script hooks need a runtime only for the language you write in — JavaScript (
.js) needs Node.js 18+, Python (.py) needs Python 3.9+, and F# (.fsx) / C# (.csx) need the .NET 10 SDK. Runtimes are found by command name (node,python3,dotnet) on yourPATH. Plain.napand.naplistfiles need nothing extra. In JavaScript and Python thectxobject is injected automatically — nothing toimport, nonpm install/pip install.
See the full installation guide for VSIX manual install, troubleshooting, and macOS Gatekeeper notes.
A .nap file can be as simple as one line:
GET https://httpbin.org/get
[request]
method = POST
url = {{baseUrl}}/posts
[request.headers]
Content-Type = application/json
Accept = application/json
[request.body]
"""
{
"title": "Nap Integration Test",
"body": "This post was created by the Nap API testing tool",
"userId": {{userId}}
}
"""
[assert]
status = 201
body.id exists
body.title = Nap Integration Test
body.userId = {{userId}}
[meta]
name = Get user by ID
description = Fetches a single user and asserts shape
tags = users, smoke
[vars]
userId = 42
[request]
method = GET
url = https://api.example.com/users/{{userId}}
[request.headers]
Authorization = Bearer {{token}}
Accept = application/json
[assert]
status = 200
body.id = {{userId}}
body.name exists
headers.Content-Type contains "json"
duration < 500ms
[script]
pre = ./scripts/auth.js
post = ./scripts/validate-user.js
# Run a single request
napper run ./health.nap
# Run a full test suite
napper run ./smoke.naplist
# With environment + JUnit output
napper run ./tests/ --env staging --output junit| Extension | Spec ID | Purpose | Example |
|---|---|---|---|
.nap |
nap-file |
Single HTTP request with optional assertions and scripts | get-users.nap |
.naplist |
naplist-file |
Ordered playlist of steps (requests, scripts, nested playlists) | smoke.naplist |
.napenv |
env-base |
Environment variables (base config, checked into git) | .napenv |
.napenv.local |
env-local |
Local secrets (gitignored) | .napenv.local |
.napenv.<name> |
env-named |
Named environment | .napenv.staging |
.js / .mjs / .cjs |
script-js |
JavaScript scripts (Node.js) — playlist steps and pre/post hooks, with an injected ctx |
setup.js |
.py |
script-py |
Python scripts (Python 3) — playlist steps and pre/post hooks, with an injected ctx |
setup.py |
.fsx |
script-fsx |
F# scripts (dotnet fsi) — playlist steps and pre/post hooks (exit-code) |
setup.fsx |
.csx |
script-csx |
C# scripts (dotnet script) — playlist steps and pre/post hooks (exit-code) |
setup.csx |
[meta]
name = JSONPlaceholder CRUD
description = Full create-read-update-delete lifecycle for posts
[steps]
../scripts/setup.fsx
./01_get-posts.nap
./02_get-post-by-id.nap
./03_create-post.nap
./04_update-post.nap
./05_patch-post.nap
./06_delete-post.nap
../scripts/teardown.fsx
Runnable examples (examples/): the same CRUD suite in each language — crud.naplist (F#), crud-javascript.naplist, crud-python.naplist, crud-csharp.naplist — plus mixed-scripts.naplist (all four languages in one playlist) and scripting-ctx/ (the injected ctx object in JavaScript and Python: ctx.set, ctx.fail, ctx.response).
.napenv (base, checked into git):
baseUrl = https://jsonplaceholder.typicode.com
userId = 1
postId = 1
.napenv.local (secrets, gitignored):
token = eyJhbGci...
apiKey = sk-secret-key
Select a named environment with --env:
napper run ./smoke.naplist --env stagingVariable priority (highest wins):
--var key=valueCLI flags (cli-var).napenv.local(env-local).napenv.<name>named environment (env-named).napenvbase (env-base)[vars]in.nap/.naplistfiles (nap-vars)
Generate .nap test files automatically from any OpenAPI or Swagger spec. Napper creates one file per operation, a .naplist playlist, and a .napenv environment file — giving you a working test suite in seconds.
Supported formats: OpenAPI 3.0.x, OpenAPI 3.1.x, Swagger 2.0 (JSON input).
# Generate from a local spec file
napper generate openapi ./petstore.json --output-dir ./tests
# Output a JSON summary for scripting
napper generate openapi ./spec.json --output-dir ./tests --output jsonOpen the Command Palette (Ctrl+Shift+P / Cmd+Shift+P) and choose:
- Napper: Import OpenAPI from URL — paste a URL (e.g.
https://petstore3.swagger.io/api/v3/openapi.json). Napper downloads the spec and generates files. - Napper: Import OpenAPI from File — browse to a local
.jsonspec file.
Both commands prompt for an output folder and offer basic or AI-enhanced generation.
Endpoints are grouped into subdirectories by API tag:
tests/
├── pets/
│ ├── get-pets.nap
│ ├── post-pets.nap
│ └── get-pets-petId.nap
├── store/
│ └── get-store-inventory.nap
├── petstore.naplist
└── .napenv
Each .nap file includes the method, URL (with path params as {{variables}}), auth headers, request body (from schema), and status code assertions. The .napenv file contains the base URL from the spec's servers field and variable placeholders for auth tokens.
With GitHub Copilot available, choose AI-enhanced generation to get:
- Semantic assertions beyond status codes (e.g.
body.email contains @) - Realistic test data in request bodies instead of placeholder values
- Logical playlist ordering (auth first, then CRUD in dependency order)
Falls back to basic generation automatically if Copilot is unavailable.
See the full OpenAPI import guide for authentication handling, $ref resolution, customisation tips, and troubleshooting.
Usage:
napper run <file|folder> Run a .nap file, .naplist playlist, or folder (cli-run)
napper check <file> Validate a .nap or .naplist file (cli-check)
napper generate openapi <spec> --output-dir <dir> Generate .nap files from OpenAPI spec (cli-generate)
napper help Show this help
Options:
--env <name> Environment name (loads .napenv.<name>) (cli-env)
--var <key=value> Variable override (repeatable) (cli-var)
--output <format> Output: pretty, junit, json, ndjson (cli-output)
--output-dir <dir> Output directory for generate command (cli-output-dir)
--version Print the installed CLI version
--verbose Enable debug-level logging (cli-verbose)
| Exit Code | Meaning |
|---|---|
| 0 | All assertions passed |
| 1 | One or more assertions failed |
| 2 | Runtime error (network, script error, parse error) |
| Feature | Napper | Postman | Bruno | .http files |
|---|---|---|---|---|
| CLI-first design | Yes | No | GUI-first | No CLI |
| Editor integration | VS Code, Cursor, Windsurf, Antigravity, Zed & LSP | Separate app | Separate app | VS Code only |
| Git-friendly files | Yes | JSON blobs | Yes | Yes |
| OpenAPI import | URL + file + AI | Import only | Import only | No |
| Assertions | Declarative + scripts | JS scripts | JS scripts | None |
| Full scripting language | JS, Python, F#, C# | Sandboxed JS | Sandboxed JS | None |
| CI/CD output formats | JUnit, JSON, NDJSON | Via Newman | Via CLI | None |
| Test Explorer | Native | No | No | No |
| Free & open source | Yes | Freemium | Yes | Yes |
| No account required | Yes | Account needed | Yes | Yes |
Napper's engine is written entirely in F#. Parsing, environment resolution, the HTTP runner, assertions, OpenAPI generation, and the language server all live in a single shared core (Napper.Core) — the CLI (Napper.Cli) and the language server (Napper.Lsp) are thin shells over it, so behaviour is identical in your terminal and your editor.
- One shared core, zero duplication —
Napper.CliandNapper.Lspboth build onNapper.Core. The same parser that powersnapper runpowers editor completions and diagnostics. - Compiled to a native binary — the CLI publishes as a self-contained NativeAOT executable: no .NET runtime, ~10 ms cold start, one statically-linked binary per platform.
- Parser combinators, not regex —
.nap,.naplist, and.napenvfiles are parsed with FParsec for precise, position-aware diagnostics. - Functional core — immutable models,
Result-based error handling, and pure functions over the request/response pipeline.
Curious how it fits together? Start with src/Napper.Core/.
my-api/
├── .napenv # Base variables (checked in)
├── .napenv.local # Secrets (gitignored)
├── .napenv.staging # Staging environment
├── auth/
│ ├── 01_login.nap
│ └── 02_refresh-token.nap
├── users/
│ ├── 01_get-user.nap
│ ├── 02_create-user.nap
│ └── 03_delete-user.nap
├── scripts/
│ ├── setup.fsx
│ ├── setup.csx
│ └── teardown.fsx
└── smoke.naplist
MIT
