Reno
Reno is a thin routing library designed to sit on top of Deno's standard HTTP module.
Overview
import { listenAndServe } from "https://deno.land/std@v0.58.0/http/server.ts";
import {
createRouter,
AugmentedRequest,
createRouteMap,
textResponse,
jsonResponse,
streamResponse,
} from "https://deno.land/x/reno@v1.0.0-alpha.3/reno/mod.ts";
export const routes = createRouteMap([
["/home", () => textResponse("Hello world!")],
// Supports RegExp routes for further granularity
[/^\/api\/swanson\/?([0-9]?)$/, async (req: AugmentedRequest) => {
const [quotesCount = "1"] = req.routeParams;
const res = await fetch(
`https://ron-swanson-quotes.herokuapp.com/v2/quotes/${quotesCount}`,
);
return jsonResponse(await res.json());
}],
// Supports Reader for streaming responses in chunks
["/streamed-response", () => streamResponse(
new ReactReader(<App />),
)],
]);
const notFound = (e: NotFoundError) => createErrorResponse(404, e);
const serverError = (e: Error) => createErrorResponse(500, e);
const mapToErrorResponse = (e: Error) =>
e instanceof NotFoundError ? notFound(e) : serverError(e);
const router = createRouter(routes);
(async () => {
console.log("Listening for requests...");
await listenAndServe(
":8001",
async (req: ServerRequest) => {
try {
const res = await router(req);
return req.respond(res);
} catch (e) {
return req.respond(mapToErrorResponse(e));
}
},
);
})();
Key Features
Responses are just Data Structures
This, along with request handlers being pure functions, makes unit testing Reno services a breeze:
import { jsonResponse, assertResponsesMatch } from "https://deno.land/x/reno@v1.0.0-alpha.3/reno/mod.ts";
import { createRonSwansonQuoteHandler } from "./routes.ts";
const createFetchStub = (response: string[]) =>
sinon.stub().resolves({
json: sinon.stub().resolves(response),
});
test({
name: "ronSwansonQuoteHandler should fetch a quote from an API and return it",
async fn() {
const quotes = ["Some Ron Swanson Quote"];
const fetchStub = createFetchStub(quotes);
const ronSwansonQuoteHandler = createRonSwansonQuoteHandler(fetchStub);
const req = {
routeParams: []
};
const response = await ronSwansonQuoteHandler(req);
assertResponsesMatch(response, jsonResponse(quotes, {
"X-Foo": "bar"
}));
}
});
Nested Routers
Like most other HTTP routing libraries that you know and love, Reno supports nested routers; you can use wildcard suffixes ("*"
) to group routers by a common path segment:
const routes = createRouteMap([
[
"/foo/*",
createRouter(
createRouteMap([
[
"/bar/*",
createRouter(createRouteMap([["/baz", () =>
textResponse("Hello from a nested route!")]])),
],
]),
),
],
]);
const router = createRouter(routes);
pipe()
- An Alternative to Middleware
Reno emulates the middleware pattern, found in Express, by favouring function piping to create reusable, higher-order route handlers:
import { createRouteMap, jsonResponse, pipe } from "https://deno.land/x/reno@v1.0.0-alpha.3/reno/mod.ts";
const withCaching = pipe(
(req, res) => {
/* Mutate the response returned by
* the inner route handler... */
res.headers.append("Cache-Control", "max-age=86400");
},
/* ...or go FP and return a new
* response reference entirely. */
(req, res) => ({
...res,
cookies: new Map<string, string>([["requested_proto", req.proto]])
})
);
const home = withCaching(() =>
jsonResponse({
foo: "bar",
isLol: true
})
);
export const routes = createRouteMap([["/", home]]);
API Documentation
Consult docs/API.md for comprehensive documentation on Reno's API.
Local Development
Once you've cloned the repository, you'll need to ensure you're running the version of Deno against which this project is developed; this is stored in .deno-version
. To install the correct version, run:
$ curl -fsSL https://deno.land/x/install/install.sh | sh -s $(cat .deno-version)
You should also run ./tools/install-types.sh
to install the TypeScript definitions for Deno and any other third-party dependencies.
Then you can run:
deno run --allow-net example/index.ts
- starts the example serverdeno test
- runs the unit tests
Functionality Checklist
- Path routing
- Async-compatible route handlers
- Error handling
- Route params
- Query params
- Response helpers
- JSON
- Custom headers
- Request bodies
- Cookies
- Streaming responses with
Reader
- Streaming request bodies