@hapi/accept
Parses HTTP Accept, Accept-Charset, Accept-Encoding, and Accept-Language headers to select the best response format per RFC 7231.
Repository Health
Technical Analysis
@hapi/accept is a focused HTTP content-negotiation library from the hapi ecosystem. It parses the four Accept-* request headers — Accept, Accept-Charset, Accept-Encoding, and Accept-Language — and returns either a single best match or a fully ranked list of client-acceptable options, taking RFC 7231 q-value weighting, wildcard entries, and encoding equivalents (like x-gzip → gzip) into account.
While built to pair naturally with the hapi web framework, it has no hard dependency on it beyond @hapi/hoek (assertions) and @hapi/boom (HTTP-aware errors for malformed headers), so it works standalone in any Node.js HTTP server that needs to decide what charset, encoding, language, or media type to respond with.
What You Get
- charset(header, preferences) and charsets(header) - resolve the best Accept-Charset match or a fully ranked list
- encoding(header, preferences) and encodings(header) - resolve Accept-Encoding, normalizing x-gzip/x-compress aliases and defaulting to identity
- language(header, preferences) and languages(header) - resolve Accept-Language with case-insensitive prefix matching (en-GB matches an en preference)
- mediaType(header, preferences) and mediaTypes(header) - resolve the Accept header for response content-type selection
- parseAll(headers) - a convenience call that runs all four parsers against a request headers object in one shot
- Shipped TypeScript definitions (lib/index.d.ts) for every exported function and the parseAll() result shape
Common Use Cases
- API content negotiation - a REST or GraphQL server picks JSON vs XML vs HTML response bodies based on the client’s Accept header
- Compression negotiation - a server chooses gzip, deflate, or no compression for a response body based on Accept-Encoding
- Localization - a server selects the response locale from Accept-Language, respecting client-side language preference ordering
- Framework-level request parsing - web frameworks (hapi and others) call parseAll() once per request to normalize all negotiable dimensions up front
- Malformed-header rejection - servers use the thrown Boom 400 errors to reject clients sending invalid Accept-* syntax without writing their own validation
Under The Hood
Architecture
The package is a small, single-purpose module split by concern: lib/header.js holds a generic selection/selections parser driven by an options descriptor (header type, default value, encoding equivalents map, prefix-match flag), while lib/media.js handles the structurally different media-type/subtype/parameter grammar of the Accept header separately. lib/index.js builds the four paired charset/encoding/language exports programmatically by looping over an internals.options config object rather than hand-writing four near-identical function pairs, then adds mediaType/mediaTypes from the separate media module and a parseAll() aggregator on top. Parsing itself is a single internal parse() function in header.js: it strips whitespace, splits on commas, extracts and validates q-values, sorts by weight/preference/position, and — when a preferences array is supplied — remaps sorted tokens back to the caller’s original-cased preference strings. Malformed headers (extra ; segments, non-q parameters) throw Boom.badRequest rather than failing silently.
Tech Stack
Pure JavaScript with no runtime framework dependency — only @hapi/hoek for a single assertion call and @hapi/boom for HTTP-shaped errors. Ships hand-written TypeScript declarations (lib/index.d.ts) rather than being authored in TypeScript directly. Tests run on hapi’s own stack, @hapi/lab as the runner and @hapi/code for assertions, with linting via @hapi/eslint-plugin. CI is delegated to a shared reusable GitHub Actions workflow (hapijs/.github) rather than a repo-local pipeline, keeping the config itself minimal.
Code Quality
Each header type has its own dedicated test file (charset.js, encoding.js, language.js, mediatype.js) plus a combined index.js/index.ts test covering parseAll() and the shipped type definitions. The test script (lab -a @hapi/code -t 100 -L -Y) enforces 100% coverage and strict linting on every run, and errors are explicit Boom throws rather than swallowed exceptions or silent fallbacks. Naming is consistent and the RFC section numbers are cited directly in comments next to the code they justify.
API Design
The public surface is deliberately narrow and uniform: every negotiable header gets a singular (best match) and plural (ranked list) function pair with an identical signature shape, so learning one pair teaches all of them. parseAll() gives a one-call shortcut for frameworks that want every dimension resolved up front. Documentation lives on the hosted hapi.dev developer portal with runnable code examples for every function rather than in the repo’s own README, which is intentionally minimal and just points there.