API Documentation


The app object is the whole tinyhttp application with all the middleware, handlers and settings.

import { App } from '@tinyhttp/app'

const app = new App()

app.get('/', (req, res) => {
  res.send('hello world')


The app object has methods for

The tinyhttp application object can be referred from the request object and the response object as req.app, and res.app, respectively.


noMatchHandler(req, res)

Handler if none of the routes match. Should return 404 Not found.

import { App, Request, Response } from '@tinyhttp/app'

const app = new App({
  noMatchHandler: (req: Request, res: Response) => void res.status(404).end('Not found :(')

  .get('/', (req, res) => {
    res.send('hello world')

onError(err, req, res)

A middleware to catch server errors. Error can be anything. Should return 500 Internal Server Error.

import { App, Request, Response } from '@tinyhttp/app'

const app = new App({
  onError: (err, req, res) => {
      message: err.message

app.get('/', (req, res) => void res.send('hello world')).listen(3000)


A handler-like function that adds request and response extensions to handlers. By default, the extendMiddleware function is used, which contains all tinyhttp's req / res extensions.

import { App, extendMiddleware } from '@tinyhttp/app'

const app = new App({
  applyExtensions: (req, res, next) => {
    extendMiddleware(req, res, next)

    res.someExt = someExt(req, res, next)

When an empty function is passed, all extensions from extendMiddleware aren't included anymore:

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)

You can also disable all of the extensions by passing an empty function:

import { App } from '@tinyhttp/app'

const app = new App({
  applyExtensions: (req, res, next) => { next() }


tinyhttp application has a list of settings to toggle various application parts. All of them are opted out by default to achieve the best performance (less extensions, better performance).

import { App } from '@tinyhttp/app'

const app = new App({
  settings: {
    networkExtensions: true

app.use((req, res) => void res.send(`Hostname: ${req.hostname}`)).listen(3000)

Here's a list of all of the settings:


Enabled a list of Request object extensions related to network.


Subdomain offset for req.subdomains. Defaults to 2.


Bind the app as a reference to the actual app to req.app and res.app. Disabled by default.


Enables req.route property. Disabled by default.



The app.locals object has properties that are local variables within the application.

// => 'My App'

// => 'me@myapp.com'

Once set, the value of app.locals properties persist throughout the life of the application, in contrast with res.locals properties that are valid only for the lifetime of the request.

You can access local variables in templates rendered within the application. This is useful for providing helper functions to templates, as well as application-level data.

app.locals.title = 'My App'
app.locals.strftime = require('strftime')
app.locals.email = 'me@myapp.com'


app.parent points to a parent App object, e.g. the app that was mounted to.

const app = new App()

const subapp = new App()



<ref *1> App {
  middleware: [],
  mountpath: '/',
  apps: {
    '/': App {
      middleware: [],
      mountpath: '/',
      apps: {},
      parent: [Circular *1]


Coming soon...



Routes an HTTP request, where METHOD is the HTTP method of the request, such as GET, PUT, POST, and so on, in lowercase. Thus, the actual methods are app.get(), app.post(), app.put(), and so on.

Routing methods


This method is like the standard app.METHOD() methods, except it matches all HTTP verbs.

The following callback is executed for requests to /secret whether using GET, POST, PUT, DELETE, or any other HTTP request method:

app.all('/secret', (req, res, next) => {
  console.log('Accessing the secret section ...')
  next() // pass control to the next handler

The app.all() method is useful for mapping “global” logic for specific path prefixes or arbitrary matches. For example, if you put the following at the top of all other route definitions, it requires that all routes from that point on require authentication, and automatically load a user. Keep in mind that these callbacks do not have to act as end-points: loadUser can perform a task, then call next() to continue matching subsequent routes.

app.all('*', requireAuthentication, loadUser)


Routes HTTP GET requests to the specified path with the specified handler functions.

app.get('/', (req, res) => {
  res.send(`${req.method || 'GET'} request to homepage`)


Routes HTTP POST requests to the specified path with the specified handler functions.

app.post('/', (req, res) => {
  res.send(`${req.method || 'POST'} request to homepage`)


Routes HTTP PUT requests to the specified path with the specified handler functions.

app.put('/', (req, res) => {
  res.send(`${req.method || 'PUT'} request to homepage`)


Routes HTTP DELETE requests to the specified path with the specified handler functions.

app.delete('/', (req, res) => {
  res.send(`${req.method || 'DELETE'} request to homepage`)


Mounts the specified middleware function or functions at the specified path: the middleware function is executed when the base of the requested path matches path.

A route will match any path that follows its path immediately with a /. For example: app.use('/apple', ...) will match /apple, /apple/images, /apple/images/news, and so on.

Since path defaults to /, middleware mounted without a path will be executed for every request to the app. For example, this middleware function will be executed for every request to the app:

app.use((req, res, next) => {
  console.log('Time: %d', Date.now())

Middleware functions are executed sequentially, therefore the order of middleware inclusion is important.

// this middleware will not allow the request to go beyond it
app.use((req, res, next) => void res.send('Hello World'))

// requests will never reach this route
app.get('/', (req, res) => res.send('Welcome'))


Register a template engine. Works with any Express template engines that contain a renderFile function.

import { App } from '@tinyhttp/app'
import { renderFile } from 'eta'

const app = new App()

app.engine('eta', renderFile) // map app.engines['eta'] to `renderFile`


Render a file with the engine that was set previously via app.engine. To render and respond with the result, use res.render

import { App } from '@tinyhttp/app'
import { renderFile } from 'eta'

const app = new App()

app.engine('eta', renderFile)

  { name: 'Eta' },
    /* some options */
  (err, html) => {
    if (err) throw err

tinyhttp doesn't support app.render overloading yet. Because of that, some engines (Pug, for example) may require additional wrapping.

import { App } from '@tinyhttp/app'
import pug from 'pug'

const app = new App()

const renderPug = (path, _, options, cb) => pug.renderFile(path, options, cb)

app.engine('pug', renderPug)

app.use((_, res) => void res.render('index.pug'))

app.listen(3000, () => console.log(`Listening on http://localhost:3000`))


Returns the mountpath of the app.

const app = new App()
const blog = new App()
const blogAdmin = new App()

app.use('/blog', blog)
blog.use('/admin', blogAdmin)

console.dir(app.path()) // ''
console.dir(blog.path()) // '/blog'
console.dir(blogAdmin.path()) // '/blog/admin'


Returns an instance of a single route, which you can then use to handle HTTP verbs with optional middleware. Use app.route() to avoid duplicate route names.

new App()
  .all((req, res, next) => {
    // runs for all HTTP verbs first
    // think of it as route specific middleware!
  .get((req, res, next) => res.json({ hello: 'world' }))
  .post((req, res, next) => {
    // maybe add a new event...


Sets the Boolean setting name to true, where name is one of the properties from the app settings.



Sets the Boolean setting name to false, where name is one of the properties from the app settings.



Sets the setting name to value, where is one of the properties from the app settings.

app.set('subdomainOffset', 2)


Extends req / res objects, pushes 404 and 500 handlers, dispatches middleware and matches paths.

In some cases where you don't need to start a server but pass the req / res handler instead.

For example you want to start an HTTP/2 server instead of HTTP one:

import { App } from '@tinyhttp/app'
import type { Request, Response } from '@tinyhttp/app'
import fs from 'fs'
import { createSecureServer } from 'http2'

const app = new App()

const options = {
  key: fs.readFileSync('localhost-privkey.pem'),
  cert: fs.readFileSync('localhost-cert.pem')

app.get('/', (req, res) => void res.send(`Hello from HTTP ${req.httpVersion} server!`))

createSecureServer(options, async (req: Request, res: Response) => {
  await app.handler(req, res)

It's also common to pass the handler in serverless functions (e.g. Vercel), like this:

const { App } = require('@tinyhttp/app')

app.use((req, res) => res.send(`You're on ${req.url}`))

module.exports = async (req, res) => await app.handler(req, res)


Starts an HTTP server and listens on a specified port and host.

import { App } from '@tinyhttp/app'

const app = new App()

app.use((_, res) => res.send('Hello World')).listen(3000, () => console.log(`Started on :3000`))


The req object represents the HTTP request and has properties for the request query string, parameters, body, HTTP headers, and so on.

app.get('/user/:id', (req, res) => void res.send(`user ${req.params.id}`))

The req object is an enhanced version of Node.js built-in IncomingMessage object.



Contains the hostname derived from either Host or X-Forwarded-Host HTTP header.

// Host: "example.com:3000"
// => 'example.com'


This property is an object containing a property for each query string parameter in the route.

// GET /search?q=tobi+ferret
// => "tobi ferret"

// GET /shoes?order=desc&shoe[color]=blue&shoe[type]=converse
// => "desc"

// => "blue"

// => "converse"

// GET /shoes?color[]=blue&color[]=black&color[]=red
// => [blue, black, red]


This property can be enabled via enableReqRoute setting

Contains the currently-matched route, a string. For example:

app.get('/user/:id?', function userIdHandler(req, res) {

Example output would be something like this:

  path: '/user/:id?',
  method: 'GET',
  handler: [Function: userIdHandler],
  type: 'route'


This property is an object containing properties mapped to the named route “parameters”. For example, if you have the route /user/:name, then the “name” property is available as req.params.name. This object defaults to {}.

// GET /user/v1rtl

app.get('/user/:name', (req, res) => {
  res.end(`Hello ${req.params.name}!`)
// => v1rtl


Contains the request protocol string: either http or (for TLS requests) https. This property will use the value of the X-Forwarded-Proto header field if present. This header can be set by the client or by the proxy.



A Boolean property that is true if a TLS connection is established. Equivalent to the following:

req.protocol === 'https'


A Boolean property that is true if the request’s X-Requested-With header field is “XMLHttpRequest”, indicating that the request was issued by a client library such as fetch.

// => true


When the response is still “fresh” in the client’s cache true is returned, otherwise false is returned to indicate that the client cache is now stale and the full response should be sent.

When a client sends the Cache-Control: no-cache request header to indicate an end-to-end reload request, this module will return false to make handling these requests transparent.

Further details for how cache validation works can be found in the HTTP/1.1 Caching Specification.

// => true


Indicates whether the request is “stale,” and is the opposite of req.fresh. For more information, see req.fresh.

// => true


Contains the remote IP address of the request.

// => ''


Contains an array of remote IP addresses of the request.

// => [']


This property can be enabled via networkExtensions setting

Contains an array of subdomains. Subdomain offset can be set via subdomainOffset

// dev.node0.example.com

// ['node0', 'dev']


This property can be enabled via bindAppToReqRes setting

Points to a reference of the currently used app.

app.use((req, res) => void res.json(req.app.settings))


Contains the path part of the request URL.

// example.com/users?sort=desc
// => '/users'



Checks if the specified content types are acceptable, based on the request’s Accept HTTP header field. The method returns the best match, or if none of the specified content types is acceptable, returns false (in which case, the application should respond with 406 "Not Acceptable").

The type value may be a single MIME type string (such as "application/json"), an extension name such as "json", a comma-delimited list, or an array. For a list or array, the method returns the best match (if any).

// Accept: text/html
// => "html"

// Accept: text/*, application/json
// => "html"
// => "text/html"
req.accepts(['json', 'text'])
// => "json"
// => "application/json"

// Accept: text/*, application/json
// => false

// Accept: text/*;q=.5, application/json
req.accepts(['html', 'json'])
// => "json"

For more information, or if you have issues or concerns, see accepts.


Returns the first accepted encoding of the specified encodings, based on the request’s Accept-Encoding HTTP header field. If none of the specified encodings is accepted, returns false.


Returns the first accepted charset of the specified character sets, based on the request’s Accept-Charset HTTP header field. If none of the specified charsets is accepted, returns .false


Returns the specified HTTP request header field (case-insensitive match).

// => "text/plain"

// => "text/plain"

// => undefined


Parse Range header.

app.use((req, res) => {
  const ranges = req.range(1000)

  if (range.type === 'bytes') {
  // the ranges
  range.forEach((r) => {
    // do something with r.start and r.end


The res object represents the HTTP response that a tinyhttp app sends when it gets an HTTP request.



This property can be enabled via bindAppToReqRes setting

Points to a reference of the currently used app.

app.use((req, res) => {



Appends the specified value to the HTTP response header field. If the header is not already set, it creates the header with the specified value. The value parameter can be a string or an array.

calling res.set() after res.append() will reset the previously-set header value.

res.append('Link', ['<http://localhost/>', '<http://localhost:3000/>'])
res.append('Set-Cookie', 'foo=bar; Path=/; HttpOnly')
res.append('Warning', '199 Miscellaneous warning')


Sets cookie name to value. The value parameter may be a string or object converted to JSON.

The options parameter is an object that can have the following properties.

Property Type Description
domain string Domain name for the cookie. Defaults to the domain name of the app.
encode Function A synchronous function used for cookie value encoding. Defaults to encodeURIComponent.
expires Date Expiry date of the cookie in GMT. If not specified or set to 0, creates a session cookie.
httpOnly boolean Flags the cookie to be accessible only by the web server.
maxAge number Convenient option for setting the expiry time relative to the current time in milliseconds.
path string Path for the cookie. Defaults to “/”.
secure boolean Marks the cookie to be used with HTTPS only.
signed boolean Indicates if the cookie should be signed.
sameSite boolean | string Value of the “SameSite” Set-Cookie attribute. More info.

All res.cookie() does is set the HTTP Set-Cookie header with the options provided. Any option not specified defaults to the value stated in RFC 6265.

res.cookie('name', 'tobi', {
  domain: '.example.com',
  path: '/admin',
  secure: true

// Enable "httpOnly" and "expires" parameters
res.cookie('rememberme', '1', {
  expires: new Date(Date.now() + 900000),
  httpOnly: true


Clears the cookie specified by name. For details about the options object, see res.cookie().

Web browsers and other compliant clients will only clear the cookie if the given options is identical to those given to res.cookie(), excluding expires and maxAge.

res.cookie('name', 'tobi', { path: '/admin' })
res.clearCookie('name', { path: '/admin' })


Ends the response process. The method comes from response.end() of http.ServerResponse..

Can be used to send raw data or end the response without any data at all. If you need to respond with data with proper content type headers set and so on, instead use methods such as res.send() and res.json().



Sends a JSON response. This method sends a response (with the correct Content-Type header) that is the parameter converted to a JSON string using JSON.stringify().

The body can be any kind of JSON, including object, array, string, boolean, number, or null.

res.json({ user: 'tobi' })
res.status(500).json({ error: 'message' })


Sends the HTTP response.

The body can be a Buffer object, a string, an object, or an array.

res.send({ some: 'json' })
res.send('<p>some html</p>')
res.status(404).send('Sorry, we cannot find that!')
res.status(500).send({ error: 'something blew up' })

This method performs many useful tasks for simple non-streaming responses: For example, it automatically set the proper Content-Length header value and provides automatic HEAD and HTTP cache freshness support.

When the parameter is a Buffer object, the method sets the Content-Type response header field to "application/octet-stream", unless previously defined as shown below:

res.set('Content-Type', 'text/html')
res.send(Buffer.from('<p>some html</p>'))

When the parameter is a string, the method sets the Content-Type to "text/html":

res.send('<p>some html</p>')

When the parameter is an Array or Object, Express responds with the JSON representation (same as res.json):

res.send({ user: 'tobi' })
res.send([1, 2, 3])


Sets the HTTP status for the response. It is a chainable alias of Node’s response.statusCode.

res.status(400).send('Bad Request')


Sets the response HTTP status code to statusCode and send its string representation as the response body.

res.sendStatus(200) // equivalent to res.status(200).send('OK')
res.sendStatus(403) // equivalent to res.status(403).send('Forbidden')
res.sendStatus(404) // equivalent to res.status(404).send('Not Found')
res.sendStatus(500) // equivalent to res.status(500).send('Internal Server Error')

If an unsupported status code is specified, the HTTP status is still set to statusCode and the string version of the code is sent as the response body.

res.sendStatus(9999) // equivalent to res.status(9999).send('9999')

More about HTTP Status Codes


Sends a file by piping a stream to response. It also checks for extension to set a proper Content-Type header field.

Path argument must be absolute. To use a relative path, specify the root option first.

res.sendFile('song.mp3', { root: process.cwd() }, (err) => console.log(err))


Sets the response’s HTTP header field to value. To set multiple fields at once, pass an object as the parameter.

res.set('Content-Type', 'text/plain')

  'Content-Type': 'text/plain',
  'Content-Length': '123',
  ETag: '12345'

Alias to res.header.

Joins the links provided as properties of the parameter to populate the response’s Link HTTP header field.

For example, the following call:

  next: 'http://api.example.com/users?page=2',
  last: 'http://api.example.com/users?page=5'

Yields the following results:

Link: <http://api.example.com/users?page=2>; rel="next",
      <http://api.example.com/users?page=5>; rel="last"


Sets the response Location HTTP header to the specified path parameter.


A path value of "back" has a special meaning, it refers to the URL specified in the Referer header of the request. If the Referer header was not specified, it refers to "/".

After encoding the URL, if not encoded already, tinyhttp passes the specified URL to the browser in the Location header, without any validation. Browsers take the responsibility of deriving the intended URL from the current URL or the referring URL, and the URL specified in the Location header; and redirect the user accordingly.


Render a template using a pre-defined engine and respond with the result.

import { App } from '@tinyhttp/app'
import ejs from 'ejs'

const app = new App()

app.engine('ejs', ejs.renderFile)

app.use((_, res) => void res.render('index.ejs', { name: 'EJS' }))

app.listen(3000, () => console.log(`Listening on http://localhost:3000`))


Adds the field to the Vary response header, if it is not there already.



Sends a conditional response based on the value in Accept header. For example, if Accept contains html, the HTML option will be sent.

  html: (req, res) => void res.send('<h1>Hello World for HTML</h1>')
  text: (req, res) => void res.send('Hello World for text')

and depending on the Accept header, it will send different responses:

curl -H "Accept: text/html" localhost:3000
# <h1>Hello World for HTML</h1>

curl localhost:3000
# Hello World for text


Redirect to a URL by sending a 302 (or any other) status code and Location header with the specified URL.


// custom status
res.redirect('/some-other-page', 300)


Sets the Content-Type HTTP header to the MIME type as determined by mime.lookup() for the specified type. If type contains the / character, then it sets the Content-Type to type.

// => 'text/html'
// => 'text/html'
// => 'application/json'
// => 'application/json'
// => 'image/png'


Send JSON response with JSONP callback support. res.jsonp isn't used that often so it's located in a separate package - @tinyhttp/jsonp

Here's how to enable it:

import { jsonp } from '@tinyhttp/jsonp'

app.use((req, res, next) => {
  res.jsonp = jsonp(req, res, app)

app.get('/', (req, res) => {
  res.jsonp({ some: 'jsonp' })