Learning objectives
After reading this article you will be able to:
- Prepare an API to run outside your computer
- Deploy a Node.js or Python API and test it
- Diagnose the most common errors in a newly published API
An API that works at localhost:3000 is not a published API yet. For an app, a website or another server to call it, the process has to run all the time, at an address reachable from the internet and, ideally, over HTTPS.
This guide takes a simple API from your computer to production, in Node.js and in Python. The ideas apply to any language: listen on the right interface, read the port from the environment, keep secrets out of the code and expose a route that proves the service is alive.
What do you need before you start?#
- An API that already runs locally and answers at least one route.
- A dependency file (
package.jsonorrequirements.txt). - A place to run it: a VPS, your own server or a hosting platform. The choice changes who maintains the operating system, not what the API has to do.
- Secrets (keys, database URL) kept out of the code, in environment variables. See what environment variables are.
How do you prepare the API for production?#
Three changes fix most deployment problems. The Twelve-Factor App methodology describes the first and the third (port and configuration):
- Port from the environment. The environment knows which port is free, not your code. Read
process.env.PORT(oros.environ) and keep a default only for development. - Listen on
0.0.0.0. On127.0.0.1(localhost), the API accepts connections only from the same machine. On a server or in a container, that makes it look "down". - Configuration outside the code. The database URL, keys and flags live in environment variables, never in the repository.
Also add a health route, such as GET /health, that returns 200 without touching the database. It tells you, an external monitor or a load balancer whether the process responds.
How do you run an API in Node.js?#
With a recent Node.js version installed, create the project and install Express 5:
npm init -y
npm install express@5index.js:
const express = require("express");
const app = express();
const port = process.env.PORT || 3000;
app.use(express.json());
app.get("/health", (req, res) => {
res.json({ status: "ok" });
});
app.get("/users/:id", async (req, res) => {
// In Express 5, an error thrown here reaches the error handler by itself
res.json({ id: req.params.id, name: "Ada" });
});
// Error handler: always last
app.use((err, req, res, next) => {
console.error(err);
res.status(500).json({ error: "internal_error" });
});
app.listen(port, "0.0.0.0", (error) => {
if (error) throw error; // in Express 5, errors like EADDRINUSE arrive here
console.log(`API listening on port ${port}`);
});Two differences between Express 5 and 4: async handlers that reject are forwarded to the error handler without .catch(next), and the app.listen callback receives the error as its first argument.
Run it with PORT=3000 node index.js.
How do you run an API in Python?#
With FastAPI, install the version with the standard extras (it includes Uvicorn):
pip install "fastapi[standard]"main.py:
from fastapi import FastAPI
app = FastAPI()
@app.get("/health")
def health():
return {"status": "ok"}
@app.get("/users/{user_id}")
def get_user(user_id: int):
return {"id": user_id, "name": "Ada"}In production, run it without --reload: the FastAPI docs keep that option for development because it uses more resources and is less stable.
uvicorn main:app --host 0.0.0.0 --port "${PORT:-8000}"main:app means "the app object in the file main.py". fastapi[standard] also installs the fastapi command, which the docs recommend: fastapi run main.py --port "${PORT:-8000}" already listens on 0.0.0.0. To run more than one process, see the Uvicorn workers page in the FastAPI documentation.
How do you know the API is up?#
Test from outside the server, not from inside it:
curl -i https://api.yourdomain.com/healthYou expect status 200 (the first line reads HTTP/2 200 or HTTP/1.1 200 OK, depending on the server) and the JSON {"status":"ok"}. For a POST with a body:
curl -i -X POST https://api.yourdomain.com/items \
-H "Content-Type: application/json" \
-d '{"name":"test"}'If it works only on your computer, the problem sits between the client and the process: network, port, DNS or HTTPS. To use a domain, see how to point a domain to your app. The cycle of shipping code and starting the process is covered in what deploy is.
What are the most common errors?#
The connection is refused or times out. Almost always the API listens on 127.0.0.1 or on a port different from the exposed one. Check the host (0.0.0.0) and the port read from the environment.
The port is already in use. Node prints:
Error: listen EADDRINUSE: address already in use 0.0.0.0:3000Another process already uses the port. Stop it or change PORT.
The API crashes right after starting. Read the logs: usually the server is missing a dependency or an environment variable (an undefined that became the database URL).
The browser blocks the call with a CORS error. The server answered, but without the headers that authorize your site's origin. Configure CORS in the API (the cors package in Express, CORSMiddleware in FastAPI) with exact origins instead of allowing everything.
It works over HTTP and fails over HTTPS. A page served over HTTPS cannot call an HTTP API (mixed content). Publish the API behind HTTPS.
The API is slow or restarts by itself. It may be running out of memory. Measure real usage instead of guessing; see how to size RAM and CPU.
What else matters for a public API?#
- Authentication: require a key or token on routes that change data.
- Rate limiting: without it, one buggy client can take the service down.
- Logs: record method, route and status, never passwords or tokens.
- Versioning: prefix routes with
/v1so you can evolve without breaking clients.
How to do this on Square Cloud#
The three changes in this guide are what Square Cloud expects from an API: the platform sets the PORT variable, the API listens on 0.0.0.0 at that port, and traffic reaches it over HTTPS. You upload the project as a ZIP in the dashboard, with the CLI, through GitHub or with the VS Code extension, and the deploy form detects the main file. The squarecloud.app file is optional: the dashboard generates it on deploy.
The Express example starts straight from index.js. FastAPI needs a start command, because running main.py only creates the app object and never opens a server. In the deploy form's start command field, use uvicorn main:app --host 0.0.0.0 --port $PORT.
Every plan, starting with Hobby (R$12.49 per month), gives a web application a *.squareweb.app subdomain with automatic TLS, so the health route answers right after the deploy: curl -i https://my-api.squareweb.app/health. If the API crashes on startup, the real-time logs show the npm install or pip install -r requirements.txt output and the error. Environment variables live in the dashboard and apply on the next restart, with no rebuild. Automatic restart is on by default for dashboard deploys and brings back a process that exited with an error; running out of memory, by contrast, stops the app until you raise its RAM and start it again.
A custom domain and database hosting start at Standard. The domain stays at your registrar, where you create the DNS records the dashboard shows. There is no autoscaling: to grow, you raise the RAM or split the domain across several applications with load balancing. See runtimes, limits and plans on API hosting and compare prices on pricing.
API hosting
APIs in always-on containers, with no cold starts
FAQs
Do I need a domain to host an API?
Not for it to work: an IP or a subdomain from the host already answers requests. For real use, your own domain lets you change servers later without breaking the clients that call the API.
Can I host an API on my own computer?
You can test that way, but it is not production: the computer turns off, the IP changes and home networks usually block incoming connections. For an API other people use, pick an always-on machine or platform.
Why does my API work locally but not respond after deploy?
The usual causes are listening only on localhost instead of 0.0.0.0, hardcoding a port different from the one expected, and forgetting environment variables or dependencies on the server.
What is the difference between hosting an API and a website?
A website serves pages; an API serves data (usually JSON) to other programs. Both are HTTP servers, but an API needs extra care with CORS, authentication and usage limits.
More in APIs
See all 15 articles- How to host a Node.js appTo host a Node.js app, prepare package.json, read the port and secrets from the environment, pick an LTS version and run it with node. Step-by-step guide.
- How to host a FastAPI, Flask or Django appTo host a FastAPI, Flask or Django app, run it with a production server (Uvicorn or Gunicorn) on 0.0.0.0. Commands, Django settings and common errors included.
- How to host a Python app or scriptTo host a Python app or script, pin the version, isolate dependencies in a venv, read secrets from the environment and handle SIGTERM. Step-by-step guide.
- What is a cron job? Syntax and examplesA cron job is a task that runs on its own at times set by a five-field expression. Learn the syntax, examples, common mistakes and how to schedule jobs in Node.js.