Skip to content

Request Composables ​

Composables for reading incoming HTTP request data: headers, query parameters, cookies, authorization, client IP, body size limits.

Raw Request Instance ​

To get a reference to the raw request instance, you can use the useRequest composable function. However, in most cases, you won't need to directly access the raw request instance unless you're developing a new feature or require low-level control over the request.

js
import { useRequest } from '@wooksjs/event-http'

app.get('/test', () => {
    const { raw } = useRequest()
    // Access the raw request instance if needed
})

URI Parameters ​

URI parameters are automatically parsed by the router and are covered in the Retrieving URI Parameters section.

Query Parameters ​

The useUrlParams composable provides three functions for working with query parameters:

  • params() — returns an instance of WooksURLSearchParams, which extends the standard URLSearchParams with a toJson method that returns a JSON object of the query parameters.
  • toJson() — is a shortcut for params().toJson(), returning the query parameters as a JSON object.
  • raw() — returns the raw search parameter string, such as ?param1=value&....
js
import { useUrlParams } from '@wooksjs/event-http'

app.get('hello', () => {
    const { params, toJson, raw } =
        useUrlParams()

    // curl http://localhost:3000/hello?name=World
    console.log(toJson()) // { name: 'World' }
    console.log(raw()) // ?name=World

    return `Hello ${params().get('name')}!`
})

Example usage with cURL:

bash
curl http://localhost:3000/hello?name=World
# Hello World!

Arrays and safe toJson() conversion ​

params() returns a WooksURLSearchParams — a subclass of the standard URLSearchParams whose only addition is a typed toJson<T>() method. The standard accessors (get, getAll, has, entries, …) work exactly as in Node's URLSearchParams.

toJson() follows a few rules that protect against malformed or hostile query strings:

  • Arrays use a trailing []. A key ending in [] collects all its values into a string[], and the [] is kept in the resulting key. So ?tags[]=a&tags[]=b becomes { 'tags[]': ['a', 'b'] }.
  • Repeated plain keys throw. A non-[] key that appears more than once raises HttpError(400) (Duplicate key). Use the [] form when you expect multiple values; if you need the raw repeated values without the array convention, read them with params().getAll('key') instead of toJson().

toJson() also rejects the prototype-pollution keys __proto__, constructor, and prototype with HttpError(400), and builds a null-prototype object.

js
// ?status=open&tags[]=urgent&tags[]=api
const query = useUrlParams().toJson()
// { status: 'open', 'tags[]': ['urgent', 'api'] }

Method and Headers ​

The useRequest composable provides additional shortcuts for accessing useful data related to the request, such as the URL, method, headers, and the raw request body.

js
import { useRequest } from '@wooksjs/event-http'

app.get('/test', async () => {
    const {
        url, // Request URL (string)
        method, // Request method (string)
        headers, // Request headers (object)
        rawBody, // Request body (() => Promise<Buffer>)
        reqId, // Per-event UUID (() => string)
        isCompressed, // Whether the body is gzip/deflate/br compressed (() => boolean)
    } = useRequest()

    const body = await rawBody() // Body as a Buffer
})

reqId() returns a stable per-event UUID — the same value as useEventId().getId() from @wooksjs/event-core.

useHeaders() is a shortcut that returns the request headers record directly (IncomingHttpHeaders, lowercased names) — equivalent to useRequest().headers:

js
import { useHeaders } from '@wooksjs/event-http'

app.get('/test', () => {
    const { 'content-type': contentType } = useHeaders()
})

Client IP ​

useRequest() provides two helpers for resolving the client's IP address:

js
import { useRequest } from '@wooksjs/event-http'

app.get('/test', () => {
    const { getIp, getIpList } = useRequest()

    getIp() // Socket remote address
    getIp({ trustProxy: true }) // First `x-forwarded-for` entry, falling back to the socket address

    getIpList()
    // {
    //     remoteIp: '...',    // socket remote address
    //     forwarded: ['...'], // all `x-forwarded-for` entries
    // }
})

By default getIp() returns the socket's remote address and ignores x-forwarded-for, which any client can spoof. Pass { trustProxy: true } only when your app runs behind a trusted reverse proxy that sets that header.

Cookies ​

Cookies are not automatically parsed unless requested. The useCookies composable function provides a cookie getter and access to the raw cookies string.

js
import { useCookies } from '@wooksjs/event-http'

