All posts

Deploy an Express API from Nigeria

An Express API is the smallest useful thing you can put in production, and the one most likely to be stuck on your laptop. This is the whole path: a repo, a build, a URL over HTTPS, and a bill in naira.

The three lines that decide whether it deploys

Almost every failed first deploy of a Node API comes down to the same three things.

Listen on the platform's port. The container is given a port through the PORT environment variable. Hard-coding 3000 means the platform's health check knocks on a door nobody is behind.

const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`API listening on ${port}`));

Bind to every interface, not just localhost. localhost inside a container means "inside this container", which is not where traffic arrives from.

app.listen(port, '0.0.0.0');

Have a real start script. nodemon is a development tool; production runs node.

{
  "scripts": {
    "start": "node src/index.js",
    "dev": "nodemon src/index.js"
  }
}

A minimal API that deploys cleanly

// src/index.js
import express from 'express';

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

app.get('/health', (_req, res) => res.json({ ok: true }));

app.get('/api/quotes', (_req, res) => {
  res.json([{ id: 1, text: 'Ship it.' }]);
});

const port = process.env.PORT || 3000;
app.listen(port, '0.0.0.0', () => console.log(`API listening on ${port}`));

A /health route costs you nothing and makes every future "is it up?" question answerable with one request.

Push and connect

git init
git add .
git commit -m "Express API"
git branch -M main
git remote add origin [email protected]:you/quotes-api.git
git push -u origin main

Then add the app from the repository: on Apps, press Add app and pick it GitHub account. The console says what it detected before the first deploy, and the build command is under Build settings — deploying from GitHub walks through the connection, and builds and frameworks covers overriding what was detected. Every push to the connected branch ships a new version.

Secrets

Never commit a database URL or an API key. Set them as environment variables and read them at runtime:

const databaseUrl = process.env.DATABASE_URL;
if (!databaseUrl) throw new Error('DATABASE_URL is not set');

Environment variables and secrets explains which values are available at build time versus runtime — a difference that catches people out exactly once, painfully.

If your API needs a database, managed PostgreSQL, MySQL, MariaDB and MongoDB are available on paid plans and hand you a connection string to drop straight into DATABASE_URL. See managed databases.

What the free plan really gives you

₦0, no card, 5 apps, 1 GB of memory, 5 GB of storage, automatic HTTPS, custom domains, live logs and metrics.

The honest catch: on the Free plan an app scales to zero when it is idle. The first request after a quiet period waits for the container to start. For a personal project, a demo or an API your mobile app calls occasionally, that is fine. For something a paying customer hits, the ₦30,000 Hobby plan keeps the app warm — and adds the managed databases.

Two other limits to know up front: OneBit Deploy runs in a single region, and it builds Node.js, Next.js and Python (early access) apps and static sites; Go and PHP are not available yet. The rest are in limits.

Put it on your own domain

An API on a subdomain you own outlives any platform. Point a record at the app and the certificate is issued automatically — see custom domains and SSL.


Ready to ship? Follow the quickstart or read another post.