npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@umeshindu222/apisnap

v1.1.6

Published

Instant API auto-discovery and health-check CLI for Express.js

Readme

📸 APISnap

Instant API auto-discovery and health-check CLI for Express.js

npm version License: MIT


What is APISnap?

Every time you change your Express backend, manually testing all your routes in Postman is slow and boring. APISnap auto-discovers every route in your app and health-checks all of them in seconds — with zero configuration.

One command. Every route. Instant results.


Features

  • 🔍 Auto Route Discovery — finds every route including nested sub-routers
  • 🔐 Full Auth Support — JWT, API Keys, Cookies, multiple headers at once
  • 💡 Auth Hints — tells you exactly how to fix 401/403 errors
  • Slow Route Detection — flags endpoints that are too slow
  • 🔁 Retry Logic — auto-retries failed requests
  • 📊 HTML Reports — beautiful visual reports you can share
  • 💾 JSON Export — structured output for CI/CD pipelines
  • ⚙️ Config File — save your settings so you don't retype every time
  • 🎯 Method Filter — test only GET, POST, DELETE etc.
  • 🧠 Smart Path Params — auto-replaces :id, :slug, :uuid with safe defaults
  • 🚀 Express v4 & v5 — works with both versions

Quick Start

Step 1 — Install

npm install @umeshindu222/apisnap

Step 2 — Add to your server file

Open your main server file (server.js, app.js, app.ts) and add 2 lines:

const apisnap = require('@umeshindu222/apisnap'); // ADD THIS at the top

// ... all your existing routes stay exactly the same ...

apisnap.init(app); // ADD THIS — after your routes

Step 3 — Start your server

node server.js
# or
npm run dev

You will see this line confirming it works:

✅ [APISnap] Discovery active → http://localhost:3000/__apisnap_discovery

Step 4 — Run the health check

Open a second terminal in your project folder and run:

npx @umeshindu222/apisnap --port 3000

That's it. You will see every route tested automatically.


Full Setup Examples

JavaScript (CommonJS)

const express = require('express');
const apisnap = require('@umeshindu222/apisnap');

const app = express();
app.use(express.json());

// Your routes
app.get('/health', (req, res) => res.json({ status: 'ok' }));
app.get('/users', (req, res) => res.json({ users: [] }));
app.post('/users', (req, res) => res.json({ message: 'created' }));
app.delete('/users/:id', (req, res) => res.json({ deleted: true }));

// APISnap — place AFTER your routes
apisnap.init(app);

app.listen(3000, () => console.log('Server running on port 3000'));

TypeScript

⚠️ TypeScript users — import must be written exactly like this:

// ✅ CORRECT — use import * as
import * as apisnap from '@umeshindu222/apisnap';

// ❌ WRONG — will give "has no default export" red error
import apisnap from '@umeshindu222/apisnap';

// ❌ WRONG — never mix import and require in TypeScript
import apisnap from '@umeshindu222/apisnap';
const apisnap = require('@umeshindu222/apisnap');

Full example:

import express, { Application, Request, Response, NextFunction } from 'express';
import * as apisnap from '@umeshindu222/apisnap'; // ✅ correct

const app: Application = express();
app.use(express.json());

// Your routes
app.get('/health', (req: Request, res: Response) => res.json({ status: 'ok' }));
app.get('/users', (req: Request, res: Response) => res.json({ users: [] }));
app.post('/users', (req: Request, res: Response) => res.json({ message: 'created' }));

// APISnap — place AFTER your routes
apisnap.init(app);

app.listen(3000, () => console.log('Server running on port 3000'));

export default app;

If TypeScript still shows a red error under the import, create a file called apisnap.d.ts in your project root:

declare module '@umeshindu222/apisnap' {
  interface APISnapOptions {
    skip?: string[];
    name?: string;
  }
  export function init(app: any, options?: APISnapOptions): void;
}

With Sub-Routers (Real World Project)

const express = require('express');
const apisnap = require('@umeshindu222/apisnap');
const userRoutes = require('./routes/users');
const postRoutes = require('./routes/posts');
const authRoutes = require('./routes/auth');

const app = express();
app.use(express.json());

// Register all your routers
app.use('/api/users', userRoutes);
app.use('/api/posts', postRoutes);
app.use('/api/auth', authRoutes);

// APISnap discovers ALL routes including sub-routers
apisnap.init(app);

app.listen(3000);

With Global Auth Middleware

⚠️ Important: If you use global auth middleware (app.use(authMiddleware)), you must place apisnap.init(app) before it. Otherwise auth will block the discovery endpoint.

