{"id":18783762,"url":"https://github.com/kreteshq/retes-deno","last_synced_at":"2026-04-16T17:37:36.346Z","repository":{"id":62422040,"uuid":"356906519","full_name":"kreteshq/retes-deno","owner":"kreteshq","description":"Declarative, Data-Driven Routing for Deno","archived":false,"fork":false,"pushed_at":"2021-04-15T13:06:48.000Z","size":16,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-01-29T16:39:38.948Z","etag":null,"topics":["deno","denoland","middleware","routing"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kreteshq.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-04-11T15:33:48.000Z","updated_at":"2021-07-06T08:25:20.000Z","dependencies_parsed_at":"2022-11-01T17:33:01.241Z","dependency_job_id":null,"html_url":"https://github.com/kreteshq/retes-deno","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kreteshq%2Fretes-deno","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kreteshq%2Fretes-deno/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kreteshq%2Fretes-deno/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kreteshq%2Fretes-deno/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kreteshq","download_url":"https://codeload.github.com/kreteshq/retes-deno/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239699579,"owners_count":19682574,"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","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","denoland","middleware","routing"],"created_at":"2024-11-07T20:40:36.680Z","updated_at":"2025-10-05T11:35:47.510Z","avatar_url":"https://github.com/kreteshq.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Retes\n\n\u003ch3\u003eDeclarative, Data-Driven Routing for Deno\u003c/h3\u003e\n\nRetes is a minimalistic routing library for Deno inspired by Clojure's\n[Ring](https://github.com/ring-clojure/ring),\n[Compojure](https://github.com/weavejester/compojure) and\n[Retit](https://github.com/metosin/reitit). It is built directly on top of\nDeno's `http` module. You can use it as an alternative to Express or Koa.\n\n## Features\n\n- **Data-Driven:** In Retes you define routes using the existing data\n  structures, i.e. less abstractions and easier to transform and combine routes.\n  Routing becomes declarative.\n- **Simple Abstractions**: Routing handlers are functions that take a request as\n  input and return a response as output, i.e.\n  `type Handler = Request =\u003e Response`. Middleware functions take a handler as\n  input and return a handler as output, i.e.\n  `type Middleware = Handler =\u003e Handler`. And that's it! No `Context`, no `Next` et al.\n- **Battery-Included (wip):** Most common middlewares will be included out of\n  the box\n\n* HTTP responses are just objects containing at least `statusCode` and `body`\n  keys\n* middlewares can be combined on per-route basis\n* built-in parsing of query params, body and route's dynamic segments\n* fast route matching (see [Benchmarks](#benchmarks))\n* built-in file uploading handling mechansim (wip)\n\n## Why Retes?\n\n- declarative route descriptions make them easily composable\n- functional handlers are more natural fit for the HTTP flow\n- common request/response transformations are already built-in\n- typed routes make it easier to discover and control the shape of data flowing\n  in and out\n\n## Usage\n\n### A `Hello, World` App\n\n```ts\nimport { ServerApp } from \"https://deno.land/x/retes/mod.ts\";\nimport { GET } from \"https://deno.land/x/retes/routing.ts\";\nimport { Plain } from \"https://deno.land/x/retes/response.ts\";\n\n// routes are just an array, thus the order matters\nconst routes = [\n  GET(\"/\", (_) =\u003e Plain(\"Hello, World\")),\n];\n\nconst app = new ServerApp(routes);\nawait app.start(5544);\n```\n\n### A More Complex App\n\n```ts\nimport { ServerApp } from \"https://deno.land/x/retes/mod.ts\";\nimport { GET } from \"https://deno.land/x/retes/routing.ts\";\nimport { Plain } from \"https://deno.land/x/retes/response.ts\";\n\nconst routes = [\n  GET(\"/\", (_) =\u003e Plain(\"Hello, World\")),\n];\n\nconst app = new ServerApp(routes);\n\napp.use((handler) =\u003e {\n  // you can do some middleware initialization here\n  return async (request) =\u003e {\n    const response = await handler(request);\n    const dTime = response.headers[\"X-Response-Time\"];\n\n    console.log(`${request.method} ${request.url} - ${dTime}`);\n\n    return response;\n  };\n});\n\napp.use((handler) =\u003e\n  async (request) =\u003e {\n    const start = Date.now();\n    const response = await handler(request);\n    const ms = Date.now() - start;\n\n    response.headers[\"X-Response-Time\"] = `${ms}ms`;\n\n    return response;\n  }\n);\n\nawait app.start(5544);\n```\n\nThis example is adapted from Oak so it's easier to compare and contrast.\n\n## Features\n\n### Params\n\nRetes combines requests' query params, body params and segment params into\n`params`.\n\n```ts\nimport { ServerApp } from \"https://deno.land/x/retes/mod.ts\";\nimport { GET, POST } from \"https://deno.land/x/retes/routing.ts\";\nimport { OK } from \"https://deno.land/x/retes/response.ts\";\n\nconst routes = [\n  GET(\"/query-params\", ({ params }) =\u003e OK(params)),\n  POST(\"/body-form\", ({ params }) =\u003e OK(params)),\n  POST(\"/body-json\", () =\u003e OK(params)),\n  GET(\"/segment/:a/:b\", ({ params }) =\u003e OK(params)),\n];\n\nconst app = new ServerApp(routes);\nawait app.start(3000);\n```\n\nThis `GET` query\n\n```\nhttp :3000/query-params?a=1\u0026b=2\n```\n\nreturns\n\n```http\nHTTP/1.1 200 OK\n\n{\n    \"a\": \"1\",\n    \"b\": \"2\"\n}\n```\n\nThis `POST` query with `Content-Type` set to\n`application/x-www-form-urlencoded; charset=utf-8`\n\n```\nhttp --form :3000/body-form a:=1 b:=2\n```\n\nreturns\n\n```http\nHTTP/1.1 200 OK\n\n{\n    \"a\": \"1\",\n    \"b\": \"2\"\n}\n```\n\nThis `POST` query with `Content-Type` set to `application/json`\n\n```\nhttp :3000/body-json a:=1 b:=2\n```\n\nreturns\n\n```http\nHTTP/1.1 200 OK\n\n{\n    \"a\": 1,\n    \"b\": 2\n}\n```\n\nThis `GET` request\n\n```\nhttp :3000/segment/1/2\n```\n\nreturns\n\n```http\nHTTP/1.1 200 OK\n{\n    \"a\": \"1\",\n    \"b\": \"2\"\n}\n```\n\n### Convenience Wrappers for HTTP Responses\n\n```ts\nimport { ServerApp } from \"https://deno.land/x/retes/mod.ts\";\nimport { GET } from \"https://deno.land/x/retes/routing.ts\";\nimport {\n  Accepted,\n  Created,\n  InternalServerError,\n  OK,\n} from \"https://deno.land/x/retes/response.ts\";\n\nconst routes = [\n  GET(\"/created\", () =\u003e Created(\"payload\")), // returns HTTP 201 Created\n  GET(\"/ok\", () =\u003e OK(\"payload\")), // returns HTTP 200 OK\n  GET(\"/accepted\", () =\u003e Accepted(\"payload\")), // returns HTTP 202 Accepted\n  GET(\"/internal-error\", () =\u003e InternalServerError()), // returns HTTP 500 Internal Server Error\n];\n\nconst app = new ServerApp(routes);\nawait app.start(3000);\n```\n\n### Middleware Composition on Per-Route Basis\n\n```ts\nimport { ServerApp } from \"https://deno.land/x/retes/mod.ts\";\nimport { GET } from \"https://deno.land/x/retes/routing.ts\";\nimport { Plain } from \"https://deno.land/x/retes/response.ts\";\n\nconst prepend = (handler) =\u003e\n  (request) =\u003e {\n    const response = handler();\n\n    return Plain(`prepend - ${response.body}`);\n  };\nconst append = (handler) =\u003e\n  (request) =\u003e {\n    const response = handler();\n    return `${response.body} - append`;\n  };\n\nconst routes = [\n  GET(\"/middleware\", () =\u003e Plain(\"Hello, Middlewares\"), {\n    middleware: [prepend, append],\n  }), // equivalent to: prepend(append(handler))\n];\n\nconst app = new ServerApp(routes);\nawait app.start(3000);\n```\n\n## Benchmarks\n\nWIP\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkreteshq%2Fretes-deno","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkreteshq%2Fretes-deno","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkreteshq%2Fretes-deno/lists"}