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

ports-manager

v1.1.0

Published

Run paired frontend/backend projects on free ports, with the frontend automatically wired to wherever the backend landed and CORS handled for you.

Readme

🚦 Ports Manager


Why Ports Manager?

Running a second project when 3000 and 5000 are already busy usually means editing vite.config.ts, editing .env, and adding a new origin to your CORS allowlist — every time. Ports Manager does that for you, for the duration of the run, without touching your source files.

cd my-project
npx ports-manager
Root:      C:\work\my-project
Backend:   express / npm run dev / port 5001
Frontend:  vite / npm run dev / port 5175
Wiring:    vite dev proxy retargeted to backend 5001
CORS:      not needed (same origin)
Open:      http://localhost:5175

Start the same project again — or a different one — and the second run steps aside:

Backend:   express / npm run dev / port 3000 (5001 busy)
Frontend:  vite / npm run dev / port 3001 (5175 busy)
Wiring:    vite dev proxy retargeted to backend 3000
Open:      http://localhost:3001

What it does:

  • 🔎 Detects your frontend + backend or client + server
  • 📦 Checks the dev binaries exist before starting, and installs them if they don't
  • 🎯 Prefers each project's own port, falls back only when it is taken
  • 🔗 Rewrites the frontend's dev proxy so /api reaches the relocated backend
  • 🛡️ Sets the CORS origin variables your backend already reads
  • 🧹 Stops both process trees cleanly on Ctrl+C

No application source file is modified. The only file written is a temporary Vite config next to your own, named per process and deleted when the run ends. If you want it out of version control entirely, add this to .gitignore:

.ports-manager.*.vite.config.mjs

Install

npm install --global ports-manager

Or run it without installing:

npx ports-manager

Quick start

Your project should look like one of these:

my-project/
├── frontend/        # or client/
│   └── package.json
└── backend/         # or server/
    └── package.json

Run Ports Manager from my-project, not from inside either child. It prefers npm run dev and falls back to npm start.

How the wiring works

Moving the backend is the easy half. The half that breaks apps is everything that still points at the old port. Ports Manager picks the best available strategy:

| Frontend | Strategy | Result | |---|---|---| | Vite with a server.proxy | Generates a config extending yours with the proxy target repointed | Same origin, native HMR, no extra port | | CRA, Next.js, generic | Same-origin reverse proxy in front of both services | One URL serves the app and /api | | No proxy anywhere | Sets the API env var your source actually references | Cross-origin, CORS variables injected |

The proxy rewrite is deliberately narrow. Given this config:

proxy: {
  '/api':    { target: 'http://localhost:5001' },  // your backend
  '/qc-api': { target: 'http://localhost:5000' }   // some other service
}

only /api is repointed, because only it targets the backend's own port. /qc-api is left alone, and both decisions are printed at startup.

CORS

When the browser talks to a single origin — the Vite proxy or the same-origin proxy — there is no CORS to fix, and Ports Manager says so rather than pretending to act.

When requests really are cross-origin, it scans your backend for the origin variables it already reads (CORS_ORIGIN, ALLOWED_ORIGINS, CLIENT_URL, FRONTEND_URL, and similar) and sets those to the frontend's origin for the run. Only names your code references are set. An existing value in .env is extended, not replaced:

CORS: injecting CORS_ORIGIN=https://app.example.com,http://localhost:3001

If your backend reads no recognizable variable, --force-cors preloads a shim that applies CORS headers at response-send time, overriding whatever the app set:

npx ports-manager --force-cors

[!WARNING] The shim is intentionally permissive and meant only for local development.

Missing dependencies

A dev script naming a binary that was never installed used to fail with a bare 'vite' is not recognized as an internal or external command. Ports Manager now resolves every binary a dev script needs before spawning anything, and installs with the package manager your lockfile implies (npm, pnpm, yarn, or bun):

