- v0.24.1Latest
- v0.24.0
- v0.23.1
- v0.23.0
- v0.22.0
- v0.21.1
- v0.21.0
- v0.20.1
- v0.20.0
- v0.19.0
- v0.18.1
- v0.18.0
- v0.17.0
- v0.16.0
- v0.15.1
- v0.15.0
- intercept-v0.14.1
- v0.14.0
- v0.13.0
- v0.5.0
- v0.4.0
- v0.3.0
- v0.2.0
- v0.1.1
- v0.1.0
- v0.0.27
- v0.0.26
- v0.0.25
- v0.0.24
- v0.0.23
- v0.0.22
- v0.0.21
- v0.0.20
- v0.0.19
- v0.0.18
- v0.0.17
- v0.0.16
- v0.0.15
- v0.0.14
- v0.0.13
- v0.0.12
- v0.0.11
- v0.0.10
- v0.0.9
- v0.0.8
- v0.0.7
- v0.0.6
- v0.0.5
- v0.0.4
- v0.0.3
- v0.0.2
- v0.0.1
HTTP Functions for Deno
This is a collection of functions to aid building of a HTTP service in Deno.
They can be used instead of a monolithic router framework, or in tandem with one.
The bullet points
- A library of composable functions rather than a monolithic router class
- Based on web standard Request => Response functions
- Works with
Deno.serve
- 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
An Example
Letās start with a really simple example, a router for GET /hello
ā¦
import { handle } from "https://deno.land/x/http_fns/lib/handle.ts";
import { byPattern } from "https://deno.land/x/http_fns/lib/by_pattern.ts";
import { byMethod } from "https://deno.land/x/http_fns/lib/by_method.ts";
Deno.serve(handle([
byPattern(
"/hello",
byMethod({
GET: () => {
return new Response("Hello world");
},
}),
),
]));
As you can see this is a fairly difference approach to the routers you may be used to.
The main idea is to compose your router from simple Request
=> Response
functions.
Letās take a look at each part of the example, starting with byPattern
(weāll
come back to handle
later):
byPattern(
"/hello",
...
)
This function actually creates a handler function, which attempts to match the request URL against the given pattern, and if it matches calls the handler given in its 2nd arg, in this caseā¦
byMethod({
GET: ...
})
Again this creates another handler function, which attempts to match the request HTTP method against a key in the given record of method => handlers. If it matches the HTTP method, the associated handler is calledā¦
() => {
return new Response("Hello world");
}
So, this will be the handler function for GET /hello
, the function is passed
the Request
and returns a Response
.
But what if the user hits GET /other
, and byPattern
doesnāt match the
pattern?
Well the function can return null
to indicate that this request cannot be
handled, and this is where handle
comes in. It can take an array of handlers,
and try each one until a non-null response is returned, and if no response comes
a fallback handler is invoked. By default returning a 404 Not Found
, but a
different fallback function can be supplied where necessary.
Although handle
itself is just a convenience function for a common combination
of cascade
and withFallback
functions, which can be used independently for a
more flexible approach. See below for the full documentation of these.
You can read more about the concepts in the blog, although it may not remain totally up to date with the state of this library.
Common handler concepts
Response or null
Although based on Request
=> Response
functions, there is a little more to
it than that, for example, many functions produced by the by*
helpers may also
return null
instead of a Response
, and where a regular handler is required
the withFallback
function can be used to catch the null
and return a
concrete Response
. The Response
or null
may also be as a promise.
Additional arguments
Also, many handlers may also accept additional arguments beyond the first
Request
, for example, in the case of byPatten(pattern, handler)
, the
handler
is given the request and the pattern match result as argumentsā¦
((request: Request, match: URLPatternResult) => Awaitable<Response | null>);
Argument shunting
Most of the by*
helpers will pass arguments on as is, or shunt the arguments
along if they want to introduce their own, so for example,
byPattern(pattern, handler)
returns a handler with the type:
(request: Request, ...additionalArgs: SomeArgsType) => ...
but the actual handler
you pass to it is actuallyā¦
(request: Request, match: URLPatternResult, ...additionalArgs: SomeArgsType) => ...
It has the extra match
argument insert before all other arguments that are
just passed along.
This allows the handlers created via by*
helpers to work with a wide variety
of other frameworks, as itās totally agnostic to the extra arguments beyond the
Request
. So when you use these functions with Deno.serve
for example, your
pattern handler function will actually have the signature:
(request: Request, match: URLPatternResult, info: Deno.ServeHandlerInfo) => ...
So you still have this extra context available to you in your handler.
Take a look inside
This is just a library of functions, and these are kept as simple a possible with the intention that it is easy to take a look inside of each function and see exactly what it does, the documentation below links to the source of each function and an example of itās usage, and I encourage you follow these and take a good look at the code.
Also, in general each module represents a single function, intended to be
imported individually, so you only import exactly what you need, not a mountain
of unused features. Youāll find no mod.ts
or deps.ts
around here.
Examples
There are many examples that can be executed directly, and many tests for these 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
Many of the examples have accompanying tests, which I hope to improve coverage of as time permits. Iād encourage you to take a look at the tests to see how each example can be exercised. You can also run the whole test suite simply with:
deno task test
Functions
- Routing
- Criteria
byPattern
bySubPattern
byMethod
byMediaType
- Delegation
handle
cascade
withFallback
lazy
- Handlers
staticRoute
- Filesystem
discoverRoutes
dynamicRoute
generateRoutesModule
- Fresh compatibility (TODO)
- Criteria
- Interceptors
intercept
interceptResponse
skip
byStatus
logging
cors
- Utilities
- Hosting (TODO)