├── .gitignore ├── .prettierrc ├── package.json ├── README.md ├── index.js └── router.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | worker/ 3 | .cargo-ok 4 | package-lock.json 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "tabWidth": 4, 4 | "semi": false, 5 | "singleQuote": true 6 | } 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{{ project-name }}", 3 | "version": "1.0.0", 4 | "description": "package for creating workers templates", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "format": "prettier --write '**/*.{js,css,json,md}'" 9 | }, 10 | "author": "{{ authors }}", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "prettier": "^1.17.0" 14 | }, 15 | "dependencies": { 16 | "serverless-cloudflare-workers": "^1.2.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Router 2 | 3 | Selects the logic to respond to requests based on the `request` method and URL. Can be used with REST APIs or apps that require basic routing logic. 4 | 5 | [`index.js`](https://github.com/cloudflare/worker-template-router/blob/master/router.js) is the content of the Workers script. 6 | 7 | Live Demos are hosted on `workers-tooling.cf/demos/router`: 8 | [Demo /bar](http://workers-tooling.cf/demos/router/bar) | [Demo /foo](http://workers-tooling.cf/demos/router/foo) 9 | 10 | #### Wrangler 11 | To generate using [wrangler](https://github.com/cloudflare/wrangler) 12 | 13 | ``` 14 | wrangler generate myApp https://github.com/cloudflare/worker-template-router 15 | ``` 16 | 17 | #### Serverless 18 | To deploy using serverless add a [`serverless.yml`](https://serverless.com/framework/docs/providers/cloudflare/) file. 19 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const Router = require('./router') 2 | 3 | /** 4 | * Example of how router can be used in an application 5 | * */ 6 | addEventListener('fetch', event => { 7 | event.respondWith(handleRequest(event.request)) 8 | }) 9 | 10 | function handler(request) { 11 | const init = { 12 | headers: { 'content-type': 'application/json' }, 13 | } 14 | const body = JSON.stringify({ some: 'json' }) 15 | return new Response(body, init) 16 | } 17 | 18 | async function handleRequest(request) { 19 | const r = new Router() 20 | // Replace with the approriate paths and handlers 21 | r.get('.*/bar', () => new Response('responding for /bar')) 22 | r.get('.*/foo', req => handler(req)) 23 | r.post('.*/foo.*', req => handler(req)) 24 | r.get('/demos/router/foo', req => fetch(req)) // return the response from the origin 25 | 26 | const resp = await r.route(request) 27 | return resp 28 | } 29 | -------------------------------------------------------------------------------- /router.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Conditions are helper functions that when passed a request 3 | * will return a boolean for if that request uses 4 | * that method, header, etc.. 5 | * */ 6 | const Method = method => req => req.method.toLowerCase() === method.toLowerCase() 7 | const Get = Method('get') 8 | const Post = Method('post') 9 | const Put = Method('put') 10 | const Patch = Method('patch') 11 | const Delete = Method('delete') 12 | const Head = Method('patch') 13 | const Options = Method('options') 14 | 15 | const Header = (header, val) => req => req.headers.get(header) === val 16 | const Host = host => Header('host', host.toLowerCase()) 17 | const Referrer = host => Header('referrer', host.toLowerCase()) 18 | 19 | const Path = regExp => req => { 20 | const url = new URL(req.url) 21 | const path = url.pathname 22 | return path.match(regExp) && path.match(regExp)[0] === path 23 | } 24 | 25 | /** 26 | * Router handles the logic of what handler is matched given conditions 27 | * for each request 28 | * */ 29 | class Router { 30 | constructor() { 31 | this.routes = [] 32 | } 33 | 34 | handle(conditions, handler) { 35 | this.routes.push({ 36 | conditions, 37 | handler, 38 | }) 39 | return this 40 | } 41 | 42 | get(url, handler) { 43 | return this.handle([Get, Path(url)], handler) 44 | } 45 | 46 | post(url, handler) { 47 | return this.handle([Post, Path(url)], handler) 48 | } 49 | 50 | patch(url, handler) { 51 | return this.handler([Patch, Path(url)], handler) 52 | } 53 | 54 | delete(url, handler) { 55 | return this.handler([Delete, Path(url)], handler) 56 | } 57 | 58 | all(handler) { 59 | return this.handler([], handler) 60 | } 61 | 62 | route(req) { 63 | const route = this.resolve(req) 64 | 65 | if (route) { 66 | return route.handler(req) 67 | } 68 | 69 | return new Response('resource not found', { 70 | status: 404, 71 | statusText: 'not found', 72 | headers: { 73 | 'content-type': 'text/plain', 74 | }, 75 | }) 76 | } 77 | 78 | // resolve returns the matching route that returns true for 79 | // all the conditions if any 80 | resolve(req) { 81 | return this.routes.find(r => { 82 | if (!r.conditions || (Array.isArray(r) && !r.conditions.length)) { 83 | return true 84 | } 85 | 86 | if (typeof r.conditions === 'function') { 87 | return r.conditions(req) 88 | } 89 | 90 | return r.conditions.every(c => c(req)) 91 | }) 92 | } 93 | } 94 | 95 | module.exports = Router 96 | --------------------------------------------------------------------------------