All free skills / Other majors
express 5.x
Use Express 5.x programmatically on Node.js 18+; covers the Express app, routers, body middleware, Express 5 routing, and migration differences.
- For
express@5.2.1- Verified
- 2026-09-08, 6 examples executed
- Skill package
@modootoday/pmcp-express@5.0.0
Use this skill for free
No PMCP login or subscription is required. Download the original instructions, or install the content-only archive with your project's package manager.
Download SKILL.md · Download package archive
npm install --save-dev https://pmcp.build/skills/express/5/package.tgz
PMCP discovers the installed package's instructions. Connect your runtime after installation.
Archive integrity
sha512-gvLNniiHTqI2GLsWWhHXS/DuAESuvdcx2Iq1sk3peoN+MZVHiTgkHmKBH8ufP2R5YPocmfRPo5MbgG/eJN76mA==Complete instructions
Verified against express@5.2.1 on 2026-09-08. 6 of 6 examples executed.
Express 5
Express ^5.0.0 requires Node.js 18 or newer. Import the package and call the exported function to create an application:
import express from 'express'
import assert from 'node:assert/strict'
const app = express()
assert.equal(typeof express, 'function')
assert.equal(typeof app, 'function')
assert.equal(typeof app.get, 'function')
assert.equal(typeof app.post, 'function')
assert.equal(typeof app.put, 'function')
assert.equal(typeof app.delete, 'function')
assert.equal(typeof app.patch, 'function')
assert.equal(typeof app.options, 'function')
assert.equal(typeof app.head, 'function')
assert.equal(typeof app.all, 'function')
assert.equal(typeof app.use, 'function')
assert.equal(typeof app.listen, 'function')
The application is itself a request handler and can be passed to Node's HTTP server APIs. app.listen(...) returns an http.Server. A plain script does not need an Express-specific runner or build step. These examples do not start a server because they must run without network access.
Middleware factories
The documented top-level middleware factories return middleware functions:
express.json(options)parses JSON bodies.express.raw(options)parses request payloads into aBuffer.express.text(options)parses text bodies.express.urlencoded(options)parses URL-encoded bodies.express.static(root, options)serves static files.
import express from 'express'
import assert from 'node:assert/strict'
assert.equal(typeof express.json(), 'function')
assert.equal(typeof express.json({}), 'function')
assert.equal(typeof express.raw(), 'function')
assert.equal(typeof express.text(), 'function')
assert.equal(typeof express.urlencoded({ extended: true }), 'function')
assert.equal(typeof express.static('public'), 'function')
In Express 5, express.urlencoded() defaults extended to false; specify { extended: true } when that is required. If no body parser handles the request, req.body is undefined. express.raw() produces a Buffer.
import express from 'express'
import assert from 'node:assert/strict'
import { PassThrough } from 'node:stream'
const request = new PassThrough()
request.headers = {
'content-type': 'application/json',
'content-length': '11'
}
request.method = 'POST'
request.url = '/profile'
const response = {
setHeader() {},
getHeader() { return undefined },
removeHeader() {},
end() {}
}
let parsed: unknown
express.json()(request, response, (error?: unknown) => {
assert.equal(error, undefined)
parsed = request.body
})
request.end('{"ok":true}')
await new Promise((resolve) => setImmediate(resolve))
assert.deepEqual(parsed, { ok: true })
Routers
express.Router(options) returns a router. The documented options are caseSensitive, mergeParams, and strict. Mount it with app.use():
import express from 'express'
import assert from 'node:assert/strict'
const app = express()
const router = express.Router({
caseSensitive: true,
mergeParams: true,
strict: true
})
router.get('/', (_req, res) => res.send('hello world'))
app.use('/api', router)
assert.equal(typeof router, 'function')
assert.equal(typeof router.get, 'function')
assert.equal(typeof router.use, 'function')
assert.equal(typeof app.use, 'function')
Express 5 route syntax
Express 5 uses the newer path-to-regexp route syntax. Do not retain the common Express 4 string-path forms:
- Use
/*splatinstead of/*. - Use
/{*splat}when the wildcard should also match the root path. - Use
/:file{.:ext}instead of/:file.:ext?. - Parameter names must be valid JavaScript identifiers or quoted names.
?, regexp characters, and several reserved characters are not accepted in string paths as they were in older forms.
import express from 'express'
import assert from 'node:assert/strict'
const app = express()
app.get('/*splat', (_req, res) => res.send('wildcard'))
app.get('/{*splat}', (_req, res) => res.send('root-or-wildcard'))
app.get('/:file{.:ext}', (_req, res) => res.send('file'))
assert.equal(typeof app.get, 'function')
Async handlers and errors
Rejected promises from middleware and route handlers are forwarded to error middleware in Express 5. An async handler can therefore stand alone; the Express 4 .catch(next) pattern is not required.
import express from 'express'
import assert from 'node:assert/strict'
const app = express()
app.get('/user/:id', async (_req, _res) => {
throw new Error('lookup failed')
})
app.use((error, _req, res, _next) => {
assert.equal(error.message, 'lookup failed')
res.status(500).send('handled')
})
assert.equal(typeof app, 'function')
This example registers the behavior but does not issue a network request. The complete request lifecycle is exercised through the HTTP server in the surrounding application or tool.
Express 5 migration pitfalls
- Use
app.delete(), not the removedapp.del(). - Use
res.status(200).send(body), notres.send(body, status). res.redirect('back')andres.location('back')are removed; usereq.get('Referrer') || '/'explicitly, for exampleres.redirect(req.get('Referrer') || '/').app.param(fn)androuter.param(fn)are removed forms.- Singular method aliases are removed.
app.routeris available again as a reference to the base router.app.listen(..., callback)passes server errors to the callback rather than throwing them.- Brotli request-body decompression is supported.
Static files
express.static(root, options) uses root and dotfiles, not the old from and hidden options. dotfiles defaults to "ignore"; explicitly opt in with { dotfiles: 'allow' } when serving hidden paths such as /.well-known.
express.static.mime is gone; use mime-types instead. Static-file behavior requires an application filesystem and is not exercised by the standalone examples here.
Not covered
This skill does not cover Node HTTP server lifecycle details, network-based integration tests, filesystem fixtures for static files, the complete request and response object API, every middleware option, deployment configuration, or optional migration codemods. Those require the surrounding application/tool or information not present in the supplied research.