# How to deploy an Express API with a database.

URL: https://antideploy.com/blog/how-to-deploy-an-express-api-with-a-database

An Express API deploys with one sentence. It needs a start script, it must read PORT, and it can use the Postgres database Antideploy creates.

## At a glance

- **npm run start**: Needs a start script in package.json
- **PORT**: Read it and listen on 0.0.0.0
- **DATABASE_URL**: Set for you when you need Postgres
- **Migrations run**: Prisma and Drizzle commands are detected

Published 7 October 2026. Facts checked 7 October 2026. 3 min read.

## The short answer

To deploy an Express API, give your coding agent the sentence below. Antideploy finds Express in your `package.json`, runs `npm run start`, and sends traffic to the port in the `PORT` variable. If your code needs Postgres, it creates a database and sets `DATABASE_URL`. You need three things in your code: a `start` script, a server that reads `PORT`, and a database connection that reads `DATABASE_URL`.

> **Say this to your coding agent**
>
> Set this project up to deploy on Antideploy. Fetch https://antideploy.com/agent.md and follow it.

## What Antideploy detects

- **Express.** Detected from the `express` dependency. Fastify, Koa, NestJS and hapi are detected the same way.
- **The start command.** A `Procfile` `web:` line wins. Otherwise Antideploy runs your `start` script with `npm run start`. Without either, it cannot tell how to run the app and says so.
- **The port.** It starts your app with `PORT` set. Your code must use it.
- **The versions.** The Node.js version comes from `engines.node` in `package.json`, a `.nvmrc` or a `.node-version` file.
- **The database.** A `pg`, `postgres`, `@neondatabase/serverless` or `@vercel/postgres` dependency, or Prisma or Drizzle, means Postgres.

## What your code needs

**server.js**

```
import express from "express";
import pg from "pg";

const app = express();
const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL });

app.get("/health", (req, res) => res.send("ok"));

const port = process.env.PORT || 3000;
app.listen(port, "0.0.0.0");
```

**package.json**

```
{
  "scripts": { "start": "node server.js" }
}
```

If you write TypeScript, add a `build` script that compiles it and point `start` at the compiled file, for example `node dist/server.js`. A start script that needs a tool kept only in `devDependencies`, such as `tsx` or `ts-node`, can fail, because development dependencies are removed from the image after the build.

## Step by step

### 1. Check the three things

Your project has a `start` script, the server reads `PORT`, and the database is read from `DATABASE_URL`.

### 2. Paste the sentence and approve one link

Your agent creates the app, and a Postgres database if your code needs one, and writes the connection details into your `.env` without printing them. The same database serves local testing and the live app.

### 3. Deploy and open the link

Antideploy installs with `npm ci` when there is a `package-lock.json`, runs your build, runs your migration command if it found one, starts the app, checks that it answers and opens it.

## Migrations

If you use an ORM with migrations, Antideploy detects the command and runs it before the new version goes live. A failed migration fails the deploy, so a broken schema never goes live.

- Prisma runs `npx prisma migrate deploy` when you have a migrations folder, and `npx prisma db push` when you do not
- Drizzle runs `npx drizzle-kit migrate`
- If a migration fails because `prisma` or `drizzle-kit` is not found, move it into `dependencies` in `package.json`

## Common problems

- **The app never starts listening.** The server ignores `PORT`, or it listens on `127.0.0.1`. See [the full fix](https://antideploy.com/blog/your-app-built-successfully-but-never-started-listening-on-its-port).
- **The lock file is out of date.** See [npm ci can only install packages when package.json and package-lock.json are in sync](https://antideploy.com/blog/npm-ci-can-only-install-packages-when-package-json-and-package-lock-json-are-in-sync).
- **Data disappears after a deploy.** You used SQLite or saved uploads to disk. Use Postgres and a bucket instead.
- **A timer never fires.** A scheduler inside the app, such as `node-cron`, cannot run while the app sleeps. Use a [scheduled job](https://antideploy.com/blog/how-to-run-a-scheduled-job-on-your-app).

## Here's where it stops.

A platform that only tells you what it is good at is one you find the edges of in production. These are the ones to know before your first deploy.

### Apps sleep when idle

On every plan, an app nobody is using goes to sleep and the next visit wakes it. The first request took 2.9 to 13.9 seconds in our measurements, depending on the stack. There are no always-on workers, so use a cron job or a webhook for background work.

### One instance, no previews

Each app is one instance with 1 shared vCPU and 1 GB of memory, and a Java app gets 1 dedicated vCPU and 2 GB. There are no preview deployments and no per-branch URLs.

### The disk forgets

Anything written to local disk is gone on the next deploy. Apps that speak S3 get a bucket instead.

### No background workers

Only the web process runs. A queue consumer or a loop inside the app will not stay alive. Use a cron job to call a path on a schedule, or a webhook.

## Express questions, answered.

Anything else? Write to us and a person answers.

[support@antideploy.com](mailto:support@antideploy.com)

### Do I need a Dockerfile for Express?

No. Antideploy builds it from your project. If you add a Dockerfile at the root, it is used instead.

### Can I use MongoDB or MySQL with Express?

Antideploy creates Postgres only. You can use another database from another provider and set its connection string as a variable.

### Does Express work with WebSockets?

Yes. Each app is one long-running process, and Antideploy sets no time limit of its own on a connection.

### What does it cost?

An Express API with a server counts as one live app. The Free plan includes 1 live app with a server and 1 Postgres database.

## More guides, and the platform.

Each platform page covers one part of Antideploy: what it does, where it stops and the questions people ask. [See the whole platform](https://antideploy.com/platform), or [all the guides](https://antideploy.com/blog).

- [How to add a Postgres database to your app](https://antideploy.com/blog/how-to-add-a-postgres-database-to-your-app): Ask your agent. A Postgres 17 database is created beside your app and DATABASE_URL is set.
- [Your app built successfully but never started listening on its port](https://antideploy.com/blog/your-app-built-successfully-but-never-started-listening-on-its-port): The app built but never answered on its port. Read PORT, listen on 0.0.0.0 and check the logs.
- [How to deploy a full-stack app](https://antideploy.com/blog/how-to-deploy-a-full-stack-app): A frontend, an API and a database. One app or two, and what to do about each piece.

- [Hosting](https://antideploy.com/platform/hosting)
- [Database](https://antideploy.com/platform/database)
- [Environment variables](https://antideploy.com/platform/environment-variables)
- [Deployments](https://antideploy.com/platform/deployments)

Facts on this page were checked against the live platform on 7 October 2026.

## Deploy something. Start with one sentence.

Paste one sentence into your coding agent, click Approve once, and get a live link. No card, no trial clock.

To set this up, give your coding agent this sentence:

```text
Set this project up to deploy on Antideploy. Fetch https://antideploy.com/agent.md and follow it.
```
