# @fastify/sensible packages the error and header helpers every API repeats

> fastify/fastify-sensible is a Fastify plugin that decorates the reply object with HTTP error constructors, vary, cacheControl and preventCache, and exposes a createError factory, because Fastify keeps its core small and ships non-essential utilities as standalone plugins. It follows Fastify's own release cycle, with a major version line per Fastify major.

**fastify/fastify-sensible** — Defaults for Fastify that everyone can agree on

- Repository: https://github.com/fastify/fastify-sensible
- Website: https://npmjs.com/package/@fastify/sensible
- Stars: 542 · Forks: 43
- Language: JavaScript
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/fastify-fastify-sensible

## Why these helpers are not in Fastify itself

The README asks a question and answers it. Why are these APIs here and not included with Fastify? Because Fastify aims to be as small and focused as possible, and every utility that is not essential should be shipped as a standalone plugin. That single design decision explains the shape of the whole package. There is no configuration surface to speak of, no opinions about your error format beyond what the helpers provide, and no framework coupling beyond the reply decorator. The audience is any team running Fastify that has written the same helper file more than once, an HTTP error constructor, a vary wrapper, a cache control setter, and now has three copies of it in three services. The alternative to this plugin is not nothing, it is a small internal module that you maintain, review and argue about, and the pitch of the project, defaults for Fastify that everyone can agree on, is that the shared version is cheaper than the agreement you would have to reach three times. It is a small, MIT licensed package, and the plugin is published on npm as @fastify/sensible.

## Three ways to raise the same error

The usage example shows three styles of doing one thing, and the choice between them is a matter of taste rather than capability. You can decorate the reply and call the method, you can throw the error, or you can return the decorated call from an async handler:

```js
const fastify = require('fastify')()
fastify.register(require('@fastify/sensible'))

fastify.get('/', (req, reply) => {
  reply.notFound()
})

fastify.get('/async', async (req, reply) => {
  throw fastify.httpErrors.notFound()
})

fastify.get('/async-return', async (req, reply) => {
  return reply.notFound()
})

fastify.listen({ port: 3000 })
```

Underneath, the fastify.httpErrors object exposes createError and all of the 4xx and 5xx error constructors, and the reply interface is decorated with the same functions. The constructors follow the same structure as the http-errors package, that is new createError[code || name]([msg]), and the custom message is optional, so a call such as fastify.httpErrors.notFound('custom message') is valid. For anything the named constructors do not cover, createError takes a status, a message and optional properties, so you can build an error with code 404 and your own text. The list of named constructors is long and complete enough to matter, covering the full set of 4xx statuses from badRequest to unavailableForLegalReasons and the 5xx set from internalServerError to networkAuthenticationRequired.

## A shared JSON Schema for error responses

Error responses are only useful to clients if they have a documented shape, and that is where the sharedSchemaId option earns its place. If you set it, a shared JSON Schema is added and can be used in your routes, which means every error response can be declared once and referenced by status code instead of being described per route. The example registers the plugin with an identifier and then references it from a route schema:

```js
const fastify = require('fastify')()
fastify.register(require('@fastify/sensible'), {
  sharedSchemaId: 'HttpError'
})

fastify.get('/async', {
  schema: {
    response: {
      404: { $ref: 'HttpError' }
    }
  },
  handler: async (req, reply) => {
    return reply.notFound()
  }
})
```

The practical value is in the response serialisation. Fastify uses the route schema to pick and serialise the response, so referencing a shared schema keeps the error body identical across routes and lets the framework validate and serialise it, rather than leaving the shape to whatever the error object happens to carry. If your API already has a custom error envelope, this is the option to look at closely, because it is the place where the plugin's defaults meet your contract.

## Header helpers for vary, cache control and no-store

The rest of the plugin decorates the reply with header helpers, and the pattern is the same for all of them. reply.vary is decorated with the jshttp/vary helper, the API is the same, but you do not need to pass the res object, so a handler calls reply.vary('Accept') and then sends its payload. reply.cacheControl is a helper to configure cache control response headers, and it takes either a type or a type with a time. Calling it once sets a single type, calling it several times configures multiple types, and the time can be a number such as 42 or a string, where all the formats used by the referenced cache header library are supported, so a value of '1d' sets max-age=86400. A typical call looks like this:

```js
// configure a single type
fastify.get('/', (req, reply) => {
  reply.cacheControl('public')
  reply.send('ok')
})

// configure multiple types
fastify.get('/', (req, reply) => {
  reply.cacheControl('public')
  reply.cacheControl('immutable')
  reply.send('ok')
})

// configure a type time
fastify.get('/', (req, reply) => {
  reply.cacheControl('max-age', 42)
  reply.send('ok')
})
```

The third helper, reply.preventCache, sets the cache control header to a no caching configuration, and the comment in the README explains that it will set cache-control to no-store, max-age=0, private, and for HTTP/1.0 compatibility also sets pragma. These three helpers together are the part of the package most likely to save real code, since cache headers are exactly the thing that is easy to forget and annoying to get subtly wrong.