app.get('/test', async () => {
    const {
        raw, // Raw "cookie" from headers (string | undefined)
        getCookie, // Cookie getter ((name) => string | null)
    } = useCookies()

    console.log(getCookie('session'))
    // Prints the value of the cookie with the name "session"
})

Authorization ​

The useAuthorization function provides helpers for working with authorization headers:

js
import { useAuthorization } from '@wooksjs/event-http'

app.get('/test', async () => {
    const {
        authorization, // The raw value of the "authorization" header (string | undefined)
        type, // The authentication type (Bearer/Basic) (() => string | null)
        credentials, // The credentials that follow the auth type (() => string | null)
        is, // Checks auth type: is('basic'), is('bearer'), etc. ((type) => boolean)
        basicCredentials, // Parsed basic auth credentials (() => { username, password } | null)
    } = useAuthorization()

    if (is('basic')) {
        const { username, password } = basicCredentials()
        console.log({ username, password })
    } else if (is('bearer')) {
        const token = credentials()
        console.log({ token })
    } else {
        // Unknown or empty authorization header
    }
})

Note: authorization is a plain string value. All other properties are lazy functions — they compute on first call and cache the result. The is() argument is typed as KnownAuthType ('basic' | 'bearer'), but any other string is also accepted.

Accept Header ​

The useAccept composable checks the request's Accept header for MIME type support:

js
import { useAccept } from '@wooksjs/event-http'

app.get('/test', () => {
    const { accept, has } = useAccept()

    // Use short names for common types
    if (has('json')) {
        return { data: 'json response' }
    } else if (has('html')) {
        return '<p>html response</p>'
    }

    // Or full MIME types for anything else
    if (has('image/webp')) {
        // ...
    }
})

Short names: 'json' (application/json), 'html' (text/html), 'xml' (application/xml), 'text' (text/plain) — typed as KnownAcceptType. Full MIME strings are also accepted.

The accept property returns the raw Accept header value.

Body Size Limits ​

Request body reading is protected by configurable limits:

LimitDefaultDescription
maxCompressed1 MBMax compressed body size in bytes
maxInflated10 MBMax decompressed body size in bytes
maxRatio100Max compression ratio (zip-bomb protection)
readTimeoutMs10 000 msBody read timeout (0 disables the timeout)

The default values are exported as DEFAULT_LIMITS from @wooksjs/event-http.

rawBody() transparently decompresses gzip, deflate, and br request bodies (including stacked encodings) and always resolves to the decompressed Buffer, cached across calls — that is why the maxInflated and maxRatio limits exist.

App-level configuration ​

js
import { createHttpApp } from '@wooksjs/event-http'

const app = createHttpApp({
    requestLimits: {
        maxCompressed: 50 * 1024 * 1024,  // 50 MB
        maxInflated: 100 * 1024 * 1024,   // 100 MB
        maxRatio: 200,
        readTimeoutMs: 30_000,
    },
})

Per-request overrides ​

Override limits inside a handler before reading the body:

js
import { useRequest } from '@wooksjs/event-http'

app.post('/upload', async () => {
    const {
        setMaxCompressed,
        setMaxInflated,
        setMaxRatio,
        setReadTimeoutMs,
        rawBody,
    } = useRequest()

    // Raise limits for this route only
    setMaxCompressed(50 * 1024 * 1024)  // 50 MB
    setMaxInflated(100 * 1024 * 1024)   // 100 MB

    const body = await rawBody()
    return { size: body.length }
})

Per-request setters use copy-on-write — they do not mutate the app-level configuration. Each setter has a matching getter (getMaxCompressed(), getMaxInflated(), getMaxRatio(), getReadTimeoutMs()) returning the currently effective value.

What happens on violation ​

When a limit is violated, rawBody() throws an HttpError that the framework renders automatically as an error response:

ConditionError
Body exceeds maxCompressed (or maxInflated when uncompressed)413 Payload Too Large
Decompressed body exceeds maxInflated413 Inflated body too large
Compression ratio exceeds maxRatio413 Compression ratio too high
Unsupported Content-Encoding415 Unsupported Content-Encoding
No data received for readTimeoutMs (the timer resets on every received chunk)408 Request body timeout

Body Parser ​

The implementation of the body parser is isolated into a separate package called @wooksjs/http-body. For more details on using the body parser, refer to the Body Parser section.

Released under the MIT License.