const express = require('express');
const apisnap = require('@umeshindu222/apisnap');
const authMiddleware = require('./middleware/auth');

const app = express();
app.use(express.json());

// Register routes first
app.get('/health', (req, res) => res.json({ status: 'ok' }));
app.use('/api/users', userRoutes);
app.use('/api/posts', postRoutes);

// ✅ APISnap BEFORE global auth middleware
apisnap.init(app);

// ✅ Global auth AFTER apisnap
app.use(authMiddleware);

app.listen(3000);

Skip Specific Routes

// Don't test certain routes (e.g. auth callbacks, webhooks, admin)
apisnap.init(app, {
  skip: ['/api/auth', '/webhooks', '/admin']
});

🔐 Fixing 401 / 403 Errors

401 and 403 errors are completely normal — it just means your routes are protected and APISnap needs credentials, exactly like any real client would.

JWT / Bearer Token

npx @umeshindu222/apisnap --port 3000 -H "Authorization: Bearer eyJhbGci..."

API Key

npx @umeshindu222/apisnap --port 3000 -H "x-api-key: your-secret-key"

Cookie / Session Auth (Passport.js, express-session)

npx @umeshindu222/apisnap --port 3000 --cookie "connect.sid=s%3Aabc123"

Multiple Headers at Once

The -H flag can be repeated as many times as you need:

npx @umeshindu222/apisnap --port 3000 \
  -H "Authorization: Bearer TOKEN" \
  -H "x-api-key: SECRET" \
  -H "x-tenant-id: my-company"

How to Get Your JWT Token

  1. Open Postman
  2. Call your login endpoint:
POST http://localhost:3000/api/auth/login
Content-Type: application/json

{
  "email": "[email protected]",
  "password": "yourpassword"
}
  1. Copy the token from the response
  2. Use it in the -H flag above

⚙️ Config File (Recommended)

Instead of typing your token every single time, save your settings in a config file. Create .apisnaprc.json in your project root:

{
  "port": "3000",
  "slow": "300",
  "headers": [
    "Authorization: Bearer YOUR_JWT_TOKEN_HERE"
  ]
}

Now just run with no flags:

npx @umeshindu222/apisnap

APISnap reads the config file automatically.

⚠️ Add .apisnaprc.json to your .gitignore so your token is never committed to GitHub.

Full Config File Options

{
  "port": "3000",
  "slow": "300",
  "timeout": "5000",
  "retry": "1",
  "headers": [
    "Authorization: Bearer YOUR_TOKEN",
    "x-api-key: YOUR_API_KEY"
  ],
  "cookie": "sessionId=your-session-id",
  "params": {
    "id": "1",
    "userId": "1",
    "slug": "hello-world",
    "uuid": "550e8400-e29b-41d4-a716-446655440000"
  }
}

CLI Reference

npx @umeshindu222/apisnap [options]

| Option | Description | Default | |--------|-------------|---------| | -p, --port <n> | Port your server runs on | 3000 | | -H, --header <str> | Auth header — can repeat multiple times | — | | -c, --cookie <str> | Cookie string for session auth | — | | -s, --slow <n> | Flag routes slower than this (ms) | 200 | | -t, --timeout <n> | Request timeout in ms | 5000 | | -r, --retry <n> | Retry failed requests N times | 0 | | -e, --export <file> | Save JSON report to file | — | | --html <file> | Save HTML report to file | — | | --only <methods> | Only test these methods e.g. GET,POST | — | | --base-url <url> | Test a different server e.g. staging | localhost | | --params <json> | Override path params as JSON | — | | --fail-on-slow | Exit code 1 if slow routes found | false |


Examples

Basic check

npx @umeshindu222/apisnap --port 3000

With JWT auth

npx @umeshindu222/apisnap -p 3000 -H "Authorization: Bearer eyJhbGci..."

Custom path params

If your route is /users/:id/posts/:postId and the default 1 doesn't work in your database:

npx @umeshindu222/apisnap --params '{"id":"42","postId":"7"}'

Only test GET routes

npx @umeshindu222/apisnap --only GET

Test your staging server

npx @umeshindu222/apisnap --base-url https://staging.myapp.com -H "Authorization: Bearer TOKEN"

Generate an HTML report

npx @umeshindu222/apisnap --html report

Then open it:

# Windows
start report.html

# Mac
open report.html

# Linux
xdg-open report.html

Generate a JSON report

npx @umeshindu222/apisnap --export report
# Creates: report.json

Retry flaky endpoints

