Ship a Node API to a free hosting tier with environment-based config, a health check and no committed secrets.
Why it matters
An API on localhost only helps you. To be used by a real web page or mobile app it has to run on a server with a public HTTPS address, read its configuration from the environment, and keep running when you close your laptop. Deploying is also where a lot of "it works on my machine" bugs show up: a missing environment variable, a hard-coded port, a database that only exists locally. Shipping one small API to a free tier teaches you the whole path.
Where an API can run
| Option | Examples | You manage | Good for |
|---|---|---|---|
| Platform as a service (PaaS) | Render, Railway, Fly.io, Heroku | Your code and settings | A long-running Express server |
| Serverless functions | Vercel Functions, Netlify Functions, AWS Lambda, Cloudflare Workers | Your handler code | Spiky or low traffic; pay per request |
| Containers | Docker on Google Cloud Run, AWS ECS, Azure Container Apps | A container image | Portable deployments, any runtime |
| Virtual machine | A VPS from any cloud | Everything: OS, updates, firewall | Full control, more work |
Free tiers change often, and some need a credit card even when they cost nothing. Read the provider's current pricing page before you sign up, and set a budget alert if the option exists.
Serverless deserves a note. Your code runs only when a request arrives, and the platform scales it for you. In exchange: the first request after idle time may be slower (a "cold start"), the local filesystem is temporary, and work should finish before the response is sent (or use the platform's "wait until" helper). Many platforms can run an Express app as a function with a small adapter.
Make the app deployable
Whatever the host, a deployable Node API follows the same rules:
// Read the port the platform gives you; fall back for local runs.
const port = Number(process.env.PORT ?? 3000);
app.listen(port, () => console.log(`Listening on ${port}`));
- Configuration from environment variables:
PORT,DATABASE_URL,JWT_SECRET,ALLOWED_ORIGINS. Nothing secret in the repository. - A start script in
package.json("start": "node index.js") and the Node version in"engines", so the platform uses the same version you tested. - A lock file, so the build installs exactly what you tested (
npm ci). - A health check route, such as
GET /healthzreturning200, that the platform and your monitoring can call. - Persistent data elsewhere. On most platforms the disk is wiped on each deploy, so a SQLite file next to your code will not survive. Use a hosted database (many have free tiers) and pass its URL as an environment variable.
Worked example: the deploy loop
A typical Git-connected deploy on a PaaS looks like this:
1. Push the repo to GitHub (without .env or node_modules).
2. In the provider's dashboard: New web service -> pick the repo.
3. Build command: npm ci
Start command: npm start
4. Add environment variables in the dashboard (never in code).
5. Deploy. The platform builds, starts the app, and gives you
https://your-app.example-host.com
6. Check it: curl -i https://your-app.example-host.com/healthz
From now on, every push to the deploy branch triggers a new build. Many platforms also build preview deployments for pull requests, so you can test a change on a real URL before merging.
After it is live
Shipping is the beginning, not the end:
- CORS: if a browser front end on another domain calls the API, allow that exact origin. Avoid
*for anything that uses cookies or credentials. - HTTPS only: platforms give you a certificate automatically. Set cookies with
Secure. - Logs: learn where your platform shows logs, and log errors without passwords, tokens or personal data.
- Rollbacks: know how to redeploy the previous version in one click when a release goes wrong.
- Rotate secrets if one is ever exposed, and redeploy.
- Clean up demo services you no longer need, so nothing keeps running (or billing) quietly.
Common mistakes
- Hard-coding
app.listen(3000)when the platform expectsprocess.env.PORT. - Committing
.env, or pasting a secret into code "just for now". - Storing data on the local disk of a serverless function or a free web service.
- Forgetting to set an environment variable in the dashboard, so the app crashes on start.
- Leaving test deployments and databases running long after the demo.
Quick recap
- Choose a host type: PaaS for a long-running server, serverless for per-request functions, containers for portability.
- Make the app deployable:
PORTfrom the environment, start script, lock file, health check, external database. - Configure secrets in the platform, never in the repository.
- After launch, check CORS, logs and rollbacks, and clean up what you do not use.
Try it: the Do tab walks you through deploying a tiny API to a free tier and tearing it down afterwards.