How to deploy Node.js app to Vercel
Deploying Node.js applications to Vercel provides serverless function deployment with automatic builds, edge network, and Git integration. With over 12 years of Node.js experience since 2014 and as the creator of CoreUI, I’ve deployed numerous Node.js APIs to Vercel. Vercel excels at serverless Node.js functions with automatic scaling, zero configuration deployments, and global edge network for low latency. This approach works perfectly for API routes, webhooks, and serverless backends with instant deployments on every push.
Use Vercel CLI or Git integration to deploy Node.js serverless functions with automatic HTTPS and environment configuration.
Install Vercel CLI:
# Install globally
npm install -g vercel
# Login
vercel login
# Deploy current directory
vercel
# Deploy to production
vercel --prod
Basic serverless function:
// api/hello.js
export default function handler(req, res) {
res.status(200).json({
message: 'Hello from Vercel!',
timestamp: new Date().toISOString()
})
}
API with Express-like routing:
// api/users.js
export default async function handler(req, res) {
const { method, query } = req
switch (method) {
case 'GET':
// Get user by ID
const userId = query.id
return res.status(200).json({
id: userId,
name: 'John Doe',
email: '[email protected]'
})
case 'POST':
// Create user
const body = req.body
return res.status(201).json({
success: true,
user: body
})
default:
res.setHeader('Allow', ['GET', 'POST'])
return res.status(405).end(`Method ${method} Not Allowed`)
}
}
Environment variables:
// api/config.js
export default function handler(req, res) {
// Access environment variables
const dbUrl = process.env.DATABASE_URL
const apiKey = process.env.API_KEY
res.status(200).json({
environment: process.env.VERCEL_ENV, // production, preview, or development
region: process.env.VERCEL_REGION
})
}
// Set via Vercel CLI
// vercel env add DATABASE_URL
// Or in vercel.json
vercel.json configuration:
{
"version": 2,
"builds": [
{
"src": "api/**/*.js",
"use": "@vercel/node"
}
],
"routes": [
{
"src": "/api/users/(.*)",
"dest": "/api/users.js"
},
{
"src": "/api/(.*)",
"dest": "/api/$1.js"
}
],
"env": {
"NODE_ENV": "production"
},
"regions": ["iad1"]
}
Database integration:
// api/db/users.js
import { Pool } from 'pg'
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
ssl: {
rejectUnauthorized: false
}
})
export default async function handler(req, res) {
try {
const { rows } = await pool.query('SELECT * FROM users LIMIT 10')
res.status(200).json({
users: rows,
count: rows.length
})
} catch (error) {
console.error('Database error:', error)
res.status(500).json({
error: 'Database query failed'
})
}
}
Middleware pattern:
// lib/middleware.js
export function withAuth(handler) {
return async (req, res) => {
const token = req.headers.authorization?.replace('Bearer ', '')
if (!token) {
return res.status(401).json({ error: 'Unauthorized' })
}
try {
// Verify token
const user = verifyToken(token)
req.user = user
return handler(req, res)
} catch (error) {
return res.status(401).json({ error: 'Invalid token' })
}
}
}
export function withCors(handler) {
return async (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*')
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE')
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization')
if (req.method === 'OPTIONS') {
return res.status(200).end()
}
return handler(req, res)
}
}
// api/protected.js
import { withAuth } from '../lib/middleware'
async function handler(req, res) {
res.status(200).json({
message: 'Protected data',
user: req.user
})
}
export default withAuth(handler)
Edge functions:
// api/edge-hello.js
export const config = {
runtime: 'edge'
}
export default function handler(req) {
return new Response(
JSON.stringify({
message: 'Hello from Edge!',
location: req.geo.city
}),
{
status: 200,
headers: {
'content-type': 'application/json'
}
}
)
}
Caching:
// api/cached.js
export default function handler(req, res) {
// Cache for 60 seconds
res.setHeader('Cache-Control', 's-maxage=60, stale-while-revalidate')
res.status(200).json({
data: 'This response is cached',
timestamp: new Date().toISOString()
})
}
GitHub integration:
# Connect repository via Vercel Dashboard
# or use CLI
vercel link
# Automatic deployments on:
# - Push to main branch (production)
# - Pull requests (preview deployments)
# View deployments
vercel ls
# Alias deployment to custom domain
vercel alias set deployment-url.vercel.app custom-domain.com
Package.json configuration:
{
"name": "nodejs-vercel-api",
"version": "1.0.0",
"scripts": {
"dev": "vercel dev",
"deploy": "vercel --prod"
},
"dependencies": {
"pg": "^8.11.3"
},
"devDependencies": {
"vercel": "^33.0.1"
}
}
Best Practice Note
Use serverless functions in api/ directory for automatic routing. Keep functions lightweight and stateless. Use environment variables for secrets. Enable caching with Cache-Control headers. Use Edge Functions for low-latency global endpoints. Connect GitHub for automatic preview deployments on pull requests. Use vercel dev for local development matching production environment. Implement middleware for auth and CORS. This is how we deploy CoreUI Node.js APIs to Vercel—serverless functions with automatic scaling, global edge network, and instant deployments with zero configuration.



