Skip to main content

Router

Module Overview

Designed with familiarity in mind, the server-side routing API is inspired by Express.js, React Router, and the native Cloudflare Workers platform.

import { RequestRouter } from 'keywork/router'

const app = new RequestRouter()

app.get('/', () => 'Hello there! 👋')

export default app

Creating a RESTful API

Instances of RequestRouter define each route handler by invoking methods that correspond with HTTP method of the same name:

HTTP MethodUsage
'GET'app.get([path pattern], [RouteRequestHandler])
'POST'app.post([path pattern], [RouteRequestHandler])
'PATCH'app.patch([path pattern], [RouteRequestHandler])
'DELETE'app.delete([path pattern], [RouteRequestHandler])
'HEAD'app.head([path pattern], [RouteRequestHandler])
'OPTIONS'app.options([path pattern], [RouteRequestHandler])
'*'app.all([path pattern], [RouteRequestHandler])

GET (app.get([path pattern], [...RouteRequestHandler]))

GET http://localhost:8788
app.get('/', () => 'Hello there! 👋')
// Hello there! 👋
GET http://localhost:8788/greet/jessie
app.get('/greet/:firstName', ({ params }) => `Hello there, ${params.firstName}!`)
// Hello there, Jessie!
GET http://localhost:8788/datetime
app.get('/datetime', () => `The current datetime is: ${new Date().toLocaleTimeString()}`)
// The current datetime is: 11:35:00 AM

POST (app.post([path pattern], [...RouteRequestHandler]))

POST http://localhost:8788/users
interface NewUserPayload {
displayName: string
email: string
}

app.post('/users', async ({ request }) => {
const user: NewUserPayload = await request.json()

//...
})

Path Parameters

Routes are defined with a path-to-regexp style path patterns.

app.get('/users/', ...)
app.post('/users/', ...)

app.get('/users/:userID/', ...)
app.get('/users/:userID/friends/', ...)
app.get('/articles/:articleID', ...)

Path matching is implemented via the JavaScript native URLPattern

danger

You may need a polyfill if your app uses on a runtime that hasn't yet added URLPattern class.

Learn more from the URI Module

IsomorphicFetchEvent

When creating a RouteRequestHandler callback, you have access to an IsomorphicFetchEvent:

GET http://localhost:8788
app.get('', (event) => {
const {
request,
params
env,
data,
originalURL
} = event

return 'Hello there! 👋'
})

IsomorphicFetchEvent.request

The incoming request received by the V8 runtime.

IsomorphicFetchEvent<ExpectedParams>.params

Parameters parsed from the incoming request's URL and the route's pattern.

This can be made type-safe by providing a generic type when defining the route:

GET http://localhost:8788/users/cambria
interface UserProps {
userID: string
}

app.get<UserProps>('/users/:userID', (event) => {
const { userID } = event.params
})

IsomorphicFetchEvent.env

The bound environment aliases. Bound environment aliases are mostly limited to Cloudflare Workers, and are usually defined in your wrangler.toml file.

Node.js

This is similar to process.env.

IsomorphicFetchEvent.data

Optional extra data to be passed to a route handler, usually from middleware.

IsomorphicFetchEvent.originalURL

The original request URL, unmodified by Keywork's middleware logic.

Middleware

Similar to Express.js's concept of Middleware, route handlers have access to a next function to pass a request off to the next route handler matching the URL pattern.

next can also be called after checking for some criteria, such as if the user has authenticated:

Check if a user is allowed to view a page
const authenticationRouter = new RequestRouter()

authenticationRouter.all('*',
(event, next) => {
if (!isAuthenticated(event.request)) {
throw new KeyworkResourceError("You need to be logged in to view that!", Status.Forbidden)
}

return next()
})

app.use(authenticationRouter)
app.get('/profile/settings', () => {...})

Overrides

Providing a request argument will override the path param parsing within RequestRouter. This can be useful if your middleware needs to modify or omit some request information before reaching the next route handler.

Additional Perks

The RequestRouter class also provides some small quality-of-life improvements over the low-level APIs of the Workers platform.

Automatic Response Parsing

Keywork will automatically infer the appropriate Response for the return type of your RouteHandler, allowing you to skip the ceremony of constructing Response with the appropriate headers

However, this behavior can be avoided by explicitly providing a Response object, or a class that extends from Response such as...

  • Keywork#HTTP/Response.CachableResponse CachableResponse
  • Keywork#HTTP/Response.HTMLResponse HTMLResponse
  • Keywork#HTTP/Response.JSONResponse JSONResponse
  • Keywork#HTTP/Response.ErrorResponse ErrorResponse

Errors

Errors in your code are caught before they crash the runtime. See KeyworkResourceError for further details.

Sessions

Support for cookie-based sessions is automatically handled. See Keywork#Session.SessionMiddleware SessionMiddleware for further details.

Further reading

Keyword

Router

Keyword

Routing

Keyword

Cloudflare Pages

Keyword

Cloudflare Workers

Keyword

Deno

Usage

import * as Router from 'keywork/router'

Router Classes

Options Interfaces

Other Interfaces

Request Interfaces

Request Handler Interfaces

Options Type Aliases

Other Type Aliases

Request Handler Functions