Skip to main content



To see the debug logs run:

DEBUG=express:* node main


import path from 'path'

const app = express()

// Optional - we can instead do app.listen(3000).
// Then we can do app.get("port").
app.set('port', process.env.PORT || 3000)

// Parse JSON body in (eg POST, PUT and PATCH) requests with 'Content-Type: application/json',
// and make it available in req.body as JS object.

// Parse URL encoded body in a <form> submission (Content-Type: application/x-www-form-urlencoded),
// and make it available in req.body as JS object.
app.use(express.urlencoded({ extended: true }))

app.use(express.static(path.join(__dirname, '../public')))
express.static(path.join(__dirname, '../public'), { maxAge: 31557600000 })

app.listen(app.get('port'), () => {
'Server is running on port %d in %s mode',




Why doesn't adding CORS headers to an OPTIONS route allow browsers to access my API? -


app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'http://localhost:3000')
res.header('Access-Control-Allow-Headers', 'Content-Type')
res.header('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE')

Using the cors package

If we don't pass any options:

import cors from 'cors'

It uses the default configuration which returns:

  • Access-Control-Allow-Origin: *
  • Access-Control-Allow-Methods: GET,HEAD,PUT,PATCH,POST,DELETE
  • It also sets Access-Control-Allow-Headers: content-type if the request has Access-Control-Request-Headers: content-type.

So with no options it accepts all origins! However we can specify the allowed origins:

origin: [''],

We can also use a callback for origin - see

Error handling

Custom error handling middleware

The 4 arguments must be provided

"Error-handling middleware always takes four arguments. You must provide four arguments to identify it as an error-handling middleware function. Even if you don’t need to use the next object, you must specify it to maintain the signature. Otherwise, the next object will be interpreted as regular middleware and will fail to handle errors." source

import { ErrorRequestHandler } from 'express'

* Important: this error handler does not get invoked if the error is thrown in
* an `async` function or callback unless you wrap the code with a try-catch
* and call next(error).
// Important: 'next' argument must be provided, even if unused! See why at
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const errorHandler: ErrorRequestHandler = (err, req, res, next) => {
console.error('Error handler', err)


In Express 4 error handlers are only called from handlers that are synchronous (ie not async functions nor callbacks). In Express 5 is OK.

// Synchronous -> the error handler works :)
// The server does not crash. There's a response sent to the client (500 Internal Server Error).
router.get('/sync', (req, res) => {
const a = {}
// @ts-ignore
a.thiswill.createacrash // TypeError: Cannot read properties of undefined (reading 'createacrash')

// Asynchronous -> the error handler does NOT work :/
// The server crashes and it stops. There's no response sent to the client.
router.get('/async', async (req, res) => {
const a = {}
// @ts-ignore
router.get('/callback', (req, res) => {
setTimeout(() => {
const a = {}
// @ts-ignore
}, 100)

In asynchronous handlers we must catch the errors in a try-catch and then call next(error) if we want our error handling middleware to be called.

Alternatively we can use


New project setup with TypeScript

git init
touch .gitignore # Add node_modules
npm init # Creates packages.json. The "main" script (entry point) should be build/index.js
npm i [-E] express
npm i -D [-E] typescript @types/express ts-node-dev
npx tsc --init # Creates tsconfig.json

Tweak tsconfig.json:

"compilerOptions": {
"outDir": "./build",
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true

Add scripts to package.json:

"main": "build/index.js",
"scripts": {
"start": "node build/index.js",
"dev": "ts-node-dev src/index.ts",
"build": "npx tsc"

See this to setup ESLint.

How to Setup Node.js with TypeScript in 2023 -

TypeScript project setup examples

Extending the Request type to add fields to it

Extend Express Request object using Typescript -

Add the following to any .ts file:

declare module 'express-serve-static-core' {
interface Request {
user?: User

Alternatively, we can also create a file types/express/index.d.ts with the same content, and then set "typeRoots": ["./types"] at tsconfig.json.

Typing the params, request and response -

const updateUserEmail: RequestHandler<
{ userId: string }, // Params
{ user: User } | { error: string }, // Response Body
{ email: string } // Request Body
> = async (req, res) => {}

Service layer



Note that there's no performance difference between the sync and async versions of jwt.sign and jwt.verify according to

Curso de Backend con Node.js: Autenticación con Passport.js y JWT - -