frontend: dependencies not installed
[frontend] running `npm install` in C:\work\my-project\frontend

Prefer to do it yourself:

npx ports-manager --no-install
dependencies are missing and --no-install was given
frontend: `vite` not found in C:\work\my-project\frontend
  fix: cd C:\work\my-project\frontend && npm install

Running several projects at once

Ports are held open from the moment they are chosen until the child processes start, so a second instance launched at the same moment cannot pick the same one. Each instance also records its ports in ~/.ports-manager/leases.json, keyed by pid, so concurrent runs avoid each other; entries whose process has exited are pruned automatically.

Common recipes

# Preview without installing or starting anything
npx ports-manager --dry-run

# Custom folder names
npx ports-manager --frontend-dir apps/web --backend-dir apps/api

# Restrict the search range and keep some ports free
npx ports-manager --range 4100-4900 --ban 4200,4300

# Pin one side
npx ports-manager --backend-port 5001

# Force the same-origin proxy even for Vite
npx ports-manager --proxy --api-prefix /api

# Let the backend come up first
npx ports-manager --wait-for-backend

Supported stacks

| Role | Detected | Port strategy | |---|---|---| | Frontend | Vite | --port + --strictPort, plus a generated config | | Frontend | Create React App | PORT environment variable | | Frontend | Next.js | -p argument | | Backend | Express, Koa, Fastify, NestJS | PORT environment variable | | Either | Generic Node project | PORT environment variable |

The natural port is read from .env, then the dev script, then vite.config.*, then a schema default such as PORT: z.coerce.number().default(5001) — so a backend that reads its port through a config module is handled correctly.

Backends must honour PORT. Rather than guessing from a static scan, Ports Manager watches whether the port is actually bound and tells you if it was not:

[Ports Manager] Backend never bound port 5001.
  It may ignore the PORT environment variable. Set PORT in the backend .env,
  or pin it with --backend-port 5001.

Configuration

Create ports-manager.config.json in the project root:

{
  "pairs": [["frontend", "backend"], ["client", "server"]],
  "portRange": [3000, 5999],
  "bannedPorts": [],
  "apiPrefix": "/api",
  "install": true,
  "cors": true,
  "proxy": null,
  "env": {
    "frontend": { "CUSTOM_API_URL": "http://127.0.0.1:{backendPort}" }
  }
}

proxy accepts true (always), false (never), or null (use it only when the frontend cannot proxy for itself). Environment values may use {frontendPort}, {backendPort}, {proxyPort}, and {apiPrefix}. CLI flags override the file.

CLI reference

--frontend-dir PATH      Override frontend/client directory
--backend-dir PATH       Override backend/server directory
--frontend-port PORT     Pin the frontend port
--backend-port PORT      Pin the backend port
--proxy-port PORT        Pin the same-origin proxy port
--range MIN-MAX          Port search range (default 3000-5999)
--ban PORTS              Never use these comma-separated ports
--proxy / --no-proxy     Force or forbid same-origin proxy mode
--api-prefix PATH        Path routed to the backend (default /api)
--no-install             Fail instead of installing missing dependencies
--no-cors                Do not inject backend CORS origin variables
--force-cors             Also preload a response-time CORS shim
--no-cors-credentials    Shim omits Access-Control-Allow-Credentials
--wait-for-backend[=MS]  Wait for the backend before starting the frontend
--config PATH            Explicit JSON config
--dry-run                Show the plan without installing or starting anything
--version, -v            Show version and author
--help, -h               Show all options

Limitations

  • Next.js has no --config flag, so its rewrites cannot be retargeted; it uses the same-origin proxy or env wiring instead.
  • CRA reads proxy from package.json, which cannot be overridden per run, so it also uses the same-origin proxy.
  • Framework and natural-port detection are heuristics; pin a port when they guess wrong.
  • Monorepos and workspace layouts beyond a single frontend/backend pair are untested.

Development

npm install
npm test