DeployxaDocs Portal
CLI v1.0.4
Deploy Express API (Node.js)
Framework guide · Express

Deploy an Express API (Node.js)

Deploy Express REST APIs and WebSocket servers as a supervised Node.js process with HTTPS in front, health-checked releases, live logs and a managed database.

Prerequisites

  • An Express 4 or 5 app with a committed lockfile.
  • A start script in package.json, for example "start": "node index.js".
  • If you write TypeScript, a build script that compiles to JavaScript (for example tsc into dist/).

How Deployxa detects Express

A package.json without a frontend framework is treated as a Node.js app on Node.js 20. Dependencies are installed from the lockfile and the start script is run.

SettingValue
Wizard presetNode.js API
Build commandnpm install (use npm run build for TypeScript)
Start commandnpm start, or set it explicitly, e.g. node index.js
Port3000 (read it from PORT)

Required config: listen on PORT and 0.0.0.0

Bind to every interface and take the port from the environment, falling back to 3000:

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

const app = express();
app.set('trust proxy', 1);           // HTTPS terminates in front of the app
app.use(cors({ origin: process.env.CORS_ORIGIN?.split(',') ?? false }));
app.use(express.json());

app.get('/health', (_req, res) => res.json({ ok: true }));
app.get('/api/hello', (_req, res) => res.json({ message: 'Hello from Deployxa' }));

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

Set the service's Internal Port to the same value (3000 by default). trust proxy makes req.secure, req.ip and secure cookies behave correctly behind the HTTPS proxy.

Note: WebSocket upgrades are passed through to your app, so ws and Socket.IO work on the same port as HTTP.

Environment variables

KeyPurposeExample
PORTPort the server listens on3000
NODE_ENVRuntime modeproduction
CORS_ORIGINComma-separated browser origins allowed to call the APIhttps://app.example.com
DATABASE_URLInjected when a database is attachedpostgresql://…

Databases

In the wizard's Configure step, set Database to PostgreSQL. The connection string is injected into the app as DATABASE_URL, so nothing needs to be committed. Choose SQLite only for small apps: it lives on a persistent volume and has no server.

js
// db.js
import pg from 'pg';
export const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL, max: 10 });

// usage
const { rows } = await pool.query('select now()');

Run migrations (Knex, Drizzle Kit, Prisma) before the server starts: for example add "start:prod": "prisma migrate deploy && node index.js" to package.json and use npm run start:prod as the start command. npm runs scripts through a shell, so && works there.

Deploy

From the dashboard

  1. 1
    Source

    Open Dashboard → Projects → New project (/dashboard/projects/new). Pick the GitHub repository and branch, or upload a .zip archive of the project.

  2. 2
    Configure

    Leave the framework preset on Auto-detect or choose Node.js API. Check the fields under advanced settings:

    • Build command: npm install
    • Root directory: ./, or the sub-folder that holds the app in a monorepo
    • Database: PostgreSQL or MySQL if needed
  3. 3
    Environment

    Add your variables in the table, or paste a whole .env file into the Bulk / .env tab.

  4. 4
    Deploy

    Start the deploy and watch the build log. When the release passes its health check it is served over HTTPS on its Deployxa URL.

  5. 5
    Confirm the start command and port

    Open the project's Environment Canvas tab and select the web service. Set Start Command to node index.js. Set Internal Port to 3000 so it matches the port your app listens on. Save and redeploy if you changed anything.

From the CLI

shell
$ npm install -g @deployxa/cli
$ deployxa login
$ cd my-app
$ deployxa link            # pick an existing project, or skip: deploy creates one
$ deployxa env set CORS_ORIGIN "https://app.example.com"
$ deployxa deploy
$ deployxa logs --live     # stream build and runtime logs
$ deployxa open            # open the live URL

If the folder is not linked, deployxa deploy creates a project named after the folder and links it (the link is saved in .deployxa/config.json). Later runs deploy the linked project. deployxa status shows the latest deployment and deployxa rollback previous returns to the release before it.

Warning: deployxa deploy packages the folder exactly as it is on disk; it does not read .gitignore. Delete local node_modules, virtualenvs, build output and real .env files first, or deploy from a clean clone. Set secrets with deployxa env set instead of shipping them in the archive.

Custom domain

Go to Dashboard → Domains, choose Connect a domain you already own and pick this project. Deployxa then shows the DNS records to create at your DNS provider:

RecordUse it forValue
AAn apex domain such as example.com85.17.244.184 (DNS only on Cloudflare)
CNAMEA subdomain such as www or appcname.deployxa.com (DNS only on Cloudflare)
TXTOnly when a CDN or proxy (for example Cloudflare orange-cloud) hides the origin recordHost _deployxa-challenge.<your-domain>, value deployxa-verification=…

DNS changes can take several minutes to propagate; use Retry verification once they have. A Let's Encrypt certificate is issued after the domain verifies. From a linked folder you can do the same with the CLI:

shell
$ deployxa domains add app.example.com
$ deployxa dns             # Waiting → Verified → SSL Active
$ deployxa domains list

Common errors and fixes

SymptomFix
Health check fails; logs show "listening on 127.0.0.1"Pass '0.0.0.0' as the host to app.listen.
Health check fails; app listens on 8080Use process.env.PORT, or set Internal Port to the port the app actually uses.
Error: Cannot find moduleMove the package from devDependencies to dependencies; for TypeScript, make sure the build emits dist/ and the start command points at it.
ERR_REQUIRE_ESM / "Cannot use import statement"Add "type": "module" to package.json or use require consistently.
Process restarts in a loopAn unhandled rejection at startup (often a bad DATABASE_URL). Check runtime logs for the first error.

Start with deployxa logs --live (or the deployment's log view in the dashboard): the build log shows dependency and compile failures, the runtime log shows crashes and bind errors. See also Troubleshooting.