Skip to main content
Back to module
Back-End Web APIs · Testing and Shipping

Deploying an API

Reading38mLesson 3 of 3 in this module

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

OptionExamplesYou manageGood for
Platform as a service (PaaS)Render, Railway, Fly.io, HerokuYour code and settingsA long-running Express server
Serverless functionsVercel Functions, Netlify Functions, AWS Lambda, Cloudflare WorkersYour handler codeSpiky or low traffic; pay per request
ContainersDocker on Google Cloud Run, AWS ECS, Azure Container AppsA container imagePortable deployments, any runtime
Virtual machineA VPS from any cloudEverything: OS, updates, firewallFull 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 /healthz returning 200, 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 expects process.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: PORT from 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.

Also in this lesson: