Introducing Napper: CLI-First API Testing, Scripted in Your Language
API testing tools have a problem. They're either too simple (.http files with no assertions and no CLI) or too heavy (Postman with its mandatory accounts, cloud sync, and paid tiers). Bruno moved the needle with git-friendly collections, but it's still a GUI-first tool with sandboxed JavaScript.
Napper takes a different approach. It's a free, open-source API testing tool for anyone testing APIs: the CLI is the primary interface, everything is stored as plain text, and you script in the language you already use — JavaScript, Python, F#, or C# — on a real runtime, with no sandbox. Napper ships as a self-contained native binary (not a .NET DLL) and edits natively in VS Code, Zed, the VS Code-compatible editors Cursor, Windsurf, and Antigravity, and any editor via a portable language server.
The CLI is the product
Napper is not a GUI with a CLI bolted on. The command line is the primary interface. Every feature works from the terminal first, then from VS Code second.
# Run a single request
napper run ./health.nap
# Run a full test suite
napper run ./smoke.naplist
# Run with a specific environment and JUnit XML output for CI/CD
napper run ./tests/ --env staging --output junit > results.xml
The CLI binary is self-contained with no runtime dependencies. It runs on Windows, macOS, and Linux. Install it with Homebrew (brew tap Nimblesite/tap && brew install napper) or Scoop (scoop bucket add Nimblesite https://github.com/Nimblesite/scoop-bucket && scoop install napper), or download it from GitHub Releases.
Plain text everything — git-friendly by design
Every request is a .nap file. Every test suite is a .naplist file. Every environment is a .napenv file. All plain text. All in your repo. Diffs are readable. Code reviews are meaningful. No binary blobs, no JSON dumps, no proprietary formats.
Here's what a .nap file looks like:
[meta]
name = Create a new post
tags = posts, crud
[request]
method = POST
url = {{baseUrl}}/posts
[request.headers]
Content-Type = application/json
Authorization = Bearer {{token}}
[request.body]
"""
{
"title": "Nap Integration Test",
"body": "Created by Napper",
"userId": 1
}
"""
[assert]
status = 201
body.id exists
body.title = Nap Integration Test
duration < 2s
That's a complete HTTP request with headers, a JSON body, and declarative assertions — all in one readable file. No scripting needed for the common cases.
Scripting in your language — real runtimes, no sandbox
This is where Napper breaks away from every other API testing tool. Postman and Bruno give you a sandboxed JavaScript environment with limited APIs. Napper lets you script in JavaScript, Python, F#, or C# — whichever your team already runs — on the real runtime, with full access to npm, PyPI, and NuGet. In JavaScript and Python, Napper injects a global ctx object that exposes the request/response and lets a script pass variables to later steps.
Here's the same post-request hook — extract a user id, chain it forward, validate — in JavaScript and Python:
// validate-response.js
// ctx is injected as a global — no import, no npm install
const body = ctx.response.json;
ctx.set("userId", String(body.id));
if (body.id <= 0) ctx.fail("User ID must be positive");
ctx.log(`Created user ${body.id}`);
# validate_response.py
# ctx is injected as a global — no import, no pip install
body = ctx.response.json
ctx.set("userId", str(body["id"]))
if body["id"] <= 0:
ctx.fail("User ID must be positive")
ctx.log(f"Created user {body['id']}")
F# and C# run too — on the full .NET SDK
Prefer .NET? F# (.fsx) and C# (.csx) scripts run as playlist steps and [script] pre/post hooks via dotnet fsi and dotnet script, with the entire NuGet ecosystem. Today they communicate through stdout and their exit code — the injected ctx object is JavaScript and Python only — so a .csx step that exits non-zero fails the run:
// guard.csx — a playlist step or a [script] post hook
var baseUrl = Environment.GetEnvironmentVariable("API_BASE_URL");
if (string.IsNullOrEmpty(baseUrl))
{
Console.Error.WriteLine("API_BASE_URL is not set");
Environment.Exit(1); // non-zero exit -> the step fails
}
Console.WriteLine("[guard] environment looks good");
C# and F# scripts can use HttpClient, System.Text.Json, System.Security.Cryptography, LINQ, async/await — everything .NET offers — and any NuGet package. No sandbox. The runnable crud-csharp.naplist example wraps the CRUD requests in C# setup and teardown steps.
You can mix languages in the same project. A single .naplist can reference .js, .py, .csx, and .fsx files as steps. Choose whichever language your team already tests with — or use several. See the Scripting Overview for the full picture.
Declarative assertions — no scripting needed for the common cases
Most API tests check the same things: status codes, JSON values, headers, and response times. Napper's assertion syntax handles all of this declaratively — no scripting required:
[assert]
status = 200
body.id = 1
body.name exists
body.email contains "@"
headers.Content-Type contains "application/json"
duration < 500ms
All assertions are evaluated and reported individually. When the declarative syntax isn't enough, drop into JavaScript, Python, C#, or F# for complex validation logic.
Composable test suites with .naplist files
Chain requests into ordered test suites with .naplist files. Nest playlists inside other playlists, reference entire folders, and mix .nap requests with .js, .py, .csx, and .fsx scripts:
[meta]
name = Full API Suite
[steps]
./scripts/setup.csx
./auth/login.nap
./crud-tests.naplist
./edge-cases/
./scripts/teardown.csx
Built for CI/CD from day one
Napper is designed for continuous integration. The CLI binary is self-contained with no runtime dependencies. It outputs JUnit XML, JSON, and NDJSON formats natively (cli-output).
GitHub Actions
name: API Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Download Napper CLI
run: |
curl -L -o napper https://github.com/Nimblesite/napper/releases/latest/download/napper-linux-x64
chmod +x napper
sudo mv napper /usr/local/bin/
- name: Run API tests
run: napper run ./tests/ --env ci --output junit > results.xml
- name: Upload results
uses: actions/upload-artifact@v4
if: always()
with:
name: api-test-results
path: results.xml
Napper exits with code 0 when all assertions pass and 1 when any assertion fails. This integrates natively with GitHub Actions, GitLab CI, Jenkins, Azure DevOps, and any CI platform that fails on non-zero exit codes.
Migrate from .http files with one command
Already using .http files with VS Code REST Client or JetBrains IDEs? Napper includes a built-in converter that transforms your existing .http files into .nap format:
# Convert a single file
napper convert http ./requests.http
# Convert an entire directory
napper convert http ./api-tests/ --output-dir ./nap-tests/
The converter supports both Microsoft (VS Code REST Client) and JetBrains (IntelliJ, Rider, WebStorm) .http dialects. It maps variables to .napenv files, preserves request names, converts JetBrains http-client.env.json environments, and warns about unsupported features like WebSocket or gRPC requests.
Migration is non-destructive — your original .http files are untouched. Use --dry-run to preview what will be generated before writing any files. Once converted, you get all the benefits of Napper: declarative assertions, composable test suites, scripting in JavaScript, Python, F#, or C#, and CI/CD integration.
See Napper vs .http files for a full comparison.
Editor-native, LSP-powered
Napper meets you in your editor. There are first-class extensions for VS Code and Zed — and because the extension is published to the Open VSX Registry, it installs in every VS Code-compatible editor too: Cursor, Windsurf, Antigravity, and VSCodium. A portable language server brings completions, diagnostics, and hover to any other editor that speaks LSP. The Napper VS Code extension brings the full experience into your editor:
- Syntax highlighting for
.nap,.naplist, and.napenvfiles - Request explorer in the sidebar with a tree view of all requests and playlists
- Run requests directly from the editor with a single click
- Environment switching between dev, staging, production, and custom environments
- Test Explorer integration with native VS Code test results
- Response inspection with headers, body, and timing information
- Copy as curl to share requests with teammates who don't use Napper
The extension relies on the CLI binary to run requests — install the CLI first, then install the extension from the VS Code Marketplace (or search Napper in the Extensions panel of Cursor, Windsurf, or Antigravity):
code --install-extension nimblesite.napper
How does Napper compare?
| 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 | REST Client |
| Git-friendly files | Plain text | JSON blobs | Yes | Yes |
| Assertions | Declarative + scripts | JS scripts | JS scripts | None |
| Scripting language | JS, Python, F#, C# | Sandboxed JS | Sandboxed JS | None |
| CI/CD output | JUnit, JSON, NDJSON | Via Newman | Via CLI | None |
| Test Explorer | Native | No | No | No |
| OpenAPI import | URL + file + AI | Import only | Import only | No |
| .http file migration | Built-in converter | Import only | No | N/A |
| Account required | No | Yes | No | No |
| Price | Free (MIT) | Freemium | Free (MIT) | Free |
Get started in 5 minutes
- Install the CLI or VS Code extension
- Follow the Quick Start guide to create your first request
- Migrate existing .http files with
napper convert http - Add assertions to validate responses
- Set up environments for different targets
- Write scripts in JavaScript, Python, C#, or F# for advanced flows
- Run everything in CI/CD with JUnit XML output
Napper is free, open source, and MIT licensed. Browse the source code and examples on GitHub.