{"id":13727184,"url":"https://github.com/reno-router/reno","last_synced_at":"2025-10-20T16:28:42.788Z","repository":{"id":37833117,"uuid":"187528273","full_name":"reno-router/reno","owner":"reno-router","description":"A thin, testable routing library designed to sit on top of Deno's standard HTTP module","archived":true,"fork":false,"pushed_at":"2023-06-27T16:25:09.000Z","size":2498,"stargazers_count":129,"open_issues_count":0,"forks_count":4,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-09-16T16:32:19.694Z","etag":null,"topics":["deno","http","router","router-as-a-function","routing","std","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/reno-router.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2019-05-19T21:08:13.000Z","updated_at":"2025-08-27T13:22:03.000Z","dependencies_parsed_at":"2023-02-19T06:16:18.521Z","dependency_job_id":"445c304f-9a66-48da-b89d-afc71122fa3f","html_url":"https://github.com/reno-router/reno","commit_stats":{"total_commits":598,"total_committers":6,"mean_commits":99.66666666666667,"dds":"0.39799331103678925","last_synced_commit":"0b598eddd510635fc43b114ddf6a8fe3588930b4"},"previous_names":[],"tags_count":147,"template":false,"template_full_name":null,"purl":"pkg:github/reno-router/reno","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reno-router%2Freno","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reno-router%2Freno/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reno-router%2Freno/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reno-router%2Freno/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/reno-router","download_url":"https://codeload.github.com/reno-router/reno/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reno-router%2Freno/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279575190,"owners_count":26193812,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-18T02:00:06.492Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["deno","http","router","router-as-a-function","routing","std","typescript"],"created_at":"2024-08-03T01:03:43.326Z","updated_at":"2025-10-20T16:28:42.204Z","avatar_url":"https://github.com/reno-router.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"# Reno\n\n\u003e ## Notice\n\u003e ⚠️ **Reno has been sunset,** and will thus no longer receive updates. The final version is v2.0.105, which supports Deno 1.34.3 and std 0.192.0.\n\u003e\n\u003e It is recommended that you use an alternative routing library or HTTP framework, such as [Oak](https://oakserver.github.io/oak/) or [Opine](https://github.com/cmorten/opine).\n\u003e\n\u003e Thank you to anyone who supported the project during its lifetime.\n\u003e\n\u003e **Reno: 2019-2023**\n\n---\n\n![Reno logo](https://raw.githubusercontent.com/reno-router/reno/master/logo/reno-500.png)\n\n[![Build status](https://github.com/reno-router/reno/workflows/CI/badge.svg)](https://github.com/reno-router/reno/actions)\n[![Deno doc](https://doc.deno.land/badge.svg)](https://doc.deno.land/https/deno.land/x/reno@v2.0.105/reno/mod.ts)\n\nReno is a thin routing library designed to sit on top of\n[Deno](https://deno.land/)'s [standard HTTP module](https://deno.land/std/http).\n\n- [Overview](#overview)\n- [Key Features](#key-features)\n- [Example Apps](#example-apps)\n- [API Documentation](#api-documentation)\n- [Local Development](#local-development)\n- [Functionality Checklist](#functionality-checklist)\n\n---\n\n## Overview\n\n```tsx\nimport { serve } from \"https://deno.land/std@0.192.0/http/server.ts\";\n\nimport {\n  AugmentedRequest,\n  createRouteMap,\n  createRouter,\n  jsonResponse,\n  MissingRouteError,\n  streamResponse,\n} from \"https://deno.land/x/reno@v2.0.105/reno/mod.ts\";\n\nconst PORT = 8000;\n\nfunction createErrorResponse(status: number, { message }: Error) {\n  return new Response(message, {\n    status,\n  });\n}\n\nexport const routes = createRouteMap([\n  [\"/\", () =\u003e new Response(\"Hello world!\")],\n\n  // Supports RegExp routes for further granularity\n  [/^\\/api\\/swanson\\/?([0-9]?)$/, async (req: AugmentedRequest) =\u003e {\n    const [quotesCount = \"1\"] = req.routeParams;\n\n    const res = await fetch(\n      `https://ron-swanson-quotes.herokuapp.com/v2/quotes/${quotesCount}`,\n    );\n\n    return jsonResponse(await res.json());\n  }],\n\n  // Supports Reader for streaming responses in chunks\n  [\"/streamed-response\", () =\u003e\n    streamResponse(\n      new ReactReader(\u003cApp /\u003e),\n    )],\n]);\n\nconst notFound = (e: MissingRouteError) =\u003e createErrorResponse(404, e);\nconst serverError = (e: Error) =\u003e createErrorResponse(500, e);\n\nconst mapToErrorResponse = (e: Error) =\u003e\n  e instanceof MissingRouteError ? notFound(e) : serverError(e);\n\nconst router = createRouter(routes);\n\nconsole.log(`Listening for requests on port ${PORT}...`);\n\nawait serve(\n  async (req) =\u003e {\n    try {\n      return await router(req);\n    } catch (e) {\n      return mapToErrorResponse(e);\n    }\n  },\n  {\n    port: PORT,\n  },\n);\n```\n\n## Key Features\n\n### Responses are just Data Structures\n\nThis, along with request handlers being\n[pure functions](https://en.wikipedia.org/wiki/Pure_function), makes unit\ntesting Reno services a breeze:\n\n```ts\nimport {\n  assertResponsesAreEqual,\n  jsonResponse,\n} from \"https://deno.land/x/reno@v2.0.105/reno/mod.ts\";\nimport { createRonSwansonQuoteHandler } from \"./routes.ts\";\n\nconst createFetchStub = (response: string[]) =\u003e\n  sinon.stub().resolves({\n    json: sinon.stub().resolves(response),\n  });\n\nDeno.test({\n  name: \"ronSwansonQuoteHandler should fetch a quote from an API and return it\",\n  async fn() {\n    const quotes = [\"Some Ron Swanson Quote\"];\n    const fetchStub = createFetchStub(quotes);\n    const ronSwansonQuoteHandler = createRonSwansonQuoteHandler(fetchStub);\n\n    const req = {\n      routeParams: [],\n    };\n\n    const response = await ronSwansonQuoteHandler(req);\n\n    await assertResponsesAreEqual(\n      response,\n      jsonResponse(quotes, {\n        \"X-Foo\": \"bar\",\n      }),\n    );\n  },\n});\n```\n\n### Wildcard Path Segments\n\nDespite the power of regular expressions for matching and capturing paths when\ntheir route parameters conform to an expected format or type, they can often\nprove verbose and unwieldy for simpler applications. Reno thus provides an\nalternative wildcard syntax (`\"*\"`) for string paths to achieve route param\nextraction:\n\n```ts\nfunction wildcardRouteParams(req: Pick\u003cAugmentedRequest, \"routeParams\"\u003e) {\n  const [authorId, postId] = req.routeParams;\n\n  return new Response(`You requested ${postId} by ${authorId}`);\n}\n\nconst routes = createRouteMap([\n  [\"/wildcard-route-params/authors/*/posts/*\", wildcardRouteParams],\n]);\n\nconst router = createRouter(routes);\n```\n\n### Nested Routers\n\nLike most other HTTP routing libraries that you know and love, Reno supports\nnested routers; you can use wildcards as suffixes to group routers by a common\npath segment:\n\n```ts\nconst routes = createRouteMap([\n  [\n    \"/foo/*\",\n    createRouter(\n      createRouteMap([\n        [\n          \"/bar/*\",\n          createRouter(\n            createRouteMap([[\n              \"/baz\",\n              () =\u003e new Response(\"Hello from a nested route!\"),\n            ]]),\n          ),\n        ],\n      ]),\n    ),\n  ],\n]);\n\nconst router = createRouter(routes);\n```\n\n### Route Handlers are Composable\n\nAnother consequence of route handlers being intrinsically pure functions is that\nthey can be composed with higher-order route handlers, allowing particular\nbehaviours to be reused across your entire application:\n\n```ts\nimport { compose } from \"https://deno.land/x/compose@1.3.2/index.js\";\n\nimport {\n  AugmentedRequest,\n  createRouteMap,\n  RouteHandler,\n} from \"https://deno.land/x/reno@v2.0.105/reno/mod.ts\";\n\nimport isValidAPIKey from \"./api_keys.ts\";\n\nfunction withLogging(next: RouteHandler) {\n  return function (req: AugmentedRequest) {\n    console.log(`${new Date().toJSON()}: ${req.method} ${req.url}`);\n    return next(req);\n  };\n}\n\nfunction withAuth(next: RouteHandler) {\n  return async function (req: AugmentedRequest) {\n    const apiKey = req.headers.has(\"Authorization\")\n      ? req.headers.get(\"Authorization\")?.replace(\"Bearer \", \"\")\n      : \"\";\n\n    const isValid = apiKey \u0026\u0026 await isValidAPIKey(apiKey);\n\n    return isValid\n      ? next(req)\n      : new Response(`API key not authorised to access ${req.pathname}`, {\n        status: 401,\n      });\n  };\n}\n\nconst profile = compose(\n  withAuth,\n  withLogging,\n)(() =\u003e new Response(\"Your profile!\"));\n\nexport const routes = createRouteMap([\n  [\"/profile\", profile],\n]);\n```\n\n### Reno Apps are Unobtrusive, Pure Functions\n\nGiven that a Reno router is a function that takes a request and returns a\nresponse (or more specifically, `Promise\u003cResponse\u003e`), you are free to integrate\nit as you wish, managing the lifecycle of your HTTP server independently. This\nalso makes it trivial to write end-to-end tests with\n[SuperDeno](https://github.com/asos-craigmorten/superdeno), as evidenced by\n[Reno's own E2E suite](https://github.com/reno-router/reno/tree/master/e2e_tests):\n\n```ts\nimport { superdeno } from \"https://deno.land/x/superdeno@4.5.0/mod.ts\";\nimport app from \"../example/app.ts\";\n\nDeno.test(\"/ should return the expected response\", async () =\u003e {\n  await superdeno(app).get(\"/\")\n    .expect(200)\n    .expect(\"Cache-Control\", \"max-age=86400\")\n    .expect(\"Set-Cookie\", \"requested_method=GET\")\n    .expect({\n      foo: \"bar\",\n      isLol: true,\n    });\n});\n```\n\n## Example Apps\n\nAs well as the\n[example app found in this repo](https://github.com/reno-router/reno/tree/v2.0.105/example),\nwhich is targetted by the end-to-end test suite, there is a\n[standalone repository for a blog microservice](https://github.com/reno-router/blog-microservice)\nbuilt with Deno, Reno, PostgreSQL, and Docker.\n\n## API Documentation\n\nConsult\n[Reno's entry on the Deno Doc website](https://doc.deno.land/https/deno.land/x/reno@v2.0.105/reno/mod.ts)\nfor comprehensive documentation on Reno's API.\n\n## Local Development\n\nOnce you've cloned the repository, you'll need to ensure you're running the\nversion of Deno against which this project is developed; this is stored in\n`deno_versions.json`. To install the correct version, run:\n\n```sh\n# If Deno isn't currently installed...\n$ curl -fsSL https://deno.land/x/install/install.sh | sh -s v$(jq -r .deno deno_versions.json)\n\n# ...or it it's already present on your system\ndeno upgrade --version $(jq -r .deno deno_versions.json)\n```\n\nYou should also run `make install-types` to install the TypeScript definitions\nfor Deno and any other third-party dependencies.\n\nThen you can run:\n\n- `make example-app`: starts the example server\n- `make test`: runs the unit tests\n- `make e2e`: runs the end-to-end tests\n- `make lint`: lints the source code\n- `make format`: formats the source code\n- `make format-check`: checks the formatting of the source code\n- `make generate-readme`: generates README.md from the template, into which the\n  version number in the package metadata is injected\n\n## Functionality Checklist\n\n- [x] Path routing\n- [x] Async route handlers\n- [x] Error handling\n- [x] Route params\n- [x] Query params\n- [x] Response helpers\n- [x] JSON\n- [x] Custom headers\n- [x] Request bodies\n- [x] Cookies\n- [x] Streaming responses with\n      [`Deno.Reader`](https://doc.deno.land/builtin/stable#Deno.Reader)\n- [ ] Streaming request bodies\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freno-router%2Freno","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Freno-router%2Freno","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freno-router%2Freno/lists"}