# Express

> What an Express app needs to run on the platform: the port and address, the start command, TypeScript, data and files, with a small example.

## The short list

| Your app needs | How |
| --- | --- |
| A `package.json` | At the top of the repository, or in one folder you set as the root directory |
| A start script | `"start": "node server.js"` in `package.json` |
| The right port | Listen on `process.env.PORT` |
| The right address | Listen on all interfaces: no host, or `0.0.0.0` |
| A quick start | Be listening within 2 minutes |
| No files of its own | Keep data in a database, not on the disk of the app |

[Check your app](https://arrangic.com/docs/check-your-app.md) checks all of these for you.

## A small app that runs

```js title="server.js"
const express = require('express');

const app = express();

app.get('/', (request, response) => {
	response.send('Hello');
});

// The platform sets PORT. 3000 is for your own machine.
const port = process.env.PORT || 3000;

const server = app.listen(port, '0.0.0.0', () => {
	console.log(`Listening on ${port}`);
});

// Finish open requests when the platform stops the app.
process.on('SIGTERM', () => {
	server.close(() => process.exit(0));
});
```

```json title="package.json"
{
	"name": "my-app",
	"scripts": {
		"start": "node server.js"
	},
	"dependencies": {
		"express": "^5.1.0"
	},
	"engines": {
		"node": "22"
	}
}
```

Push these two files and the lockfile (`package-lock.json`), create an app from the repository, and it runs. Nothing else to set.

## Port and address

The platform sets the `PORT` variable and sends every request to that port. It is 3000 unless you change it in the settings.

- **Read the port from `PORT`.** A port written into the code only works by accident.
- **Listen on all interfaces.** Give no host, or `0.0.0.0`. A server on `localhost` or `127.0.0.1` cannot be reached, and the deployment fails.
- **One port.** Only this port gets traffic. A second server on another port is not reachable.
- **Plain HTTP.** HTTPS is handled before a request reaches your app. Do not load certificates in the app.

## Start command

The start command is `npm run start`, so your `package.json` needs a `start` script. Without one, the `main` file is started with `node`.

Start the server with `node`. Tools that watch files and restart (nodemon, for example) are for your own machine, and they are usually in `devDependencies`, which the running app does not have.

## Starting in time

The new version has 2 minutes to start listening. If it takes longer, the deployment fails and the old version stays.

Do slow one-off work before the app starts, not inside it. Database migrations belong in [deploy commands](https://arrangic.com/docs/deploy-commands.md): they run once for every deployment, before the new version takes traffic.

## TypeScript

Build to JavaScript, then start the built file:

```json
{
	"scripts": {
		"build": "tsc",
		"start": "node dist/server.js"
	}
}
```

The build command `npm run build` is filled in when your `package.json` has a build script.

> **Build tools in devDependencies**
>
> With a build command, the packages in `devDependencies` are installed for the build and removed after it. TypeScript and bundlers can stay there. See [Build settings](https://arrangic.com/docs/build-settings.md#packages-your-build-needs).

## Stopping

To stop a replica, the platform sends `SIGTERM` and waits 30 seconds. Close the server on that signal, as in the example, so open requests can finish.

## Data and files

The disk of the app is temporary. A file written while the app runs is gone after the next deployment or restart, and it can be gone after the app has slept.

- **Database.** Use a hosted database and put its address in a variable, `DATABASE_URL` for example. SQLite in a file does not survive.
- **Uploads.** Send them to a file store outside the app, not to a folder.
- **Sessions.** `express-session` keeps sessions in memory unless you give it a store. In memory they are lost on every restart and are not shared between replicas. Use a store backed by your database.

## Timers and jobs

An idle app sleeps and wakes only for a request. A timer, a cron job or a queue worker inside the app does not run while it sleeps.

If your app does work without a visitor, keep **Keep one replica always on** switched on under **Scaling**. With more than one replica, every replica runs the job. See [Sleep and wake](https://arrangic.com/docs/sleep-and-wake.md).

## Variables

Settings and secrets come from [environment variables](https://arrangic.com/docs/environment-variables.md) you add in the dashboard. A `.env` file that is not committed does not exist on the platform.

Three variables are set for you: `PORT`, `NODE_ENV` (always `production`) and `APP_URL` (the address of your app).

---

This page on the site: https://arrangic.com/docs/frameworks/express  
All pages of the documentation: https://arrangic.com/llms.txt
