{"id":13495194,"url":"https://github.com/kreteshq/retes","last_synced_at":"2025-08-21T15:32:03.557Z","repository":{"id":38305454,"uuid":"306115980","full_name":"kreteshq/retes","owner":"kreteshq","description":"Typed, Declarative, Data-Driven \u0026 Functional Routing for Node.js (Express.js alternative in TypeScript)","archived":false,"fork":false,"pushed_at":"2023-01-16T11:04:13.000Z","size":463,"stargazers_count":55,"open_issues_count":0,"forks_count":7,"subscribers_count":6,"default_branch":"main","last_synced_at":"2024-12-10T10:10:13.238Z","etag":null,"topics":["express","nodejs","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":"2020-10-21T18:39:07.000Z","updated_at":"2024-01-30T22:58:06.000Z","dependencies_parsed_at":"2023-02-10T02:45:29.549Z","dependency_job_id":null,"html_url":"https://github.com/kreteshq/retes","commit_stats":null,"previous_names":[],"tags_count":48,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kreteshq%2Fretes","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kreteshq%2Fretes/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kreteshq%2Fretes/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kreteshq%2Fretes/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kreteshq","download_url":"https://codeload.github.com/kreteshq/retes/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":229906078,"owners_count":18142423,"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":["express","nodejs","routing"],"created_at":"2024-07-31T19:01:32.318Z","updated_at":"2024-12-20T01:15:12.710Z","avatar_url":"https://github.com/kreteshq.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"# Retes\n\nTyped, Declarative, Data-Driven Routing for Node.js.\n\n## What is Retes?\n\nRetes is a routing library for Node.js written in TypeScript and inspired by Clojure's [Ring](https://github.com/ring-clojure/ring), [Compojure](https://github.com/weavejester/compojure) and [Retit](https://github.com/metosin/reitit). It is built directly on top of the Node.js `http` module, so you can use it as an alternative to Express or Koa.\n\n- **Data-Driven:** In Retes you define routes using the existing data structures. This way, we limit the number of abstractions and we are able to easily transform and combine routes. Our routing description is declarative.\n- **Typed:** The type system conveniently helps us control the shape of our routing\n- **Battery-Included (wip):** Most common middlewares will be included out of the box\n\n## Key Features\n\n- built-in parsing of query params, body and route's dynamic segments\n- built-in file uploading handling mechansim\n- fast route matching (see [Benchmarks](#benchmarks))\n- handlers are functions that take requests as input and return responses as output\n- middlewares can be combined on per-route basis\n- an HTTP response is just an object containing at least `statusCode` and `body` keys\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 in and out\n\n## Usage\n\nGenerate a new Retes app using `create-retes-app`. This will set up automatically the required project structure for you.\n\n```\npnpm create retes-app my-retes-app\n# or\nyarn create retes-app my-retes-app\n# or\nnpx create-retes-app@latest my-retes-app\n```\n\nOnce the project is created, install its dependencies:\n\n```\ncd my-retes-app\n```\n\n```\npnpm install\n# or\nyarn\n# or\nnpm install\n```\n\nFinally, start the application\n\n```\npnpm start\n```\n\nThe server application listens on the specified port, in our case `:3000`. Open [localhost:3000](http://localhost:3000) and test the routes.\n\n## Features\n\n### Params\n\nRetes combines requests' query params, body params and segment params into `params`.\n\n```ts\nimport { Response, Route, ServerApp } from 'retes';\nimport { Created } from 'retes/response';\nimport { GET, POST } from 'retes/route';\n\nconst routes = [\n\tGET('/query-params', ({ params }) =\u003e OK(params)),\n\tPOST('/body-form', ({ params }) =\u003e OK(params)),\n\tPOST('/body-json', () =\u003e OK(params)),\n\tGET('/segment/:a/:b', ({ params }) =\u003e OK(params)),\n];\n\nasync function main() {\n\tconst app = new ServerApp(routes);\n\tawait app.start(3000);\n\n\tconsole.log('started');\n}\n\nmain();\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 `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 { Response, Route, ServerApp } from 'retes';\nimport { Accepted, Created, InternalServerError, OK } from 'retes/response';\nimport { GET } from 'retes/route';\n\nconst routes = [\n\tGET('/created', () =\u003e Created('payload')), // returns HTTP 201 Created\n\tGET('/ok', () =\u003e OK('payload')), // returns HTTP 200 OK\n\tGET('/accepted', () =\u003e Accepted('payload')), // returns HTTP 202 Accepted\n\tGET('/internal-error', () =\u003e InternalServerError()), // returns HTTP 500 Internal Server Error\n];\n\nasync function main() {\n\tconst app = new ServerApp(routes);\n\tawait app.start(3000);\n\n\tconsole.log('started');\n}\n\nmain();\n```\n\n### Middleware Composition on Per-Route Basis\n\n```ts\nimport { ServerApp } from 'retes';\nimport { GET } from 'retes/route';\n\nconst { GET } = Route;\n\nconst prepend = next =\u003e request =\u003e `prepend - ${next()}`;\nconst append = next =\u003e request =\u003e `${next()} - append`;\n\nconst routes = [\n\tGET('/middleware', () =\u003e 'Hello, Middlewares', {\n\t\tmiddleware: [prepend, append],\n\t}), // equivalent to: prepend(append(handler))\n];\n\nasync function main() {\n\tconst app = new ServerApp(routes);\n\tawait app.start(3000);\n\n\tconsole.log('started');\n}\n\nmain();\n```\n\n## Benchmarks\n\nWIP\n\n## Roadmap\n\n- [ ] infer types for dynamic segments from routes using the string literals feature from TypeScript 4.1 (in progress PR #1)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkreteshq%2Fretes","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkreteshq%2Fretes","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkreteshq%2Fretes/lists"}