{"id":25969422,"url":"https://github.com/thesephi/oak-routing-ctrl","last_synced_at":"2025-08-09T21:05:37.281Z","repository":{"id":225616906,"uuid":"766306490","full_name":"Thesephi/oak-routing-ctrl","owner":"Thesephi","description":"TypeScript Decorators for easy scaffolding API services with the oak framework (jsr:@oak/oak)","archived":false,"fork":false,"pushed_at":"2025-02-23T16:43:35.000Z","size":188,"stargazers_count":7,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-02T00:12:38.906Z","etag":null,"topics":["api","bun","cloudflare-workers","deno","nodejs","oak","routing"],"latest_commit_sha":null,"homepage":"https://jsr.io/@dklab/oak-routing-ctrl","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/Thesephi.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":"GOVERNANCE.md","roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-03-02T22:24:43.000Z","updated_at":"2025-03-01T14:03:24.000Z","dependencies_parsed_at":"2024-06-08T02:26:30.113Z","dependency_job_id":"c7cc9556-d0f9-4b44-a0c8-6bc56eaa5256","html_url":"https://github.com/Thesephi/oak-routing-ctrl","commit_stats":null,"previous_names":["thesephi/oak-routing-ctrl"],"tags_count":34,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thesephi%2Foak-routing-ctrl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thesephi%2Foak-routing-ctrl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thesephi%2Foak-routing-ctrl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thesephi%2Foak-routing-ctrl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Thesephi","download_url":"https://codeload.github.com/Thesephi/oak-routing-ctrl/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241935238,"owners_count":20044826,"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":["api","bun","cloudflare-workers","deno","nodejs","oak","routing"],"created_at":"2025-03-04T22:48:08.555Z","updated_at":"2025-03-04T22:48:09.164Z","avatar_url":"https://github.com/Thesephi.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# oak-routing-ctrl\n\n[![JSR](https://jsr.io/badges/@dklab/oak-routing-ctrl)](https://jsr.io/@dklab/oak-routing-ctrl)\n[![JSR Score](https://jsr.io/badges/@dklab/oak-routing-ctrl/score)](https://jsr.io/@dklab/oak-routing-ctrl)\n[![Built with the Deno Standard Library](https://raw.githubusercontent.com/denoland/deno_std/main/badge.svg)](https://jsr.io/@std)\n[![Known Vulnerabilities](https://snyk.io/test/github/thesephi/oak-routing-ctrl/badge.svg)](https://snyk.io/test/github/thesephi/oak-routing-ctrl)\n[![codecov](https://codecov.io/github/Thesephi/oak-routing-ctrl/graph/badge.svg?token=BA3M9P6410)](https://codecov.io/github/Thesephi/oak-routing-ctrl)\n[![Runtime Tests](https://github.com/Thesephi/oak-routing-ctrl/actions/workflows/runtime-tests.yml/badge.svg)](https://github.com/Thesephi/oak-routing-ctrl/actions/workflows/runtime-tests.yml)\n\nTypeScript Decorators for easy scaffolding API services with the\n[Oak](https://jsr.io/@oak/oak) framework (`jsr:@oak/oak`) 🚗 🐿️ 🦕\n\nWorks on Node.js, Bun, Cloudflare Workers, and Deno\n\n```ts\n@Controller(\"/api/v1/pokemon\")\nclass MyPokedex {\n  @Get(\"/:id\")\n  viewEntry(ctx) {\n    if (ctx.params.id === \"0025\") return \"Pikachu\";\n  }\n}\n```\n\n![Open API Spec example](https://khangdinh.wordpress.com/wp-content/uploads/2024/07/oak-routing-ctrl-oas-example.png)\n\n## Forewords\n\nIf you're familiar with the\n[npm library routing-controllers](https://www.npmjs.com/package/routing-controllers),\nyou'll find yourself very much at home.\n\nHowever, please note that this libray is **not** meant to be a drop-in\nreplacement for routing-controllers, as it attempts to conform to\n[TC39 Decorators proposal](https://github.com/tc39/proposal-decorators) which\n[doesn't support Method Parameter Decorator](https://github.com/tc39/proposal-decorators?tab=readme-ov-file#comparison-with-typescript-experimental-decorators)\nyet. There's currently no plan to support TypeScript \"experimental\" decorators,\nbut if you feel strongly for it, please feel free to fork this repo!\n\n## Heads up\n\nFor easy reading, the examples below do not specify any explicit version when\ninstalling library dependencies. But in your production code, it's advisable to\npin every dependency to a specific version.\n\n## Deno runtime\n\nPrerequisite:\n[Deno](https://docs.deno.com/runtime/manual/getting_started/installation)\n\n### Example: Retrieving path parameters\n\n```ts\n// main.ts\n\nimport { Application } from \"jsr:@oak/oak/application\";\nimport {\n  Controller,\n  ControllerMethodArgs,\n  Get,\n  useOakServer,\n} from \"jsr:@dklab/oak-routing-ctrl\";\n\nconst app = new Application();\n\n@Controller(\"/v1\")\nclass MyController {\n  @Get(\"/hello/:name\")\n  @ControllerMethodArgs(\"param\")\n  hello(param) {\n    return `hello, ${param.name}`;\n  }\n}\n\nuseOakServer(app, [MyController]);\n\nawait app.listen({ port: 1993 });\n```\n\n```bash\ndeno run --allow-env --allow-net main.ts\n```\n\n```bash\n# in another terminal\ncurl localhost:1993/v1/hello/world # prints: hello, world\n```\n\n### Example: Retrieving path parameters and request body\n\n\u003cdetails\u003e\n\u003csummary\u003eView Example\u003c/summary\u003e\n\n```ts\nimport { Application } from \"jsr:@oak/oak/application\";\nimport {\n  Controller,\n  ControllerMethodArgs,\n  Post,\n  useOakServer,\n} from \"jsr:@dklab/oak-routing-ctrl\";\n\n@Controller(\"/v1\")\nclass MyController {\n  @Post(\"/tell/:name\")\n  @ControllerMethodArgs(\"param\", \"body\")\n  tell(param, body) {\n    return `telling ${param.name} that \"${body.message}\"`;\n  }\n}\n\nconst app = new Application();\nuseOakServer(app, [MyController]);\nawait app.listen({ port: 1993 });\n```\n\n_\n\n```bash\ncurl -H\"Content-Type: application/json\" localhost:1993/v1/tell/alice -d'{\"message\": \"all we need is love\"}'\n# prints: telling alice that \"all we need is love\"\n```\n\n\u003c/details\u003e\n\n### Example: Retrieving request query and path parameters\n\n\u003cdetails\u003e\n\u003csummary\u003eView Example\u003c/summary\u003e\n\n```ts\nimport { Application } from \"jsr:@oak/oak/application\";\nimport {\n  Controller,\n  ControllerMethodArgs,\n  Get,\n  useOakServer,\n} from \"jsr:@dklab/oak-routing-ctrl\";\n\n@Controller(\"/v1\")\nclass MyController {\n  @Get(\"/books/:category\")\n  @ControllerMethodArgs(\"query\", \"param\")\n  search(query, param) {\n    return `searching for books in category \"${param.category}\" with query \"page=${query.page}\"`;\n  }\n}\n\nconst app = new Application();\nuseOakServer(app, [MyController]);\nawait app.listen({ port: 1993 });\n```\n\n_\n\n```bash\ncurl localhost:1993/v1/books/thriller\\?page=2\n# prints: searching for books in category \"thriller\" with query \"page=2\"\n```\n\n\u003c/details\u003e\n\n### Example: Accessing underlying context object\n\n\u003cdetails\u003e\n\u003csummary\u003eView Example\u003c/summary\u003e\n\n```ts\nimport { Application } from \"jsr:@oak/oak/application\";\nimport { Controller, Get, useOakServer } from \"jsr:@dklab/oak-routing-ctrl\";\n\n@Controller()\nclass MyController {\n  @Get(\"/foo/bar\")\n  fooBar(ctx) {\n    return `request header x-foo has value \"${\n      ctx.request.headers.get(\"x-foo\")\n    }\"`;\n  }\n}\n\nconst app = new Application();\nuseOakServer(app, [MyController]);\nawait app.listen({ port: 1993 });\n```\n\n_\n\n```bash\ncurl -H\"x-foo: lorem\" localhost:1993/foo/bar\n# prints: request header x-foo has value \"lorem\"\n```\n\n\u003c/details\u003e\n\n## Other runtimes\n\n### Node.js\n\nIf you're on Node.js 22 (or above), please consult this example boilerplate:\nhttps://replit.com/@0x97FB9/auto-swagger-node-alpha\n\nIf you're on Node.js 21 (or below), then the example workflow below may apply to\nyou.\n\n\u003cdetails\u003e\n\u003csummary\u003eView Example for Node.js 21 and below\u003c/summary\u003e\n\n_\n\nYou can start with a boilerplate\n\n```bash\nnpm create oak-nodejs-esbuild@latest\n```\n\n_\n\nOr you can start from scratch\n\n\u003csmall\u003efriendly note: if something doesn't work as advertised in this section,\nplease file an issue, thanks!\u003c/small\u003e\n\n```bash\nnpm i @jsr/oak__oak @jsr/dklab__oak-routing-ctrl\n\n# note that `npx jsr i {package}` also works, but\n# installing directly from the `@jsr` scope might result\n# in better dependency resolutions\n```\n\n_\n\n```ts\n// alternatively imported from \"@oak/oak/application\"\nimport { Application } from \"@jsr/oak__oak/application\";\n\n// alternatively imported from \"@dklab/oak-routing-ctrl\"\nimport {\n  Controller,\n  ControllerMethodArgs,\n  Get,\n  useOakServer,\n} from \"@jsr/dklab__oak-routing-ctrl\";\n\n@Controller(\"/v1\")\nexport class MyController {\n  @Get(\"/hello/:name\")\n  @ControllerMethodArgs(\"param\")\n  hello(param: Record\u003cstring, string\u003e) {\n    return `hello, ${param.name}`;\n  }\n}\n\nconst app = new Application();\nuseOakServer(app, [MyController]);\nawait app.listen({ port: 1993 });\n```\n\n_\n\n```bash\ncurl http://localhost:1993/v1/hello/world # prints: hello, world\n```\n\n\u003c/details\u003e\n\n### Cloudflare Workers\n\n```bash\nnpm create oak-cloudflare-worker@latest\n```\n\nLive Demo (uptime \u003cins\u003enot\u003c/ins\u003e guaranteed):\nhttps://oak-routing-ctrl-cloudflare.dklab.workers.dev/swagger\n\n\u003cdetails\u003e\n\u003csummary\u003eView Example\u003c/summary\u003e\n\n```bash\nnpx jsr add @oak/oak @dklab/oak-routing-ctrl\n```\n\n_\n\n```ts\nimport { Application } from \"@oak/oak/application\";\nimport {\n  Controller,\n  ControllerMethodArgs,\n  Get,\n  useOakServer,\n} from \"@dklab/oak-routing-ctrl/mod\";\n\n@Controller()\nclass MyCloudflareWorkerController {\n  @Get(\"/hello/:name\")\n  @ControllerMethodArgs(\"param\")\n  hello(param: { name: string }) {\n    return `hello, ${param.name}`;\n  }\n}\n\nconst app = new Application();\nuseOakServer(app, [MyCloudflareWorkerController]);\nexport default { fetch: app.fetch };\n```\n\n_\n\n```bash\ncurl http://{your-cloudflare-worker-domain}/hello/world # prints: hello, world\n```\n\n\u003c/details\u003e\n\n### Bun\n\n```bash\nnpm create oak-bun@latest\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eView Example\u003c/summary\u003e\n\n```bash\nbunx jsr i @oak/oak @dklab/oak-routing-ctrl\n```\n\n_\n\n```ts\nimport { Application, type RouterContext } from \"@oak/oak\";\nimport { Controller, Get, useOakServer } from \"@dklab/oak-routing-ctrl\";\n\n@Controller(\"/v1\")\nclass MyController {\n  @Get(\"/hello/:name\")\n  hello(ctx: RouterContext\u003c\"/hello/:name\"\u003e) {\n    return `hello, ${ctx.params.name}`;\n  }\n}\n\nconst app = new Application();\nuseOakServer(app, [MyController]);\nawait app.listen({ port: 1993 });\n```\n\n_\n\n```bash\ncurl http://localhost:1993/v1/hello/world # prints: hello, world\n```\n\n\u003c/details\u003e\n\n## Serving Open API Spec\n\nServing Open API Spec (both as a JSON doc and as an HTML view) is supported as\nfollowed:\n\n```ts\nimport { Application } from \"@oak/oak\";\nimport {\n  Controller,\n  ControllerMethodArgs,\n  Get,\n  useOakServer,\n  useOas,\n  z,\n  type zInfer,\n} from \"@dklab/oak-routing-ctrl\";\n\nconst HelloNamePathParamsSchema = z.object({ name: z.string() });\nconst OpenApiSpecForHelloName = {\n  // using `zod` to express Open API Spec for this route\n  // e.g. `request` and `responses`\n  request: { params: HelloNamePathParamsSchema },\n  responses: {\n    \"200\": {\n      description: \"Success\",\n      content: { \"text/html\": { schema: z.string() } },\n    },\n  },\n};\n\n@Controller(\"/v1\")\nclass MyController {\n  @Get(\n    \"/hello/:name\",\n    OpenApiSpecForHelloName, // API spec is entirely optional\n  )\n  @ControllerMethodArgs(\"param\")\n  hello(\n    param: zInfer\u003ctypeof HelloNamePathParamsSchema\u003e, // or type it however else you like\n  ) {\n    return `hello, ${param.name}`; // intellisense should just work ™\n  }\n}\n\nuseOakServer(app, [MyController]);\nuseOas(app, {\n  // optionally declare OAS info as per your project needs\n  info: {\n    version: \"0.1.0\",\n    title: \"My awesome API\",\n    description: \"This is an awesome API\",\n  },\n});\n\nawait app.listen({ port: 1993 });\n```\n\nThe following OAS resources are now served:\n\n- UI: http://localhost:1993/swagger\n- JSON doc: http://localhost:1993/oas.json\n\n\u003cdetails\u003e\n\u003csummary\u003eView Example OAS json doc\u003c/summary\u003e\n\n```bash\ncurl localhost:1993/oas.json\n\n{\n  \"openapi\": \"3.0.0\",\n  \"info\": {\n    \"version\": \"0.1.0\",\n    \"title\": \"My awesome API\",\n    \"description\": \"This is an awesome API\"\n  },\n  \"servers\": [\n    {\n      \"url\": \"http://localhost:1993\"\n    }\n  ],\n  \"components\": {\n    \"schemas\": {},\n    \"parameters\": {}\n  },\n  \"paths\": {\n    \"/hello/{name}\": {\n      \"get\": {\n        \"parameters\": [\n          {\n            \"schema\": {\n              \"type\": \"string\"\n            },\n            \"required\": true,\n            \"name\": \"name\",\n            \"in\": \"path\"\n          }\n        ],\n        \"responses\": {\n          \"200\": {\n            \"description\": \"Success\",\n            \"content\": {\n              \"text/plain\": {\n                \"schema\": {\n                  \"type\": \"string\"\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n```\n\n\u003c/details\u003e\n\n## Documentation\n\nDocumentation is hosted on the Javascript Registry:\nhttps://jsr.io/@dklab/oak-routing-ctrl/doc\n\n## Contributor Resources\n\n### Tests\n\n```bash\ndeno test -A --coverage=cov_profile\ndeno coverage cov_profile\n```\n\n[![test coverage](https://codecov.io/gh/Thesephi/oak-routing-ctrl/graphs/tree.svg?token=BA3M9P6410)](https://codecov.io/github/Thesephi/oak-routing-ctrl)\n\n## FAQs\n\nQ: can this library support fullstack web development?\n\nA: yes, in fact there's a fullstack web framework on top of this library:\n[FullSoak](https://jsr.io/@fullsoak/fullsoak)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthesephi%2Foak-routing-ctrl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthesephi%2Foak-routing-ctrl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthesephi%2Foak-routing-ctrl/lists"}