NOTE: This is still fairly experimental.
A collection of functions for HTTP.
- Based on Request => Response functions
- Works with
Deno.serve
- Handlers for routing based on various criteria
- URLPattern
- Method
- Media Type
- Request and Response helper functions
- Generate router module from filesystem based handlers
- Static or dynamic imports
- Build time or runtime discovery
- Request/Response interceptor function chains
- Logging
- CORS
Deno.serve
options helper fns for various hosting scenarios- Development on localhost (including https support)
- Deno Deploy
Read the blog.
See the examples.
You can run them after cloning this repo, for example:
deno run -A --import-map=examples/import_map.json examples/logging.ts
or (using a task defined in the deno.json file)
deno task example examples/logging.ts
(NOTE: The above will map the imports to use the local http_fns modules rather than fetching from deno.land)
or directly from deno.land:
deno run -A https://deno.land/x/http_fns/examples/logging.ts
or directly from GitHub:
deno run -A https://raw.githubusercontent.com/jollytoad/deno_http_fns/main/examples/logging.ts
Most functions could be considered as handler factories, in that they create and return a Request handler function, generally of the form:
(req: Request, data: unknown) => Response
Response can also be in a Promise, and in many case may also be null
to
indicate that the Request cannot be handled and it should be delegated to
another handler.
Here is a very simple example of how the functions can be composed into a server:
await serve(
handle([
byPattern(
"/",
byMethod({
GET: () => ok("Hello"),
}),
),
byPattern(
"/foo",
byMethod({
GET: () => ok("Foo"),
}),
),
]),
);
handle(handlers, fallback) => Handler
This is the top-level function you'll use to form a router.
You pass it a list of handlers, each handler may return either a Response
or a
null
. If the handler returns null
, the next handler is called until a
Response
is returned, or it will end by calling the optional fallback
handler which must return a Response
.
The default fallback is to return a 404 Not Found
response.
(handle
is actually just a shortcut for cascade
& withFallback
, discussed
later)
byPattern(pattern, handler) => Handler
Every router needs a way to delegate by actual path or URL. byPattern
provides
that using the standard
URLPattern.
It can take a pattern or array of patterns, and the handler to be called on a successful match.
The pattern can be a string (to match just the path), a URLPatternInit
which
can declare patterns for other parts of the URL, or a pre-constructed
URLPattern
itself.
The handler created will attempt to match the Request URL against each given
pattern in order until one matches, and then call the delegate handler (passed
in the 2nd arg of byPattern), with the Request and the URLPatternResult
:
(req: Request, match: URLPatternResult) => Response | null | Promise<Response | null>
If no pattern matches, the handler returns null
, allowing the request to
cascade to the next handler in the array of handlers passed to handle
(or
cascade
).
bySubPattern(pattern, handler) => Handler
Match a child route pattern after already matching a parent pattern.
byMethod({ METHOD: handler }, fallback) => Handler
Select a handler based on the request method.
byMediaType({ "media/type": handler }, fallbackExt, fallbackAccept) => Handler
Select the most appropriate handler based on the desired media-type of the request.
cascade(...handlers) => Handler
Attempt each handler in turn until one returns a Response.
withFallback(handler, fallback) => Handler
Provide a fallback Response should the handler 'skip' (ie. return no response).
lazy(module url or loader) => Handler
Dynamically load a handler when first required.
staticRoute(pattern, fileRootUrl, options) => Handler
Serve static files.
intercept(handler, ...interceptors) => Handler
Modify the Request and/or Response around the handler, and handle errors.
interceptResponse(handler, ...responseInterceptors) => Handler
Modify the Response from a handler.
skip(...status) => ResponseInterceptor
Use with interceptResponse
to convert Responses of the given status to a
'skipped' response.
byStatus(status, interceptor) => ResponseInterceptor
Create a Response Interceptor that matches the status of the Response.
logging() => Interceptors
A set of standard logging interceptors.
cors(options) => ResponseInterceptor
A response intercept that adds the appropriate CORS headers, and handles the OPTIONS request.
Walk the filesystem discovering potential routes and handlers modules.
Module | Example script | Example of generated routes | Example router
Generate a TypeScript module that exports a routing handler of discovered
modules, using byPattern
.
dynamicRoute(options) => Handler
A handler that performs route discovery dynamically at runtime.
TODO
TODO