Implementation of the factories defined by chubbyts/chubbyts-http-types, inspired by PSR-7, PSR-15 and PSR-17.
A message body is an AsyncIterable<Uint8Array>, the factories create Node.js Readable streams (which are one),
getQueryParams parses query strings with qs.
| Factory | Creates | Dependencies (with defaults) |
|---|---|---|
createUriFactory |
UriFactory |
|
createStreamFactory |
StreamFactory |
|
createStreamFromFileFactory |
StreamFromFileFactory |
|
createRequestFactory |
RequestFactory |
UriFactory, StreamFactory |
createServerRequestFactory |
ServerRequestFactory |
RequestFactory |
createResponseFactory |
ResponseFactory |
StreamFactory |
| Helper factory | Creates | Dependencies (with defaults) |
|---|---|---|
createMessageHelpers |
MessageHelpers |
|
createRequestHelpers |
RequestHelpers |
MessageHelpers |
createServerRequestHelpers |
ServerRequestHelpers |
RequestHelpers |
createResponseHelpers |
ResponseHelpers |
MessageHelpers |
createUriHelpers |
UriHelpers |
- node: 22
- chubbyts/chubbyts-http-types: ^4.0.0
- qs: ^6.17.0
Through NPM as @chubbyts/chubbyts-http.
npm i @chubbyts/chubbyts-http@^3.0.0import { createUriFactory } from '@chubbyts/chubbyts-http/message-factory';
const uriFactory = createUriFactory();
const uri = uriFactory(
'https://user:password@localhost:10443/api?key1=value1&key2[]=value2&key3[key31]=value3#fragment',
);
// {
// scheme: 'https',
// userInfo: 'user:password',
// host: 'localhost',
// port: 10443,
// path: '/api',
// query: 'key1=value1&key2[]=value2&key3[key31]=value3',
// fragment: 'fragment',
// }import { text } from 'node:stream/consumers';
import { createStreamFactory, createStreamFromFileFactory } from '@chubbyts/chubbyts-http/message-factory';
const streamFactory = createStreamFactory();
const streamFromFileFactory = createStreamFromFileFactory();
const stream = streamFactory('content');
const streamFromFile = streamFromFileFactory('/path/to/file'); // throws if there is no file at this path
await text(stream); // 'content'Any Node.js stream (like an IncomingMessage) or web ReadableStream already is a body, it needs no factory. Errors
while reading (e.g. a failing file read) get thrown while iterating the body.
import { createRequestFactory, createServerRequestFactory } from '@chubbyts/chubbyts-http/message-factory';
const requestFactory = createRequestFactory();
const serverRequestFactory = createServerRequestFactory();
const request = requestFactory('GET', 'https://localhost/api');
const serverRequest = serverRequestFactory('GET', 'https://localhost/api'); // with empty attributesRequests and responses get created with protocol version 1.1, no headers and an empty body. Each request gets its
own signal, which never gets aborted: a bridge replaces it with one it aborts when the client disconnects (e.g. with
requestHelpers.withSignal).
The reason phrase falls back to the one of STATUS_CODES:
import { createResponseFactory } from '@chubbyts/chubbyts-http/message-factory';
const responseFactory = createResponseFactory();
const response = responseFactory(404); // reasonPhrase: 'Not Found', empty body
const customResponse = responseFactory(404, 'Gone Fishing'); // reasonPhrase: 'Gone Fishing'The body gets set with the response helpers, e.g. responseHelpers.withBody(response, streamFactory('content')).
The helpers follow the rules of chubbyts/chubbyts-http-types, getQueryParams and withQueryParams use qs
with its defaults (keys changing the prototype get ignored, depth 5, 20 array items, 1000 parameters).
import {
createResponseHelpers,
createServerRequestHelpers,
createUriHelpers,
} from '@chubbyts/chubbyts-http/message-helper';
const serverRequestHelpers = createServerRequestHelpers(); // contains the request and message helpers
const responseHelpers = createResponseHelpers(); // contains the message helpers
const uriHelpers = createUriHelpers();
serverRequestHelpers.getHeaderLine(serverRequest, 'Accept'); // 'application/json, text/html'
const responseWithStatus = responseHelpers.withStatus(response, 404); // reasonPhrase: 'Not Found'
const responseWithHeader = responseHelpers.withHeader(responseWithStatus, 'Content-Type', 'application/json');
uriHelpers.toString(uriHelpers.withPath(uri, '/a b')); // 'https://localhost/a%20b'
serverRequestHelpers.getCookieParams(serverRequest); // { session: 'abc' }
serverRequestHelpers.getQueryParams(serverRequest); // { key1: 'value1', key2: ['value2'], key3: { key31: 'value3' } }
serverRequestHelpers.withQueryParams(serverRequest, { key: ['value'] }); // uri.query: 'key%5B0%5D=value'
const responseWithCookie = responseHelpers.withCookie(response, '__Host-session', 'abc', {
path: '/',
secure: true,
httpOnly: true,
sameSite: 'Lax',
}); // set-cookie: __Host-session=abc; Path=/; Secure; HttpOnly; SameSite=Lax2026 Dominik Zogg