## The compatibility table ties plugin versions to Fastify versions

The plugin ships in lockstep with Fastify, and the README gives the mapping in a table. Plugin version 6.x and above targets Fastify 5.x, plugin 5.x targets Fastify 4.x, plugin 4.x targets Fastify 3.x, plugin versions from 2.x up to but not including 4.x target Fastify 2.x, and plugin 1.x targets Fastify 1.x. The note under the table is the part to internalise, because it ties your upgrade decision to someone else's support policy. If a Fastify version is out of support, then so are the corresponding versions of this plugin. The README points to Fastify's LTS policy document for the details of which versions are supported and for how long. In practice this means you do not get to hold a plugin major indefinitely while upgrading Fastify, and it also means that when you move to a new Fastify major you should expect to move the plugin major with it. For a team that treats the Fastify version as a scheduled upgrade, that is a feature, since the error helpers move with the framework rather than lagging behind it.

## A small CommonJS package with modern tooling

The repository is small and modern, and package.json shows the shape of it. The package name is @fastify/sensible at version 6.0.7, it is CommonJS with main pointing at index.js and types pointing at types/index.d.ts, and the working tree holds index.js, a lib/ directory, a types/ directory and a test/ directory alongside an eslint.config.js, a .borp.yaml for the test runner and a .npmrc. The scripts are the interesting part. The test command runs the unit tests and then the TypeScript tests, the unit runner is borp with a check-coverage flag and a line reporter, and the type tests run through tstyche, which is the modern way to type test declaration files. Linting uses eslint with the neostandard style, and there is a CI workflow on the main branch. The runtime dependencies are small, with @lukeed/ms and dequal in the list alongside the header and error libraries the helpers wrap. The author of the package is Tomas Della Vedova, and the contributor list includes several of the core Fastify maintainers, which is worth noting because it is the closest thing to a statement about how this code is looked after. The licence is MIT, and the funding is set up through GitHub Sponsors and Open Collective for the Fastify project.

## Where sensible is the wrong choice

The first case is an API that has a custom error envelope. If your clients expect a body of the shape { error: { code, message, details } } with your own field names, the value of a shared default disappears, because you are back to wrapping the plugin's errors in your own layer, and you have added a dependency to do it. The second case is a service that has already standardised its own helpers, in which case the plugin is a second standard, and the argument about everyone agreeing on defaults becomes an argument about which standard to keep. The third case is a codebase that does not use Fastify, where the plugin is simply not applicable. The real alternative is http-errors on its own, the library this plugin builds its constructors on, used directly with your own reply helpers. The difference in approach is that http-errors gives you the error objects and nothing else, no reply decoration, no vary, no cacheControl, and no shared schema, while sensible gives you the whole set wired into the Fastify reply object in the shape Fastify expects. If you only need error objects and you already control your responses, the underlying library is the smaller dependency. If you want the whole kit, this is the kit.

## Conclusion

@fastify/sensible fits a Fastify service that wants one agreed way to raise HTTP errors and set caching headers, and that prefers shared defaults over per-project helpers. It does not fit an API with a custom error envelope, because the plugin's value is precisely that everyone uses the same shapes. Before adopting it, check the compatibility table against the Fastify major you run, since plugin 6.x targets Fastify 5.x and the plugin versions stop when the matching Fastify version goes out of support, and note the last push was on 2026-10-15 with v6.0.7 released on 2026-10-08.

## FAQ

### How do I install and use @fastify/sensible?

Run npm i @fastify/sensible, register it with fastify.register(require('@fastify/sensible')), and the reply object gains the error helpers. You can then call reply.notFound(), throw fastify.httpErrors.notFound() or return the decorated call from an async handler.

### Which Fastify version does a given @fastify/sensible version support?

Plugin 6.x and above targets Fastify 5.x, 5.x targets Fastify 4.x, 4.x targets Fastify 3.x, versions from 2.x to below 4.x target Fastify 2.x, and 1.x targets Fastify 1.x. If a Fastify version is out of support, the matching plugin versions are too.

### What does the sharedSchemaId option do?

If you set sharedSchemaId, a shared JSON Schema is added and can be referenced from your route schemas, for example a 404 response referencing a shared HttpError schema. That keeps error response bodies consistent across routes and lets Fastify serialise them from the schema.

### Which response header helpers does the plugin add?

The reply object is decorated with vary, which wraps jshttp/vary without needing the res object, cacheControl for setting cache control headers with a type and optional time, and preventCache, which sets cache-control to no-store, max-age=0, private and also sets pragma for HTTP/1.0 compatibility.

## Sources

- [fastify/fastify-sensible on GitHub](https://github.com/fastify/fastify-sensible)
- [License: MIT](https://github.com/fastify/fastify-sensible/blob/main/LICENSE)
- [Project website](https://npmjs.com/package/@fastify/sensible)
- [README](https://github.com/fastify/fastify-sensible/blob/main/README.md)
- [Releases](https://github.com/fastify/fastify-sensible/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/fastify-fastify-sensible