npx @umeshindu222/apisnap --retry 3

CI/CD — fail the pipeline if any endpoint is broken

npx @umeshindu222/apisnap --export ci-report
# Exits with code 1 automatically if any endpoint fails

All options together

npx @umeshindu222/apisnap \
  -p 5000 \
  -H "Authorization: Bearer TOKEN" \
  -H "x-api-key: SECRET" \
  --cookie "sessionId=abc" \
  --slow 300 \
  --retry 2 \
  --html report \
  --export report

Sample Output

📸 APISnap v1.1.4
   Target:     http://localhost:3000
   Slow:       >200ms
   Timeout:    5000ms
   Headers:    {"Authorization":"Bearer ey••••••"}

✔ Connected! Found 6 endpoints to test.

  ✔ GET     /health                             [200]  3ms
  ✔ GET     /users                              [200] 12ms
  ✔ POST    /users                              [200]  8ms
  ✔ GET     /users/1                            [200]  5ms
  ⚠️  GET     /reports                            [200] 543ms ← slow!
  ✖ DELETE  /users/1                            [401]  2ms
     💡 Hint: 401 Unauthorized — try adding -H "Authorization: Bearer YOUR_TOKEN"

📊 Summary:
  ✅ Passed:  5
  ❌ Failed:  1
  ⚠️  Slow:    1 (>200ms)
  ⏱  Avg:    95ms
  🕐 Total:  573ms

⚠️  Some endpoints are unhealthy!

JSON Report Format

{
  "tool": "APISnap",
  "version": "1.1.4",
  "generatedAt": "2026-03-08T10:00:00.000Z",
  "config": {
    "port": "3000",
    "slowThreshold": 200,
    "timeout": 5000
  },
  "summary": {
    "total": 6,
    "passed": 5,
    "failed": 1,
    "slow": 1,
    "avgDuration": 95,
    "totalDuration": 573
  },
  "results": [
    {
      "method": "GET",
      "path": "/users",
      "status": 200,
      "duration": 12,
      "success": true,
      "slow": false,
      "retries": 0
    }
  ]
}

CI/CD tip: Check summary.failed > 0 to fail your build automatically.


Common Problems & Fixes

"Cannot reach discovery endpoint"

✖ Cannot reach discovery endpoint: http://localhost:3000/__apisnap_discovery

Causes:

  • Your server is not running — start it first in another terminal
  • Wrong port — use -p YOUR_PORT to specify the correct one
  • You forgot to add apisnap.init(app) to your server

All routes show 401

Cause: Your routes are protected and no credentials were provided.

Fix:

npx @umeshindu222/apisnap -H "Authorization: Bearer YOUR_REAL_TOKEN"

Also check your middleware order — apisnap.init(app) must come before any global app.use(authMiddleware).


Routes are missing from the output

Cause: apisnap.init(app) was called before the routes were registered.

Fix: Make sure apisnap.init(app) is the last thing before app.listen():

// All routes first
app.use('/api/users', userRoutes);
app.use('/api/posts', postRoutes);

// APISnap last (before app.listen)
apisnap.init(app);

app.listen(3000);

Config file not loading

Cause: On Windows, creating JSON files with PowerShell echo saves them with wrong encoding (UTF-16).

Fix: Create .apisnaprc.json manually in VS Code or Notepad — File → Save As → select UTF-8 encoding.


404 on routes with path params

Routes like /users/:id get :id replaced with 1 by default. If 1 is not a valid ID in your database, override it:

npx @umeshindu222/apisnap --params '{"id":"YOUR_REAL_ID"}'

Or in your config file:

{
  "params": {
    "id": "64f1a2b3c4d5e6f7a8b9c0d1"
  }
}

How It Works

APISnap has two parts:

1. Middlewareapisnap.init(app) registers a hidden endpoint /__apisnap_discovery in your Express app. When called, it recursively walks the entire Express router stack and returns a map of every registered route — including all nested sub-routers.

2. CLInpx @umeshindu222/apisnap calls the discovery endpoint, gets the full route list, then sends a real HTTP request to each route using your headers and cookies. It replaces path params with smart defaults (:id1, :uuid → valid UUID, :slug"example"), measures response time, and reports everything.


Contributing

Contributions, issues and feature requests are welcome!

  1. Fork the repo
  2. Create your branch: git checkout -b feat/amazing-feature
  3. Commit: git commit -m 'feat: add amazing feature'
  4. Push: git push origin feat/amazing-feature
  5. Open a Pull Request

License

MIT © Umesh Induranga