Instances of TuftRouteMap are not created directly, but are instead returned by the function tuft, imported and invoked like so:

const { tuft } = require('tuft')
const routes = tuft()

An options object can be passed as the first and only argument.

const routes = tuft({
  trailingSlash: true,
  basePath: '/foo'

The options argument may contain any of the following properties:


Prepended to the path of all routes that are added to the route map. If set to '/foo', then a route with the path '/bar' would end up having a path of '/foo/bar'.

Defaults to an empty string.

corsboolean | CorsOptions

Enables CORS support for all routes that are added to the route map.

If set to true, CORS will be enabled for all origins and will support the methods DELETE, GET, HEAD, PATCH, POST, and PUT.

If set to an object, the following options are supported:


Configures the Access-Control-Allow-Origin response header. If set to an array of origins, then a request from any of the set origins will be accepted.

Defaults to '*'.


Configures the Access-Control-Allow-Methods response header. Can be set to a single method or an array of methods.

Defaults to ['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT'].


Configures the Access-Control-Allow-Headers response header. Can be set to a single method or an array of methods.

If not set, defaults to the headers provided by the request's Access-Control-Request-Headers header.


Configures the Access-Control-Allow-Credentials response header. The only accepted value is true.

If not set, the header is not added to the response.


Configures the Access-Control-Expose-Headers response header. Can be set to a single header or an array of headers.

If not set, no additional headers will be exposed.


Configures the Access-Control-Max-Age response header. Set to a number (in seconds).

If not set, the header is omitted from the response.


Used as the default value for path if one is not provided when adding a route.

Defaults to '/'.


An array of Tuft pre-handlers that are executed serially before the response handler. For more information, see the section on pre-handlers.

Defaults to [].


An array of Tuft responders that are executed serially after the response handler returns. A Tuft responder is a function which receives a response object, and then determines how to respond to the client based on the properties included in that object. For more information, see the section on Tuft responders.

Defaults to [].


Match routes that have a trailing slash. If set to true, the route '/foo' would be matched by both '/foo' and '/foo/'.

Defaults to null.


Indicates whether or not the application should trust the following incoming headers:

  • 'X-Forwarded-For'
  • 'X-Forwarded-Port'
  • 'X-Forwarded-Proto'

If set to false, these headers will always be set to undefined on every incoming request.

Defaults to true.


.set(routeName, response)

Accepts a string representing the route path as the first argument, with a specific request method or methods optionally prepended, and a response object (or function that returns a response object) as the second argument.

The following route will respond to all supported request methods when requesting path '/foo':

routes.set('/foo', {
  status: 200

To add a route that responds to a specific request method, prepend the method name to the path, followed by a space:

routes.set('GET /foo', {
  status: 200

To add multiple methods, separate them with a vertical slash '|':

// Add the routes 'GET /foo' and 'POST /foo'
routes.set('GET|POST /foo', {
  status: 200

Tuft supports the following request methods:

  • GET
  • HEAD
  • POST
  • PUT

Tuft also supports wildcard globs and named parameters in pathnames. To add a wildcard for a single path segment, use an asterisk enclosed between curly braces {*}, or use two asterisks to represent a path segment and any successive segments {**}.

// Match '/foo' and '/bar', but not '/foo/bar'
routes.set('GET /{*}', {
  status: 200

// Match '/foo/bar' and '/foo/baz', but not '/bar/bar' or '/bar/baz'
routes.set('GET /foo/{*}', {
  status: 200

// Match '/foo/bar' and '/foo/bar/baz', but not '/bar/bar' or '/bar/bar/baz'
routes.set('GET /foo/{**}', {
  status: 200

To add a route with a named parameter, replace the single asterisk from the example above with the parameter name. The parameter values will be made available in the params property of the request object.

// Match '/users/1234'
routes.set('GET /users/{id}', ({ request }) => {
  console.log(request.params) // { id: '1234' }
  return {
    status: 200


Accepts another instance of TuftRouteMap. All routes from the passed route map will be merged with the current one.

const routes1 = tuft()
routes1.set('GET /foo', {})

const routes2 = tuft()
routes2.merge(routes1) // routes2 now also includes the route 'GET /foo'

There are some important things to note about how the values set in route map options are handled when merging:

  • If the current route map has the basePath option set, then the paths for all merged routes will also have that base path prepended to them.
  • Any prehandlers and/or responders present for the current route map will also be added to all the merged routes.
  • If a merged route already has the trailingSlash option set then it will be preserved, otherwise it will inherit whatever has been set in the current route map.

.redirect(routeName, url)

Redirects any requests for routeName to url, which can be a relative path or an absolute URI.

// Redirect all GET requests for '/foo' to '/bar'
routes.redirect('GET /foo', '/bar')

// Redirect all GET requests for '/example' to ''
routes.redirect('GET /example', '')

Clients will be redirected with a 302 Found status code.

.static(routeName, path) async

Serves static files from the provided route. Unlike other route map methods, routeName should not include the request method in the string. Static files are automatically served to GET and HEAD requests only. Also unlike other route map methods, .static() returns a promise and so should be called with the await keyword.

path can be either a file path or directory path. If a file path, the file will be available at the provided route path:

await routes.static('/images', 'assets/profile.png')
// profile.png will be available at '/images/profile.png'

If a directory path, all files within that directory (including subdirectories) will be available at the provided route path:

// Assuming the 'assets' directory includes the files:
// * profile.png
// * styles.css
// * favicon.ico

await routes.static('/', 'assets')
// The above files will be available at:
// * '/profile.png'
// * '/styles.css'
// * '/favicon.ico'


Adds a listener for errors that occur in the request/response cycle, executing callback whenever they are emitted. Tuft catches synchronous exceptions and asynchronous rejections that occur in the request/response cycle, or any errors that are emitted by the Node HTTP/2 stream object. It then responds to the client with a 500 Internal Server Error status code, provided the connection is still open and the headers have not already been sent. The emitted Error object is then passed to callback as its first and only argument.

routes.onError(err => {
  console.error(err) // Pipe the error to stderr


At present, Tuft only supports a single error listener. Multiple calls to .onError() will override the previously added callback.


Returns an instance of TuftServer, which is an HTTP server instance that listens for and responds to requests based on the routes that were added to the route map. Accepts an options argument, which is an object that may contain any of the following properties:


The host address the server should listen on.

Defaults to 'localhost'.


The port the server should listen on.

Defaults to 0 (random port).


Same as .createServer(), except that it returns an instance of TuftSecureServer, which is an HTTPS server instance. In addition to the options accepted by .createServer(), it also accepts the following properties:


A private key in .pem format.


A certificate in .pem format.


Although key and cert are listed as options for .createSecureServer(), they are in fact required for a secure server to function properly. Your server will not be able to respond to requests if you start a secure server without them.