{"id":21810020,"url":"https://github.com/tsndr/cloudflare-worker-router","last_synced_at":"2025-05-15T15:07:46.916Z","repository":{"id":39068928,"uuid":"338464807","full_name":"tsndr/cloudflare-worker-router","owner":"tsndr","description":"A super lightweight router (1.0K) with middleware support and ZERO dependencies for Cloudflare Workers.","archived":false,"fork":false,"pushed_at":"2024-10-26T21:08:32.000Z","size":547,"stargazers_count":245,"open_issues_count":1,"forks_count":30,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-05-09T03:39:45.233Z","etag":null,"topics":["api","cloudflare","cloudflare-worker","cloudflare-workers","framework","middleware","router","routing","worker","workers"],"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/tsndr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"tsndr"}},"created_at":"2021-02-13T00:22:34.000Z","updated_at":"2025-05-07T11:09:10.000Z","dependencies_parsed_at":"2024-02-24T20:39:45.481Z","dependency_job_id":"9b392af7-32fa-4397-8fac-03d4d4318cf0","html_url":"https://github.com/tsndr/cloudflare-worker-router","commit_stats":{"total_commits":128,"total_committers":11,"mean_commits":"11.636363636363637","dds":0.1328125,"last_synced_commit":"fa1733312380afb7c6d1dca8c3563386bf463b14"},"previous_names":[],"tags_count":77,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsndr%2Fcloudflare-worker-router","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsndr%2Fcloudflare-worker-router/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsndr%2Fcloudflare-worker-router/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsndr%2Fcloudflare-worker-router/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tsndr","download_url":"https://codeload.github.com/tsndr/cloudflare-worker-router/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254364270,"owners_count":22058878,"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","cloudflare","cloudflare-worker","cloudflare-workers","framework","middleware","router","routing","worker","workers"],"created_at":"2024-11-27T13:30:05.681Z","updated_at":"2025-05-15T15:07:41.906Z","avatar_url":"https://github.com/tsndr.png","language":"TypeScript","funding_links":["https://github.com/sponsors/tsndr"],"categories":[],"sub_categories":[],"readme":"# Cloudflare Workers Router\n\nCloudflare Workers Router is a super lightweight router (1.0K gzipped) with middleware support and **ZERO dependencies** for [Cloudflare Workers](https://workers.cloudflare.com/).\n\nWhen I was trying out Cloudflare Workers I almost immediately noticed how fast it was compared to other serverless offerings. So I wanted to build a full-fledged API to see how it performs doing real work, but since I wasn't able to find a router that suited my needs I created my own.\n\n\n## Contents\n\n- [Features](#features)\n- [Usage](#usage)\n- [Reference](#reference)\n- [Getting started](#getting-started)\n\n\n## Features\n\n- ZERO dependencies\n- Lightweight (1.0K gzipped)\n- Fully written in TypeScript\n- Built specifically around Middlewares\n- Debug-Mode, CORS and Bearer helpers\n- Allows accessing request body multiple times\n\n\n## Usage\n\nMigrating from `v2.x.x`, check out the [Migration Guide](MIGRATION.md).\n\n### TypeScript Example\n\n```typescript\nimport { Router } from '@tsndr/cloudflare-worker-router'\n\n// Env Types\nexport type Var\u003cT = string\u003e = T\nexport type Secret\u003cT = string\u003e = T\n\nexport type Env = {\n    // Example binding to KV. Learn more at https://developers.cloudflare.com/workers/runtime-apis/kv/\n    // MY_KV_NAMESPACE: KVNamespace\n    //\n    // Example binding to Durable Object. Learn more at https://developers.cloudflare.com/workers/runtime-apis/durable-objects/\n    // MY_DURABLE_OBJECT: DurableObjectNamespace\n    //\n    // Example binding to R2. Learn more at https://developers.cloudflare.com/workers/runtime-apis/r2/\n    // MY_BUCKET: R2Bucket\n\n    ENVIRONMENT: Var\u003c'dev' | 'prod'\u003e\n\n    SECRET_TOKEN: Secret\n}\n\n// Request Extension\nexport type ExtReq = {\n    userId?: number\n}\n\n// Context Extension\nexport type ExtCtx = {\n    //sentry?: Toucan\n}\n\n// Initialize Router\nconst router = new Router\u003cEnv, ExtCtx, ExtReq\u003e()\n\n// Enabling build in CORS support\nrouter.cors()\n\n// Register global middleware\nrouter.use(({ env, req }) =\u003e {\n    // Intercept if token doesn't match\n    if (req.headers.get('authorization') !== env.SECRET_TOKEN)\n        return new Response(null, { status: 401 })\n})\n\n// Simple get\nrouter.get('/user', () =\u003e {\n    return Response.json({\n        id: 1,\n        name: 'John Doe'\n    })\n})\n\n// Post route with url parameter\nrouter.post('/user/:id', ({ req }) =\u003e {\n\n    const userId = req.params.id\n\n    // Do stuff\n\n    if (!true) {\n        return Response.json({\n            error: 'Error doing stuff!'\n        }, { status: 400 })\n    }\n\n    return Response.json({ userId }, { status: 204 })\n})\n\n// Delete route using a middleware\nrouter.delete('/user/:id', ({ env, req }) =\u003e {\n    if (req.headers.get('authorization') === env.SECRET_TOKEN)\n        return new Response(null, { status: 401 })\n\n}, ({ req }) =\u003e {\n\n  const userId = req.params.id\n\n  // Do stuff...\n\n  return Response.json({ userId })\n})\n\n// Listen Cloudflare Workers Fetch Event\nexport default {\n    async fetch(request: Request, env: Env, ctx: ExecutionContext) {\n        return router.handle(request, env, ctx)\n    }\n}\n\n```\n\n\u003cdetails\u003e\n    \u003csummary\u003eJavaScript Example\u003c/summary\u003e\n\n```javascript\nimport { Router } from '@tsndr/cloudflare-worker-router'\n\n// Initialize router\nconst router = new Router()\n\n// Enabling build in CORS support\nrouter.cors()\n\n// Register global middleware\nrouter.use(({ env, req }) =\u003e {\n    // Intercept if token doesn't match\n    if (req.headers.get('authorization') !== env.SECRET_TOKEN)\n        return new Response(null, { status: 401 })\n})\n\n// Simple get\nrouter.get('/user', () =\u003e {\n    return Response.json({\n        id: 1,\n        name: 'John Doe'\n    })\n})\n\n// Post route with url parameter\nrouter.post('/user/:id', ({ req }) =\u003e {\n\n    const userId = req.params.id\n\n    // Do stuff\n\n    if (!true) {\n        return Response.json({\n            error: 'Error doing stuff!'\n        }, { status: 400 })\n    }\n\n    return Response.json({ userId }, { status: 204 })\n})\n\n// Delete route using a middleware\nrouter.delete('/user/:id', ({ env, req }) =\u003e {\n    if (req.headers.get('authorization') === env.SECRET_TOKEN)\n        return new Response(null, { status: 401 })\n\n}, ({ req }) =\u003e {\n\n  const userId = req.params.id\n\n  // Do stuff...\n\n  return Response.json({ userId })\n})\n\n// Listen Cloudflare Workers Fetch Event\nexport default {\n    async fetch(request, env, ctx) {\n        return router.handle(request, env, ctx)\n    }\n}\n```\n\u003c/details\u003e\n\n\n## Reference\n\n### `router.debug([state = true])`\n\nEnable or disable debug mode. Which will return the `error.stack` in case of an exception instead of and empty `500` response. Debug mode is disabled by default.\n\n\n#### `state`\nState is a `boolean` which determines if debug mode should be enabled or not (default: `true`)\n\nKey                    | Type      | Default Value\n---------------------- | --------- | -------------\n`state`                | `boolean` | `true`\n\n\n### `router.use([...handlers])`\n\nRegister a global middleware handler.\n\n\n#### `handler(ctx)`\n\nHandler is a `function` which will be called for every request.\n\n\n#### `ctx`\n\nObject containing `env`, [`req`](#req-object)\n\n\n### `router.cors([config])`\n\nIf enabled will overwrite other `OPTIONS` requests.\n\n\n#### `config` (object, optional)\n\nKey                        | Type       | Default Value\n-------------------------- | ---------- | -------------\n`allowOrigin`              | `string`   | `*`\n`allowMethods`             | `string`   | `*`\n`allowHeaders`             | `string`   | `*`\n`allowCredentials`         | `boolean`  | `undefined`\n`vary`                     | `string`   | `undefined`\n`maxAge`                   | `integer`  | `86400`\n`optionsSuccessStatus`     | `integer`  | `204`\n\n\n### Supported Methods\n\n- `router.any(url, [...handlers])`\n- `router.delete(url, [...handlers])`\n- `router.get(url, [...handlers])`\n- `router.head(url, [...handlers])`\n- `router.options(url, [...handlers])`\n- `router.patch(url, [...handlers])`\n- `router.post(url, [...handlers])`\n- `router.put(url, [...handlers])`\n\n\n#### `url` (string)\n\nThe URL starting with a `/`.\nSupports the use of dynamic parameters, prefixed with a `:` (i.e. `/user/:userId/edit`) which will be available through the [`req`-Object](#req-object) (i.e. `req.params.userId`).\n\n\n#### `handlers` (function, optional)\n\nAn unlimited number of functions getting [`ctx`](#ctx-object) passed into them.\n\n\n### `ctx`-Object\n\nKey       | Type                | Description\n--------- | ------------------- | -----------\n`env`     | `object`            | Environment\n`req`     | `req`-Object        | Request Object\n`ctx`     | `ctx`-Object        | Cloudflare's `ctx`-Object\n\n\n### `req`-Object\n\nKey       | Type                | Description\n--------- | ------------------- | -----------\n`body`    | `object` / `string` | Only available if method is `POST`, `PUT`, `PATCH` or `DELETE`. Contains either the received body string or a parsed object if valid JSON was sent.\n`headers` | `Headers`           | Request [Headers Object](https://developer.mozilla.org/en-US/docs/Web/API/Headers)\n`method`  | `string`            | HTTP request method\n`params`  | `object`            | Object containing all parameters defined in the url string\n`query`   | `object`            | Object containing all query parameters\n\n\n## Getting started\n\nPlease follow Cloudflare's [Get started guide](https://developers.cloudflare.com/workers/get-started/guide/) to install wrangler.\n\n\n#### Initialize Project\n\n```bash\nwrangler init \u003cname\u003e\n```\n\nUse of TypeScript is strongly encouraged :)\n\n```bash\nnpm i @tsndr/cloudflare-worker-router\n```\n\n\n### TypeScript (\u003ccode\u003esrc/index.ts\u003c/code\u003e)\n\n```typescript\nimport { Router } from '@tsndr/cloudflare-worker-router'\n\n// Env Types\nexport type Var\u003cT = string\u003e = T\nexport type Secret\u003cT = string\u003e = T\n\nexport type Env = {\n  // Example binding to KV. Learn more at https://developers.cloudflare.com/workers/runtime-apis/kv/\n  // MY_KV_NAMESPACE: KVNamespace\n  //\n  // Example binding to Durable Object. Learn more at https://developers.cloudflare.com/workers/runtime-apis/durable-objects/\n  // MY_DURABLE_OBJECT: DurableObjectNamespace\n  //\n  // Example binding to R2. Learn more at https://developers.cloudflare.com/workers/runtime-apis/r2/\n  // MY_BUCKET: R2Bucket\n  //\n  // Example Variable\n  // ENVIRONMENT: Var\u003c'dev' | 'prod'\u003e\n  //\n  // Example Secret\n  // JWT_SECRET: Secret\n}\n\n// Request Extension\nexport type ExtReq = {\n  userId?: number\n}\n\n// Context Extension\nexport type ExtCtx = {\n  //sentry?: Toucan\n}\n\n// Handler Type\nexport type Handler = RouterHandler\u003cEnv, ExtCtx, ExtReq\u003e\n\n// Initialize Router\nconst router = new Router\u003cEnv, ExtCtx, ExtReq\u003e()\n\n// Enable Debug Mode\nrouter.debug()\n\n// Enabling build in CORS support\n//router.cors()\n\n/// Example Route\n//\n// router.get('/hi', async () =\u003e {\n//   return new Response('Hello World')\n// })\n\n\n/// Example Route for splitting into multiple files\n//\n// const helloHandler: Handler = async () =\u003e {\n//    return new Response('Hello World')\n// }\n//\n// router.get('/hellow', helloHandler)\n\n// TODO: add your routes here\n\nexport default {\n  async fetch(request: Request, env: Env, ctx: ExecutionContext) {\n    return router.handle(request, env, ctx)\n  }\n}\n```\n\n\n### JavaScript (\u003ccode\u003esrc/index.js\u003c/code\u003e)\n\n\u003cdetails\u003e\n    \u003csummary\u003eConsider using TypeScript instead :)\u003c/summary\u003e\n\n```javascript\nimport { Router } from '@tsndr/cloudflare-worker-router'\n\nconst router = new Router()\n\n// Enable Debug Mode\n//router.debug()\n\n// Enabling build in CORS support\n//router.cors()\n\n/// Example Route\n//\n// router.get('/hi', async () =\u003e {\n//    return new Response('Hello World')\n//})\n\n/// Example Route for splitting into multiple files\n//\n// async function hiHandler() {\n//    return new Response('Hello World')\n// }\n//\n// router.get('/hi', hiHandler)\n\n// TODO: add your routes here\n\nexport default {\n    async fetch(request, env, ctx) {\n        return router.handle(request, env, ctx)\n    }\n}\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftsndr%2Fcloudflare-worker-router","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftsndr%2Fcloudflare-worker-router","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftsndr%2Fcloudflare-worker-router/lists"}