All Projects → reno-router → reno

reno-router / reno

Licence: MIT license
A thin, testable routing library designed to sit on top of Deno's standard HTTP module

Programming Languages

typescript
32286 projects

Projects that are alternatives of or similar to reno

compress
compress and uncompress for Deno
Stars: ✭ 29 (-77.17%)
Mutual labels:  deno
SymfonyModularRouting
[DEPRECATED] Decouple your Symfony routing to independent, standalone services.
Stars: ✭ 18 (-85.83%)
Mutual labels:  routing
kyuko
Fast and easy http framework for Deno Deploy 🦕
Stars: ✭ 18 (-85.83%)
Mutual labels:  deno
Deno-news-cli
A deno based CLI app to show quick news at your terminal
Stars: ✭ 18 (-85.83%)
Mutual labels:  deno
bbbike
BBBike
Stars: ✭ 56 (-55.91%)
Mutual labels:  routing
cf-k8s-networking
building a cloud foundry without gorouter....
Stars: ✭ 33 (-74.02%)
Mutual labels:  routing
shell
A very lightweight framework for building shell/CLI applications. Works in Node.js, Deno, and the browser.
Stars: ✭ 63 (-50.39%)
Mutual labels:  deno
balanceofsatoshis
Tool for working with the balance of your satoshis on LND
Stars: ✭ 447 (+251.97%)
Mutual labels:  routing
sinco
Browser Automation and Testing Tool for Deno, written in full TypeScript
Stars: ✭ 54 (-57.48%)
Mutual labels:  deno
lwip nat arduino
lwip library with NAT feature for Arduino environment
Stars: ✭ 55 (-56.69%)
Mutual labels:  routing
node-match-path
Matches a URL against a path. Parameters, wildcards, RegExp.
Stars: ✭ 30 (-76.38%)
Mutual labels:  routing
croner
Trigger functions and/or evaluate cron expressions in JavaScript. No dependencies. Most features. All environments.
Stars: ✭ 169 (+33.07%)
Mutual labels:  deno
rtrlib
An open-source C implementation of the RPKI/Router Protocol client
Stars: ✭ 62 (-51.18%)
Mutual labels:  routing
svelte
Svelte compiler ported to Deno
Stars: ✭ 71 (-44.09%)
Mutual labels:  deno
justjavac
justjavac's modules
Stars: ✭ 15 (-88.19%)
Mutual labels:  deno
deno-bin
Use Deno via npm
Stars: ✭ 52 (-59.06%)
Mutual labels:  deno
deno math
Deno module for high-precision calculations and scientific computing
Stars: ✭ 21 (-83.46%)
Mutual labels:  deno
sqlite3
The fastest and correct module for SQLite3 in Deno.
Stars: ✭ 143 (+12.6%)
Mutual labels:  deno
deno-mongo-api
Example for building REST APIS with deno and MongoDB
Stars: ✭ 17 (-86.61%)
Mutual labels:  deno
AloeDB
Light, Embeddable, NoSQL database for Deno 🦕
Stars: ✭ 111 (-12.6%)
Mutual labels:  deno

Reno

Reno logo

Build status Deno doc Published on Nest.land

Reno is a thin routing library designed to sit on top of Deno's standard HTTP module.


Overview

import { serve } from "https://deno.land/[email protected]/http/server.ts";

import {
  AugmentedRequest,
  createRouteMap,
  createRouter,
  jsonResponse,
  MissingRouteError,
  streamResponse,
} from "https://deno.land/x/[email protected]/reno/mod.ts";

/* Alternatively, you can import Reno from nest.land:
 * import { ... } from "https://x.nest.land/[email protected]/reno/mod.ts";
 */

const PORT = 8000;

function createErrorResponse(status: number, { message }: Error) {
  return new Response(message, {
    status,
  });
}

export const routes = createRouteMap([
  ["/", () => new Response("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: MissingRouteError) => createErrorResponse(404, e);
const serverError = (e: Error) => createErrorResponse(500, e);

const mapToErrorResponse = (e: Error) =>
  e instanceof MissingRouteError ? notFound(e) : serverError(e);

const router = createRouter(routes);

console.log(`Listening for requests on port ${PORT}...`);

await serve(
  async (req) => {
    try {
      return await router(req);
    } catch (e) {
      return mapToErrorResponse(e);
    }
  },
  {
    port: PORT,
  },
);

Key Features

Responses are just Data Structures

This, along with request handlers being pure functions, makes unit testing Reno services a breeze:

import {
  assertResponsesAreEqual,
  jsonResponse,
} from "https://deno.land/x/[email protected]/reno/mod.ts";
import { createRonSwansonQuoteHandler } from "./routes.ts";

const createFetchStub = (response: string[]) =>
  sinon.stub().resolves({
    json: sinon.stub().resolves(response),
  });

Deno.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);

    await assertResponsesAreEqual(
      response,
      jsonResponse(quotes, {
        "X-Foo": "bar",
      }),
    );
  },
});

