---
title: "Deploy Express API | TypeScript, Rate Limiting and Security Headers"
description: "Express 5 and TypeScript, proxy-aware rate limiting and security headers"
category: "Starters"
url: https://railway.com/deploy/express-api-or-typescript-rate-limiting-
---

# Deploy Express API | TypeScript, Rate Limiting and Security Headers

Express 5 and TypeScript, proxy-aware rate limiting and security headers

**[Deploy Express API | TypeScript, Rate Limiting and Security Headers on Railway](https://railway.com/template/express-api-or-typescript-rate-limiting-)**

- **Creator:** Pavel Volkov's Projects
- **Category:** Starters
- **Total deploys:** 1

## Template content

### API

- **Source:** https://github.com/ak40u/express-api-railway-starter
- **Public domain:** Yes

## Documentation

# Deploy and Host an Express API on Railway

Express 5 on TypeScript, with the middleware an internet-facing API actually needs.

## About Hosting Express

The ExpressJS template on Railway deploys a repository whose package.json is a 2018 express-generator skeleton: express ~4.16.1, pug 2.0.0-beta11, debug ~2.6.9, http-errors ~1.6.3. Express 4.16 shipped in 2017 and that pug is a beta from the same year. Around four deployments in ten do not come up, and the ones that do run a stack nobody has patched in seven years.

This starter is Express 5 on current Node, with a committed lockfile that passes `npm audit --audit-level=high` - which matters here, because Railway refuses to build when the lockfile carries a HIGH advisory.

## Common Use Cases

- A JSON API for a front end you host separately
- A webhook receiver that has to be reachable and rate-limited
- A starting point that already handles proxies, CORS and shutdown correctly

## Dependencies for Express Hosting

- Nothing. One service, no database. Add Prisma or Mongoose and a database service when you need one.

### Deployment Dependencies

- Express 5: https://expressjs.com
- Source: https://github.com/ak40u/express-api-railway-starter

### Implementation Details

| Method | Path | Does |
|---|---|---|
| GET | / | Lists the endpoints |
| GET | /health | Status and uptime - deliberately outside the rate limiter |
| GET | /api/time | Server time |
| POST | /api/echo | Echoes {"message": "..."}, 400 when missing |

Four decisions, each of which is a thing that bites in production rather than locally:

- **app.set("trust proxy", 1).** TLS terminates at the platform proxy and the real client address arrives in X-Forwarded-For. Without this every request appears to come from the proxy, which breaks rate limiting and any per-client logic. The value 1 trusts exactly one hop; trusting all of them would let a client spoof its own address.
- **Rate limiting covers /api, not everything.** A burst of traffic should not lock out the health check and cause the platform to replace a container that is working fine.
- **helmet() is on.** The security headers a browser-facing API is expected to send, with no reason to omit them.
- **Graceful shutdown.** SIGTERM arrives before a container is replaced; without a handler, in-flight requests are cut off on every deploy.

## Why Deploy Express on Railway?

Push TypeScript, get a running API on a domain with TLS and a health check - and a middleware stack that behaves correctly behind a proxy, which is the part most starters get wrong.

## Similar templates

- [open-excalidraw](https://railway.com/deploy/open-excalidraw) — Self-hostable collaborative drawing built on Excalidraw
- [caring-vibrancy](https://railway.com/deploy/caring-vibrancy) — Deploy and Host caring-vibrancy with Railway
- [Appsmith](https://railway.com/deploy/appsmith-1) — Low-code platform for internal tools, dashboards, and admin panels.

Open this page in a browser: https://railway.com/deploy/express-api-or-typescript-rate-limiting-
