Presenting tinyhttp 1.0 - a 0-legacy, tiny & fast web framework as a replacement of Express, written in TypeScript.

Presenting tinyhttp 1.0 - a 0-legacy, tiny & fast web framework as a replacement of Express, written in TypeScript.


After months of hard work I'm thrilled to present the first major (1.0) release of tinyhttp, a 0-legacy, tiny and fast web framework which aims to replace Express for modern Node.js environments.

What is tinyhttp

A few months ago I wrote an introduction post to tinyhttp, but if in short, it's an Express-like web framework that tries to be as minimal as possible by relying on modern runtime and language features instead of dozens of polyfills and deprecated APIs.

tinyhttp apps look almost the same as Express ones, for example:

import { App } from '@tinyhttp/app'
import { logger } from '@tinyhttp/logger'
const app = new App()
.use(function someMiddleware(req, res, next) {
console.log('Did a request')
.get('/', (_, res) => {
res.send('<h1>Hello World</h1>')
.get('/page/:page/', (req, res) => {
res.status(200).send(`You just opened ${}`)

You probably noticed that instead of require, here we use ESM imports. tinyhttp is recommended to be used with "Native ESM", a module system of the ECMAScript spec implemented for Node.js.

Unlike Express, tinyhttp is compiled to both ESM and CommonJS module systems, while still being much smaller than Express.

tinyhttp also targets the latest LTS runtimes, such as Node 12 and newer, to achieve the smallest package size and avoid using polyfills for language features that are already present in JavaScript.

The project repository contains a lot of examples of using tinyhttp with other technologies, such as ORMs, databases, authentification libraries, etc.

What's new in 1.0

There are a few important additions since the 0.5 release, such as custom req / res extensions and catching async errors.

Custom middleware extensions

With the new applyExtensions parameter you can define your own req / res extensions, or disable all tinyhttp's extensions to achieve the best performance.

import { App } from '@tinyhttp/app'
import { send } from '@tinyhttp/send'
const app = new App({
applyExtensions: (req, res, next) => {
// now tinyhttp only has a `res.send` extension
res.send = send(req, res)

Catching errors in async handlers

Errors thrown in async handlers are now catched and passed to next, e.g. if an error is throwed in a handler it won't crash and will keep working.


import { App } from '@tinyhttp/app'
const app = new App()
app.use(async (_req, _res) => {
throw `error`

The app will return the error and send 500 status:

$ curl localhost:3000

Other changes

  • Code coverage increased up to 92%
  • Every submodule of tinyhttp doesn't strictly depend on IncomingMessage and ServerResponse anymore. Only the used properties are required, meaning that these packages are more flexible and can be used in other runtimes.
  • A few bug fixes in @tinyhttp/router and other packages

tinyhttp is also open for contributions! Read the guide to get started.