@wooksjs/event-http
!!! This is work-in-progress library, breaking changes are expected !!!

As a part of wooks
event processing framework, @wooksjs/event-http
implements http events and provides composables that let you:
- parse urls search params
- parse cookies
- parse request body (json, url-encoded, form, ...)
- serve files
The main ideas behind composable functions are:
- Never mutate request object (
req
). Accumulate a request context in a separate object(s) instead (wooks store
); - Never parse anything (cookies, body) before it is really requested by the request handler;
- Get rid of complex predefined data objects containing everything (cookies, headers, body, parsed body etc.) and use composable functions (hooks) instead;
- Get rid of tons of dependencies (middlewares) and implement everything that is needed for web app in a simple way.
Official Wooks HTTP composables:
Install
npm install wooks @wooksjs/event-http
Quick Start
import { useRouteParams } from 'wooks'
import { createHttpApp } from '@wooksjs/event-http'
const app = createHttpApp()
app.on('GET', 'hello/:name', () => `Hello ${ useRouteParams().get('name') }!`)
app.listen(3000, () => { console.log('Wooks Server is up on port 3000') })
Get More Control on Server
You can create http(s) server manually and pass server callback from the wooks http app:
import { useRouteParams } from 'wooks'
import { createHttpApp } from '@wooksjs/event-http'
import http from 'http'
const app = createHttpApp()
app.get('hello/:name', () => `Hello ${ useRouteParams().get('name') }!`)
const server = http.createServer(app.getServerCb())
server.listen(3000, () => { console.log('Wooks Server is up on port 3000') })
Quick Navigation
User Documentation
URL Parameters
To get access to URL parameters use composable function useRouteParams
import { useRouteParams } from 'wooks'
app.get('parametric/:param1/:param2/...', () => {
const { params, get } = useRouteParams()
console.log('param1=' + get('param1'))
console.log('param2=' + get('param2'))
console.log(params)
})
Query Parameters
To get access to Query parameters use composable function useSearchParams
import { useSearchParams } from '@wooksjs/event-http'
app.get('with-query', () => {
const { jsonSearchParams, urlSearchParams } = useSearchParams()
console.log('param1=' + urlSearchParams('param1'))
console.log('param2=' + urlSearchParams('param2'))
console.log(jsonSearchParams)
})
Request
To get a reference to the raw request instance use composable function useRequest
You probably don't need a rawRequest
unless you are developing some new feature. All the base use-cases covered with other composable functions.
import { useRequest } from '@wooksjs/event-http'
app.get('test', () => {
const { rawRequest } = useRequest()
})
Request Method and Headers
useRequest
provides some more shortcuts for useful data
import { useRequest } from '@wooksjs/event-http'
app.get('test', async () => {
const {
url,
method,
headers,
rawBody,
} = useRequest()
const body = await rawBody()
})
Request Cookies
Cookies are not parsed unless requested. Composable function useCookies
provides cookie getter and raw cookies string.
import { useCookies } from '@wooksjs/event-http'
app.get('test', async () => {
const {
rawCookies,
getCookie,
} = useCookies()
console.log(getCookie('session'))
})
Request Authorization
useAuthorization
function provides useful helpers for auth-headers:
import { useAuthorization } from '@wooksjs/event-http'
app.get('test', async () => {
const {
authorization,
authType,
authRawCredentials,
isBasic,
isBearer,
basicCredentials,
} = useAuthorization()
if (isBasic()) {
const { username, password } = basicCredentials()
console.log({ username, password })
} else if (isBearer()) {
const token = authRawCredentials
console.log({ token })
} else {
}
})
Request Body Parser
More details here
Response
The easiest way to respond to the request is to return some value from handler function like this:
app.get('string_response', () => {
return 'hello world!'
})
Whatever is returned from the handler is the response. Content-Type
and Content-Length
headers will be calculated accordingly.
If a handler returns a json object, it will be stringified and the header Content-Type
will be set to application/json
automatically:
app.get('json_response', () => {
return { value: 'hello world!' }
})
Supported response types:
- string (text/plain, text/html, application/xml - depending on the content)
- object/array (application/json)
- boolean (text/plain)
- readable stream (you must specify
Content-Type
and Content-Length
headers yourself) - fetch (native) response (streaming body to client response)
Raw Response: When it is needed to take the full control of the response, use composable function useResponse
When you get a raw response instance you take away the control of the response on yourself. The framework will not process the output of the handler in this case.
An example of using raw response instance:
import { useResponse } from '@wooksjs/event-http'
app.get('test', () => {
const { rawResponse } = useResponse()
const res = rawResponse()
res.writeHead(200, {})
res.end('ok')
})
If you don't want to take away a responsibility for the response but still need a raw response instance you can use { passthrough: true }
as an argument.
The next example does the same thing as the previous example using passthrough
options:
import { useResponse } from '@wooksjs/event-http'
app.get('test', () => {
const { rawResponse } = useResponse()
const res = rawResponse({ passthrough: true })
return 'ok'
})
A function useSetHeaders
provides variety of response headers helpers:
import { useSetHeaders, contentTypes } from '@wooksjs/event-http'
app.get('test', async () => {
const {
setHeader,
removeHeader,
setContentType,
headers,
enableCors,
} = useSetHeaders()
setContentType(contentTypes.application.json)
setHeader('server', 'myServer v1.0')
enableCors()
return '{ "value": "OK" }'
})
Another hook for set header
import { useSetHeader } from '@wooksjs/event-http'
app.get('test', async () => {
const server = useSetHeader('server')
server.value = 'myServer v1.0'
})
Response Cookies
A function useSetCookies
provides variety of set-cookie helpers:
import { useSetCookies } from '@wooksjs/event-http'
app.get('test', async () => {
const {
setCookie,
removeCookie,
clearCookies,
cookies,
} = useSetCookies()
setCookie('session', 'value', {
expires: '2029-01-01',
maxAge: '1h',
domain: 'my-domain',
path: '/home',
secure: true,
httpOnly: false,
sameSite: true,
})
})
Another hook for set-cookie
import { useSetCookie } from '@wooksjs/event-http'
app.get('test', async () => {
const session = useSetCookie('session')
session.value = 'value'
session.attrs = {
expires: '2029-01-01',
maxAge: '1h',
domain: 'my-domain',
path: '/home',
secure: true,
httpOnly: false,
sameSite: true,
}
})
Response Status
It's possible to control the response status via status
function that is available in useResponse()
import { useResponse } from '@wooksjs/event-http'
app.get('test', async () => {
const { status } = useResponse()
status(201)
console.log(status())
return 'response with status 201'
})
Cache-Control
useSetCacheControl
function provides helpers for headers responsible for cache control
import { useSetCacheControl } from '@wooksjs/event-http'
app.get('static/*', () => {
const {
setAge,
setExpires,
setPragmaNoCache,
setCacheControl,
} = useSetCacheControl()
setAge('2h 15m')
setExpires('2022-05-05')
setCacheControl({
mustRevalidate: true,
noCache: false,
noStore: false,
noTransform: true,
public: true,
private: 'field',
proxyRevalidate: true,
maxAge: '3h 30m 12s',
sMaxage: '2h 27m 54s',
})
})
Proxy Requests
More details here
Serve File
More details here
Create you own hooks
As an example we'll create a composable that resolves user profile
import { useAuthorization, useHttpContext } from '@wooksjs/event-http'
interface TUser {
username: string
age: number
}
export function useUserProfile() {
const { store } = useHttpContext<{ user: TUser }>()
const user = store('user')
const { basicCredentials } = useAuthorization()
const username = basicCredentials()?.username
async function userProfile() {
if (!user.value) {
user.value = await readUser()
}
return user.value
}
function readUser(): Promise<TUser> {
}
return {
username,
userProfile,
}
}
app.get('/user', async () => {
const { username, userProfile } = useUserProfile()
console.log('username =', username)
const data = await userProfile()
return { user: data }
})
Example of custom set header hook
import { useSetHeaders } from '@wooksjs/event-http'
import { attachHook } from '@wooksjs/event-core'
function useHeaderHook(name: string) {
const { setHeader, headers } = useSetHeaders()
return attachHook({
name,
type: 'header',
}, {
get: () => headers()[name] as string,
set: (value: string | number) => setHeader(name, value),
})
}
app.get('/test', () => {
const myHeader = useHeaderHook('x-my-header')
myHeader.value = 'header value'
return 'ok'
})
Adapter Documentation
The next paragraph is for those who wants to create threir own adapter and get more control over the wooks.
Create a new wooks context
Wooks context is a special object that stores:
- instance of request - is used to get data from request
- instance of response - is used to send a response
- parsed route params - usually Web App frameworks put it to
req
(req.params
) - cache store - is used to cache alread computed values, e.g. parsed body, parsed cookies, ..., in order to avoid multiple parsing/computetaion of the same entities.
When request is generated by the client a new Wooks context must be created.
const {restoreCtx, clearCtx} = createHttpContext({ req, res })
import { createHttpContext } from '@wooksjs/event-http'
function requestHandler(req, res) {
const {
restoreCtx,
clearCtx,
store,
} = createHttpContext({
req,
res,
})
store('routeParams').value = { name: 'value' }
}
Create a responder
Responder is a function that takes any output of the request handler and transforms it into http response, processing headers, cookies, formats etc.
const {createResponse, respond} = createWooksResponder()
import { createWooksResponder, createHttpContext } from '@wooksjs/event-http'
const {createResponse, respond} = createWooksResponder(
)
async function requestHandler(req, res) {
const {restoreCtx, clearCtx} = createHttpContext({ req, res })
const response = await createHttpContext()
restoreCtx()
respond(response)
clearCtx()
}
async function processHandlers() {
}
Restore Context
There is one more way to get restoreCtx
hook besides the one you get when createHttpContext
const { restoreCtx, clearCtx } = useHttpContext()
import { useHttpContext } from '@wooksjs/event-http'
async function someHandler() {
const { restoreCtx, clearCtx } = useHttpContext()
await ...
restoreCtx()
}