Compatibility with Express 5 โ
In general, basically all features and options are supported. Use the Express 5.x documentation for API reference. Anything Express 5 removed is removed here too, so the list below covers only where this differs from Express 5 itself.
โ
- Full support (all features and options are supported)
๐ง - Partial support (some options are not supported)
โ - Not supported
express โ
- โ express()
- โ express.Router()
- โ express.json()
- โ express.urlencoded()
- โ express.static()
- โ options.index, options.redirect, options.fallthrough, options.extensions
- โ
options.dotfiles, plus
"ignore_files", which is Fulmine's own: it hides a dotfile that is the last segment while letting a dotted directory through
- โ
options.dotfiles, plus
- โ options.setHeaders, options.headers
- โ options.etag, options.lastModified, options.maxAge, options.immutable, options.cacheControl, options.acceptRanges
- โ
options.preCompressed, Fulmine's own: serve the
.bror.gztwin on disk, described under Performance tips
- โ
options.preCompressed, Fulmine's own: serve the
- โ express.text()
- โ express.raw()
- โ express.serverTiming(). Fulmine's own: Server-Timing carrying how the request was routed, described under Performance tips.
- โ
express.testing. Fulmine's own:
expectNative,expectDeclarative,routeReport,expectLazyandworkReport, described under Performance tips. - โ express.compression(). Fulmine's own, since Express has none: it is the compression module's options and behaviour built in, described under Performance tips.
- ๐ง express.request (this is not a constructor but a prototype for replacing methods)
- ๐ง express.response (this is not a constructor but a prototype for replacing methods)
- ๐ง express.application (likewise: a method added here is on every app)
- โ
express.Route. Both
app.route("/path").get(...).post(...)and the class itself, for building a route by hand and dispatching to it.
Application โ
- โ app.listen(port[, host][, callback])
- โ app.listen(unix_socket[, callback])
- โ app.METHOD() (app.get, app.post, etc.)
- โ app.route()
- โ app.all()
- โ app.use()
- โ app.mountpath
- โ app.set()
- โ app.get()
- โ app.enable()
- โ app.disable()
- โ app.enabled()
- โ app.disabled()
- โ app.path()
- โ app.param(name, callback)
- โ app.engine()
- โ app.render()
- โ app.locals
- โ app.settings
- โ app.engines
- โ app.on("mount")
- โ HEAD method
- โ OPTIONS method
- โ QUERY method
What listen() hands back is the app, and it answers as an http.Server so the shutdown wrappers recognise it: app.close(), app.address(), app.listening, app.getConnections(), app.ref(), app.unref(), app.setTimeout() and the keepAliveTimeout family. See Differences from Express for what is behind them and what is not.
Application settings โ
- โ case sensitive routing
- โ env
- โ etag
- โ jsonp callback name
- โ json escape
- โ json replacer
- โ json spaces
- โ query parser
- โ strict routing
- โ subdomain offset
- โ trust proxy
- โ views
- โ view cache
- โ view engine
- โ x-powered-by
Two of these keep a compiled form alongside the value, which you can also set directly:
etag fn, the function that produces an ETag. Settingetagcompiles one; setting this replaces it.query parser fn, likewise forquery parser.
Fulmine adds eight of its own:
body methods, unset by default. The body is read for POST, PUT, PATCH and QUERY, and this names the methods to read one for as well:app.set("body methods", ["DELETE"]). Reading a body no handler asks for costs about 15%, which is why the built-in list is short rather than every method.native routes, on by default. Off, every request walks the ordinary chain instead of letting ยตWS match what it can, which is slower and answers the same. It is a diagnostic rather than a tuning knob: it exists so one application can be served both ways and the two sets of answers compared, which is how the optimizer is tested. A compiled response needs a native registration to hang on, so turning this off turnsdeclarative responsesoff with it.etag methods, unset by default. Express computes the generated ETag for every method, and so does this until told otherwise.app.set("etag methods", ["GET", "HEAD"])skips the digest on every other method, where freshness is not defined and the validator can never match: worth 21% here on a 4KB POST answer. An ETag set by hand still goes out whatever the method.declarative responses, on by default. Lets a simple enough handler be compiled into a native uWS response, described under Performance tips.connection headers, on by default. Express sendsConnection: keep-aliveandKeep-Aliveon every response, and so does this. Turn it off and neither goes out, while a connection the client asked to close still answersConnection: close: it is the advertisement that goes, not the truth. Worth 2% to 3.5% here on a route that is not compiled, plus the bytes.file cache, on by default. Small files served byres.sendFilecome from a bounded in-process cache, checked against the file'sstaton every request, so an edited file is never served stale. Turn it off where every request has to reach the disk, which is what a public benchmark asks of a standard entry: it was worth about 4% on a 4KB file here, so the cost of turning it off is small.stat cache, off by default. Takes a duration,app.set("stat cache", "1s"). The size and mtime of a file served byres.sendFileorexpress.staticare remembered for that long, so a file that is asked for again inside the window costs no syscall at all. It was worth 15% on a 3KB file and 3% on a 200KB one, where the bytes are the work. What it costs is the one promise thefile cachekeeps: inside the window an edited file is served as it was, so keep the window shorter than you would notice.trust proxy protocol, off by default. Takesreq.ipfrom a PROXY protocol preamble, described under Behind a proxy. Read the warning there before turning it on.
Request โ
- โ implements Readable stream
- โ req.app
- โ req.baseUrl
- โ req.body
- โ req.cookies
- โ req.fresh
- โ req.hostname
- โ req.header
- โ req.headers
- โ req.headersDistinct
- โ req.rawHeaders
- โ req.ip
- โ req.ips
- โ req.method
- โ req.url
- โ req.originalUrl
- โ req.params
- โ req.path
- โ req.protocol
- โ req.query
- โ req.res
- โ req.secure
- โ req.signedCookies
- โ req.stale
- โ req.subdomains
- โ req.xhr
- ๐ง req.route (route implementation is different from Express)
- ๐ง req.connection, req.socket (only
end(),encrypted,remoteAddress,remotePortandlocalPortare supported) - โ req.accepts()
- โ req.acceptsCharsets()
- โ req.acceptsEncodings()
- โ req.acceptsLanguages()
- โ req.get()
- โ req.is()
- โ req.range()
Response โ
- โ implements Writable stream
- โ res.app
- โ res.headersSent
- โ res.req
- โ res.locals
- โ res.append()
- โ res.attachment()
- โ res.cookie()
- โ res.clearCookie()
- โ res.download()
- โ res.end()
- โ res.format()
- โ res.getHeader(), res.get()
- โ res.json()
- โ res.jsonp()
- โ res.links()
- โ res.location()
- โ res.redirect()
- โ res.render()
- โ res.send()
- โ res.sendFile()
- โ options.maxAge
- โ options.root
- โ options.lastModified
- โ options.headers
- โ options.dotfiles
- โ options.acceptRanges
- โ options.cacheControl
- โ options.immutable
- โ Range header
- โ Setting ETag header
- โ If-Match header
- โ If-Modified-Since header
- โ If-Unmodified-Since header
- โ If-Range header
- โ res.sendStatus()
- โ res.header(), res.setHeader(), res.set()
- โ res.status()
- โ res.type()
- โ res.vary()
- โ res.removeHeader()
- โ res.write()
- โ res.writeHead()
- โ res.flushHeaders()
Router โ
- โ router.all()
- โ router.METHOD() (router.get, router.post, etc.)
- โ router.route()
- โ router.use()
- โ router.param(name, callback)
- โ options.caseSensitive
- โ options.strict
- โ options.mergeParams
Tested middlewares โ
Almost all middlewares that are compatible with Express are compatible with Fulmine. Here's list of middlewares that we test for compatibility:
- โ express-fast-json-stringify
- โ
socket.io (via
io.attachApp(app.uwsApp), see WebSockets) - โ
body-parser (use
express.text()etc instead for better performance) - โ cookie-parser
- โ cookie-session
- โ
compression (use
express.compression()instead for better performance) - โ
serve-static (use
express.static()instead for better performance) - โ serve-index
- โ cors
- โ errorhandler
- โ method-override
- โ multer
- โ response-time
- โ express-fileupload
- โ express-session
- โ express-rate-limit
- โ express-subdomain
- โ vhost
- โ http-proxy-middleware
- โ express-http-proxy
- โ express-mongo-sanitize
- โ helmet
- โ passport
- โ morgan
- โ swagger-ui-express
- โ graphql-http
- โ better-sse
- โ supertest
Tested frameworks โ
The list above is middlewares. A framework built on Express is a much larger user of the Express surface than any application is, so those have a suite of their own, in integrations/: the same application served twice, once on Express and once here, with the two outputs compared byte for byte. The four that render pages, and the tsoa routes, are built first by that suite, so what is compared is what their own build produces.
- โ
NestJS through
fulmine.js/nest - โ
Next.js as a custom server,
next().getRequestHandler() - โ
Astro through
@astrojs/nodein middleware mode - โ
SvelteKit through
@sveltejs/adapter-node - โ
React Router v7 through
@react-router/express - โ
Apollo Server through
@as-integrations/express5 - โ
tRPC through
@trpc/server/adapters/express - โ
MCP through
@modelcontextprotocol/sdkon the Streamable HTTP transport, with the body read off the stream or handed over byexpress.json(). Runnable:examples/mcp.js - โ
tsoa: the routes
tsoa spec-and-routesgenerates from a decorated controller, registered withRegisterRoutes(app), validation errors included - โ
Angular SSR, which is an ordinary Express
server.tsplus one line of build configuration
Each of these mounts on an ordinary Express application, so there is nothing to install and nothing to configure beyond what that framework already asks for. Nest is the exception, and only because its adapter decides what to listen on: that one is fulmine.js/nest.
Tested view engines โ
Any Express view engine should work. Here's list of engines we include in our test suite:
- โ ejs
- โ pug
- โ express-dot-engine
- โ express-art-template
- โ express-handlebars
- โ swig