Reno
Reno is a thin routing library designed to sit on top of Deno's standard HTTP module.
import { serve } from "https://deno.land/std@v0.51.0/http/server.ts";
import {
createRouter,
AugmentedRequest,
createRouteMap,
textResponse,
jsonResponse,
streamResponse,
} from "https://deno.land/x/reno@v0.7.0/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 router = createRouter(routes);
(async () => {
console.log("Listening for requests...");
for await (const req of serve(":8001")) {
req.respond(await router(req));
}
})();
TODO: replace/complement this with proper documentation
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@v0.7.0/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"
}));
}
});
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@v0.7.0/reno/mod.ts";
const withCaching = pipe(
(req, res) => {
/* Mutate the response returned by
* the inner route handler... */
res.headers = res.headers || new Headers();
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]]);
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