Vercel can host a Node.js API, including Express, when the application fits a request-driven serverless model. It is a good match for portfolio APIs, webhooks, and small backends. It is a poor match for a worker that must run forever or an application that depends on local disk state.

Before you deploy: check the fit

Workload Vercel fit Reason
REST or JSON API Good Each request can run independently
Express portfolio backend Good Minimal setup and automatic deployments
Webhook receiver Good Short request, validation, durable write
Background queue worker Poor Workers need a continuously running process
Local file uploads Poor Function filesystems are not durable storage
Stateful WebSocket rooms Poor Presence and pub/sub need external state

If your API writes to PostgreSQL, Redis, object storage, or another managed service, that is normal. The function should be replaceable; durable state should live elsewhere.

Create the Express project

mkdir vercel-express-api
cd vercel-express-api
npm init -y
npm install express

Create api/index.js:

const express = require('express');

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

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

app.post('/api/messages', async (request, response) => {
  const message = String(request.body?.message ?? '').trim();
  if (!message) {
    return response.status(400).json({ error: 'message is required' });
  }

  return response.status(201).json({ message });
});

module.exports = app;

Vercel’s Express guide uses an /api entry point. Keep the first deployment small so routing problems are easy to isolate.

Route requests to the API

If you want every path to reach Express, add vercel.json:

{
  "version": 2,
  "rewrites": [
    { "source": "/(.*)", "destination": "/api" }
  ]
}

If the repository also contains a frontend, do not rewrite every path blindly. Route only the API prefix and let the frontend framework own page routes.

Test the Vercel-shaped app locally

Running node api/index.js does not reproduce Vercel’s routing and function environment. Use the CLI:

npx vercel login
npx vercel dev

Then test both success and failure:

curl http://localhost:3000/health

curl -i -X POST http://localhost:3000/api/messages \
  -H 'content-type: application/json' \
  -d '{}'

The second request should return 400. A deployment check that only opens the homepage misses validation and error-handling problems.

Add environment variables safely

Use project settings or the Vercel CLI for secrets such as DATABASE_URL, API keys, and JWT secrets. Keep local values in .env.local and exclude them from Git.

.env
.env.local
.vercel
node_modules

Remember that Preview and Production are separate environments. A preview deployment can work while production fails because a variable exists in only one environment.

Validate required configuration at startup instead of discovering it inside the first user request:

const required = ['DATABASE_URL', 'JWT_SECRET'];

for (const key of required) {
  if (!process.env[key]) {
    throw new Error(`Missing environment variable: ${key}`);
  }
}

Deploy a preview, then production

npx vercel

That creates a preview URL. Test /health, one database-backed route, one invalid request, and CORS from the real frontend origin.

When the preview passes:

npx vercel --prod

Connecting a Git repository gives you the same useful workflow automatically: branches and pull requests get previews, while the production branch creates production deployments.

Database connection mistakes

Serverless traffic can create several function instances, and each instance may open database connections. Reusing a client inside the module and using a serverless-friendly pool or provider helps prevent connection exhaustion.

Do not cache user-specific data in a module variable and assume it belongs to one user. A warm function instance can serve later requests.

Why an app works locally but fails on Vercel

Symptom Likely cause Check
404 on every route Entry file or rewrite mismatch Deployment output and vercel.json
Database timeout Network allowlist or too many connections Runtime logs and pool limits
CORS error Production frontend origin is missing Response headers and environment variables
Secret is undefined Variable added to the wrong environment Preview vs Production settings
Uploaded file disappears Local function storage was used Move files to object storage
Request times out Long task runs inside the request Move work to a queue or job system

Read the runtime logs for the exact production request. Repeated redeploys do not fix a routing or configuration mismatch.

When to choose another host

Choose a container, VPS, or worker platform when you need a process that stays alive, custom operating-system packages, long background tasks, or direct control over networking. Vercel can still host the frontend while the backend worker runs elsewhere.

The practical rule is simple: use Vercel when requests are independent and external services hold durable state. Choose a continuously running host when the process itself is part of the application’s state.

Official references