Wildcard Path Segments

Despite the power of regular expressions for matching and capturing paths when their route parameters conform to an expected format or type, they can often prove verbose and unwieldy for simpler applications. Reno thus provides an alternative wildcard syntax ("*") for string paths to achieve route param extraction:

function wildcardRouteParams(req: Pick<AugmentedRequest, "routeParams">) {
  const [authorId, postId] = req.routeParams;

  return new Response(`You requested ${postId} by ${authorId}`);
}

const routes = createRouteMap([
  ["/wildcard-route-params/authors/*/posts/*", wildcardRouteParams],
]);

const router = createRouter(routes);

Nested Routers

Like most other HTTP routing libraries that you know and love, Reno supports nested routers; you can use wildcards as suffixes to group routers by a common path segment:

const routes = createRouteMap([
  [
    "/foo/*",
    createRouter(
      createRouteMap([
        [
          "/bar/*",
          createRouter(
            createRouteMap([[
              "/baz",
              () => new Response("Hello from a nested route!"),
            ]]),
          ),
        ],
      ]),
    ),
  ],
]);

const router = createRouter(routes);

Route Handlers are Composable

Another consequence of route handlers being intrinsically pure functions is that they can be composed with higher-order route handlers, allowing particular behaviours to be reused across your entire application:

import { compose } from "https://deno.land/x/[email protected]/index.js";

import {
  AugmentedRequest,
  createRouteMap,
  RouteHandler,
} from "https://deno.land/x/[email protected]/reno/mod.ts";

import isValidAPIKey from "./api_keys.ts";

function withLogging(next: RouteHandler) {
  return function (req: AugmentedRequest) {
    console.log(`${new Date().toJSON()}: ${req.method} ${req.url}`);
    return next(req);
  };
}

function withAuth(next: RouteHandler) {
  return async function (req: AugmentedRequest) {
    const apiKey = req.headers.has("Authorization")
      ? req.headers.get("Authorization")?.replace("Bearer ", "")
      : "";

    const isValid = apiKey && await isValidAPIKey(apiKey);

    return isValid
      ? next(req)
      : new Response(`API key not authorised to access ${req.pathname}`, {
        status: 401,
      });
  };
}

const profile = compose(
  withAuth,
  withLogging,
)(() => new Response("Your profile!"));

export const routes = createRouteMap([
  ["/profile", profile],
]);

Reno Apps are Unobtrusive, Pure Functions

Given that a Reno router is a function that takes a request and returns a response (or more specifically, Promise<Response>), you are free to integrate it as you wish, managing the lifecycle of your HTTP server independently. This also makes it trivial to write end-to-end tests with SuperDeno, as evidenced by Reno's own E2E suite:

import { superdeno } from "https://deno.land/x/[email protected]/mod.ts";
import app from "../example/app.ts";

Deno.test("/ should return the expected response", async () => {
  await superdeno(app).get("/")
    .expect(200)
    .expect("Cache-Control", "max-age=86400")
    .expect("Set-Cookie", "requested_method=GET")
    .expect({
      foo: "bar",
      isLol: true,
    });
});

Example Apps

As well as the example app found in this repo, which is targetted by the end-to-end test suite, there is a standalone repository for a blog microservice built with Deno, Reno, PostgreSQL, and Docker.

API Documentation

Consult Reno's entry on the Deno Doc website 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_versions.json. To install the correct version, run:

# If Deno isn't currently installed...
$ curl -fsSL https://deno.land/x/install/install.sh | sh -s v$(jq -r .deno deno_versions.json)

# ...or it it's already present on your system
deno upgrade --version $(jq -r .deno deno_versions.json)

You should also run make install-types to install the TypeScript definitions for Deno and any other third-party dependencies.

Then you can run:

  • make example-app: starts the example server
  • make test: runs the unit tests
  • make e2e: runs the end-to-end tests
  • make lint: lints the source code
  • make format: formats the source code
  • make format-check: checks the formatting of the source code
  • make generate-readme: generates README.md from the template, into which the version number in the package metadata is injected

Functionality Checklist

  • Path routing
  • Async route handlers
  • Error handling
  • Route params
  • Query params
  • Response helpers
  • JSON
  • Custom headers
  • Request bodies
  • Cookies
  • Streaming responses with Deno.Reader
  • Streaming request bodies
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].