Utilities for parsing, manipulating and stringifying HTTP header values.
HTTP headers contain critical information—from content negotiation and caching directives to authentication tokens and file metadata. While the native Headers API provides a basic string-based interface, it leaves the complexities of parsing specific header formats entirely up to you.
Installation
npm install @remix-run/headers
Each supported header has a class that represents the header value. Use the static from() method to parse header values. Each class has a toString() method that returns the header value as a string, which you can either call manually, or will be called automatically when the header class is used in a context that expects a string.
The following headers are currently supported:
Accept
Parse, manipulate and stringify Accept headers.
Implements Map<mediaType, quality>.
import { Accept } from '@remix-run/headers'
let accept = Accept.from(request.headers.get('accept'))
accept.mediaTypes
accept.weights
accept.accepts('text/html')
accept.accepts('text/plain')
accept.accepts('image/jpeg')
accept.getWeight('text/plain')
accept.getPreferred(['text/html', 'text/plain'])
for (let [mediaType, quality] of accept) {
}
accept.set('application/json', 0.8)
accept.delete('text/*')
headers.set('Accept', accept)
new Accept('text/html, text/*;q=0.9')
new Accept({ 'text/html': 1, 'text/*': 0.9 })
new Accept(['text/html', ['text/*', 0.9]])
let headers = new Headers({
Accept: new Accept({ 'text/html': 1, 'application/json': 0.8 }),
})
headers.set('Accept', new Accept({ 'text/html': 1, 'application/json': 0.8 }))
Accept-Encoding
Parse, manipulate and stringify Accept-Encoding headers.
Implements Map<encoding, quality>.
import { AcceptEncoding } from '@remix-run/headers'
let acceptEncoding = AcceptEncoding.from(request.headers.get('accept-encoding'))
acceptEncoding.encodings
acceptEncoding.weights
acceptEncoding.accepts('gzip')
acceptEncoding.accepts('br')
acceptEncoding.getWeight('gzip')
acceptEncoding.getPreferred(['gzip', 'deflate', 'br'])
acceptEncoding.set('br', 1)
acceptEncoding.delete('deflate')
headers.set('Accept-Encoding', acceptEncoding)
new AcceptEncoding('gzip, deflate;q=0.8')
new AcceptEncoding({ gzip: 1, deflate: 0.8 })
let headers = new Headers({
'Accept-Encoding': new AcceptEncoding({ gzip: 1, br: 0.9 }),
})
headers.set('Accept-Encoding', new AcceptEncoding({ gzip: 1, br: 0.9 }))
Accept-Language
Parse, manipulate and stringify Accept-Language headers.
Implements Map<language, quality>.
import { AcceptLanguage } from '@remix-run/headers'
let acceptLanguage = AcceptLanguage.from(request.headers.get('accept-language'))
acceptLanguage.languages
acceptLanguage.weights
acceptLanguage.accepts('en-US')
acceptLanguage.accepts('en-GB')
acceptLanguage.getWeight('en-GB')
acceptLanguage.getPreferred(['en-US', 'en-GB', 'fr'])
acceptLanguage.set('fr', 0.5)
acceptLanguage.delete('en')
headers.set('Accept-Language', acceptLanguage)
new AcceptLanguage('en-US, en;q=0.9')
new AcceptLanguage({ 'en-US': 1, en: 0.9 })
let headers = new Headers({
'Accept-Language': new AcceptLanguage({ 'en-US': 1, fr: 0.5 }),
})
headers.set('Accept-Language', new AcceptLanguage({ 'en-US': 1, fr: 0.5 }))
Cache-Control
Parse, manipulate and stringify Cache-Control headers.
import { CacheControl } from '@remix-run/headers'
let cacheControl = CacheControl.from(response.headers.get('cache-control'))
cacheControl.public
cacheControl.maxAge
cacheControl.sMaxage
cacheControl.noCache
cacheControl.noStore
cacheControl.noTransform
cacheControl.mustRevalidate
cacheControl.immutable
cacheControl.maxAge = 7200
cacheControl.immutable = true
headers.set('Cache-Control', cacheControl)
new CacheControl('public, max-age=3600')
new CacheControl({ public: true, maxAge: 3600 })
let headers = new Headers({
'Cache-Control': new CacheControl({ public: true, maxAge: 3600 }),
})
headers.set('Cache-Control', new CacheControl({ public: true, maxAge: 3600 }))
Content-Disposition
Parse, manipulate and stringify Content-Disposition headers.
import { ContentDisposition } from '@remix-run/headers'
let contentDisposition = ContentDisposition.from(response.headers.get('content-disposition'))
contentDisposition.type
contentDisposition.filename
contentDisposition.filenameSplat
contentDisposition.preferredFilename
contentDisposition.filename = 'download.pdf'
headers.set('Content-Disposition', contentDisposition)
new ContentDisposition('attachment; filename="example.pdf"')
new ContentDisposition({ type: 'attachment', filename: 'example.pdf' })
let headers = new Headers({
'Content-Disposition': new ContentDisposition({ type: 'attachment', filename: 'example.pdf' }),
})
headers.set(
'Content-Disposition',
new ContentDisposition({ type: 'attachment', filename: 'example.pdf' }),
)
Content-Range
Parse, manipulate and stringify Content-Range headers.
import { ContentRange } from '@remix-run/headers'
let contentRange = ContentRange.from(response.headers.get('content-range'))
contentRange.unit
contentRange.start
contentRange.end
contentRange.size
let unsatisfied = ContentRange.from('bytes */67589')
unsatisfied.start
unsatisfied.end
unsatisfied.size
new ContentRange({ unit: 'bytes', start: 0, end: 499, size: 1000 })
let headers = new Headers({
'Content-Range': new ContentRange({ unit: 'bytes', start: 0, end: 499, size: 1000 }),
})
headers.set('Content-Range', new ContentRange({ unit: 'bytes', start: 0, end: 499, size: 1000 }))
Content-Type
Parse, manipulate and stringify Content-Type headers.
import { ContentType } from '@remix-run/headers'
let contentType = ContentType.from(request.headers.get('content-type'))
contentType.mediaType
contentType.charset
contentType.boundary
contentType.charset = 'iso-8859-1'
headers.set('Content-Type', contentType)
new ContentType('text/html; charset=utf-8')
new ContentType({ mediaType: 'text/html', charset: 'utf-8' })
let headers = new Headers({
'Content-Type': new ContentType({ mediaType: 'text/html', charset: 'utf-8' }),
})
headers.set('Content-Type', new ContentType({ mediaType: 'text/html', charset: 'utf-8' }))
Cookie
Parse, manipulate and stringify Cookie headers.
Implements Map<name, value>.
import { Cookie } from '@remix-run/headers'
let cookie = Cookie.from(request.headers.get('cookie'))
cookie.get('session_id')
cookie.get('theme')
cookie.has('session_id')
cookie.size
for (let [name, value] of cookie) {
}
cookie.set('theme', 'light')
cookie.delete('session_id')
headers.set('Cookie', cookie)
new Cookie('session_id=abc123; theme=dark')
new Cookie({ session_id: 'abc123', theme: 'dark' })
new Cookie([
['session_id', 'abc123'],
['theme', 'dark'],
])
let headers = new Headers({
Cookie: new Cookie({ session_id: 'abc123', theme: 'dark' }),
})
headers.set('Cookie', new Cookie({ session_id: 'abc123', theme: 'dark' }))
If-Match
Parse, manipulate and stringify If-Match headers.
Implements Set<etag>.
import { IfMatch } from '@remix-run/headers'
let ifMatch = IfMatch.from(request.headers.get('if-match'))
ifMatch.tags
ifMatch.has('"67ab43"')
ifMatch.matches('"67ab43"')
ifMatch.matches('"abc123"')
let weak = IfMatch.from('W/"67ab43"')
weak.matches('W/"67ab43"')
ifMatch.add('"newetag"')
ifMatch.delete('"67ab43"')
headers.set('If-Match', ifMatch)
new IfMatch(['abc123', 'def456'])
let headers = new Headers({
'If-Match': new IfMatch(['"abc123"', '"def456"']),
})
headers.set('If-Match', new IfMatch(['"abc123"', '"def456"']))
If-None-Match
Parse, manipulate and stringify If-None-Match headers.
Implements Set<etag>.
import { IfNoneMatch } from '@remix-run/headers'
let ifNoneMatch = IfNoneMatch.from(request.headers.get('if-none-match'))
ifNoneMatch.tags
ifNoneMatch.has('"67ab43"')
ifNoneMatch.matches('"67ab43"')
let weak = IfNoneMatch.from('W/"67ab43"')
weak.matches('W/"67ab43"')
ifNoneMatch.add('"newetag"')
ifNoneMatch.delete('"67ab43"')
headers.set('If-None-Match', ifNoneMatch)
new IfNoneMatch(['abc123'])
let headers = new Headers({
'If-None-Match': new IfNoneMatch(['"abc123"']),
})
headers.set('If-None-Match', new IfNoneMatch(['"abc123"']))
If-Range
Parse, manipulate and stringify If-Range headers.
import { IfRange } from '@remix-run/headers'
let ifRange = IfRange.from(request.headers.get('if-range'))
ifRange.matches({ lastModified: 1609459200000 })
ifRange.matches({ lastModified: new Date('2021-01-01') })
let etagHeader = IfRange.from('"67ab43"')
etagHeader.matches({ etag: '"67ab43"' })
let empty = IfRange.from(null)
empty.matches({ etag: '"any"' })
new IfRange('"abc123"')
let headers = new Headers({
'If-Range': new IfRange('"abc123"'),
})
headers.set('If-Range', new IfRange('"abc123"'))
Range
Parse, manipulate and stringify Range headers.
import { Range } from '@remix-run/headers'
let range = Range.from(request.headers.get('range'))
range.unit
range.ranges
range.canSatisfy(2000)
range.canSatisfy(500)
range.normalize(2000)
let multi = Range.from('bytes=0-499, 1000-1499')
multi.ranges.length
let suffix = Range.from('bytes=-500')
suffix.normalize(2000)
new Range({ unit: 'bytes', ranges: [{ start: 0, end: 999 }] })
let headers = new Headers({
Range: new Range({ unit: 'bytes', ranges: [{ start: 0, end: 999 }] }),
})
headers.set('Range', new Range({ unit: 'bytes', ranges: [{ start: 0, end: 999 }] }))
Set-Cookie
Parse, manipulate and stringify Set-Cookie headers.
import { SetCookie } from '@remix-run/headers'
let setCookie = SetCookie.from(response.headers.get('set-cookie'))
setCookie.name
setCookie.value
setCookie.path
setCookie.httpOnly
setCookie.secure
setCookie.domain
setCookie.maxAge
setCookie.expires
setCookie.sameSite
setCookie.maxAge = 3600
setCookie.sameSite = 'Strict'
headers.set('Set-Cookie', setCookie)
new SetCookie('session_id=abc; Path=/; HttpOnly; Secure')
new SetCookie({
name: 'session_id',
value: 'abc',
path: '/',
httpOnly: true,
secure: true,
})
let headers = new Headers({
'Set-Cookie': new SetCookie({ name: 'session_id', value: 'abc', httpOnly: true }),
})
headers.set('Set-Cookie', new SetCookie({ name: 'session_id', value: 'abc', httpOnly: true }))
Vary
Parse, manipulate and stringify Vary headers.
Implements Set<headerName>.
import { Vary } from '@remix-run/headers'
let vary = Vary.from(response.headers.get('vary'))
vary.headerNames
vary.has('Accept-Encoding')
vary.size
vary.add('User-Agent')
vary.delete('Accept-Language')
headers.set('Vary', vary)
new Vary('Accept-Encoding, Accept-Language')
new Vary(['Accept-Encoding', 'Accept-Language'])
new Vary({ headerNames: ['Accept-Encoding', 'Accept-Language'] })
let headers = new Headers({
Vary: new Vary(['Accept-Encoding', 'Accept-Language']),
})
headers.set('Vary', new Vary(['Accept-Encoding', 'Accept-Language']))
Parse and stringify raw HTTP header strings.
import { parse, stringify } from '@remix-run/headers'
let headers = parse('Content-Type: text/html\r\nCache-Control: no-cache')
headers.get('content-type')
headers.get('cache-control')
stringify(headers)
Related Packages
License
See LICENSE