Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork6
Most powerful, flexible and composable router for building enterprise RESTful APIs easily!
License
tunnckoCore/koa-rest-router
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
Most powerful, flexible and composable router for building enterprise RESTful APIs easily!
You might also be interested ingibon - a minimal & functional 600 bytes client-side router.
- production: ready for and used in
- composability: grouping multiple resources and multiple routers
- flexibility: overriding controller and request methods, plus custom prefixes
- compatibility: accepts both old and modern middlewares without deprecation messages
- powerful: multiple routers on samekoa app - even can combine multiple routers
- light: not poluting your router instance and app - see
.loadMethods - backward compatible: works on koa v1 - use
.legacyMiddleware - maintainability: very small, beautiful, maintainable and commented codebase
- stability: strict semantic versioning and very well documented, based onkoa-better-router
- open: love PRs for features, issues and recipes -Contribute a recipe? See therecipes ofkoa-better-router
(TOC generated byverb usingmarkdown-toc)
ProTip: Checkoutkoa-better-router API too to know what more methods comes with this.
This router useskoa-better-router, so you should review its API documentation to get more info how the things are working and what more methods are exposed.
In addition this router allows you to override the controller methods which will be used in certain route path.
Defaults
| Request method | Route path | Controller method |
|---|---|---|
| GET | /users | index |
| GET | /users/new | new |
| POST | /users | create |
| GET | /users/:user | show |
| GET | /users/:user/edit | edit |
| PUT | /users/:user | update |
| DELETE | /users/:user | remove |
Example
letRouter=require('koa-rest-router')letrouter=Router()router.resource('users',{// GET /usersindex:(ctx,next)=>{},// GET /users/newnew:(ctx,next)=>{},// POST /userscreate:(ctx,next)=>{},// GET /users/:usershow:(ctx,next)=>{},// GET /users/:user/editedit:(ctx,next)=>{},// PUT /users/:userupdate:(ctx,next)=>{},// DELETE /users/:userremove:(ctx,next)=>{}})letusers=router.getResource('users')console.log(users.length)// => 7console.log(users)// => Array Route Objectsconsole.log(router.routes.length)// => 7console.log(router.resources.length)// => 1
Note: Multiple middlewares can be passed on each. Also combining old and modern koa middlewares, so both generator functions and normal functions.
You easily can override the defaults by passing
options.mapobject with key/value pairs where the key represents the original, and value is a string containing the wanted override.
Example
letrouter=require('koa-rest-router')()letoptions={map:{index:'foo',new:'bar',create:'baz',show:'qux',}}router.resource('users',{// GET /usersfoo:(ctx,next)=>{},// GET /users/newbar:(ctx,next)=>{},// POST /usersbaz:(ctx,next)=>{},// GET /users/:userqux:(ctx,next)=>{},// ... etc},options)
In some cases in guides the REST routes uses different request methods and that field is not clear enough. So every sane router should allow overriding such things, so we do it. By default for updating is used
PUT, for deleting/removing isDELETE. You can override this methods to usePOSTinstead, so ...
Example
letrouter=require('koa-rest-router')()letoptions={methods:{put:'POST'}}router.resource('cats',{// POST /cats/:catupdate:(ctx,next)=>{}},options)
And you can combine both overriding variants, of course
Example
letrouter=require('koa-rest-router')()letoptions={methods:{put:'POST'},map:{update:'foobar'}}router.resource('cats',{// POST /cats/:catfoobar:(ctx,next)=>{}},options)
Install withnpm
$ npm i koa-rest-router --save
For more use-cases see thetests
letrouter=require('koa-rest-router')()// orletRouter=require('koa-rest-router')letapiRouter=Router({prefix:'/api/v1'})
Initialize
KoaRestRouterwith optionaloptions, directly passed tokoa-better-router and this package inherits it. So you have all methods and functionality from the awesomekoa-better-router middleware.
Params
[options]{Object}: passed directly tokoa-better-router, in addition we have 2 more options here.[options.methods]{Object}: override request methods to be used[options.map]{Object}: override controller methods to be called
Example
letRouter=require('koa-rest-router')letapi=Router({prefix:'/api/v1'})// - can have multiples middlewares// - can have both old and modern middlewares combinedapi.resource('companies',{index:function(ctx,next){},show:function(ctx,next){},create:function(ctx,next){}// ... and etc})console.log(api.routes.length)// 7console.log(api.resources.length)// 1api.resource('profiles',{index:function(ctx,next){},show:function(ctx,next){},create:function(ctx,next){}// ... and etc})console.log(api.routes.length)// 14console.log(api.resources.length)// 2letKoa=require('koa')// Koa v2letapp=newKoa()letbasic=Router()// prefix is `/` by defaultbasic.extend(api)app.use(api.middleware())app.use(basic.middleware())app.listen(4444,()=>{console.log('Open http://localhost:4444 and try')// will output 2x14 links// - 14 links on `/api/v1` prefix// - 14 links on `/` prefixapi.routes.forEach((route)=>{console.log(`${route.method} http://localhost:4444${route.path}`)})basic.routes.forEach((route)=>{console.log(`${route.method} http://localhost:4444${route.path}`)})})
Core method behind
.resourcefor creating single resource with aname, but without adding it tothis.routesarray. You can override any defaults - default request methods and default controller methods, just by passing respectivelyopts.methodsobject andopts.mapobject. It useskoa-better-router's.createRouteunder the hood.
Params
name{String|Object}: name of the resource orctrlctrl{Object}: controller object to be called on each endpoint, oroptsopts{Object}: optional, merged with options from constructorreturns{KoaRestRouter}this: instance for chaining
Example
letrouter=require('koa-rest-router')({prefix:'/api'}).loadMethods()// The server partletbody=require('koa-better-body')letKoa=require('koa')letapp=newKoa()// override request methodsletmethods={put:'POST'del:'POST'}// override controller methodsletmap={index:'list',show:'read',remove:'destroy'}// notice the body should be invoked explicitly// with or without options object, no matterletupdateMiddlewares=[body(),(ctx,next)=>{ctx.body=`This method by default is triggered with PUT requests only.`ctx.body=`${ctx.body} But now it is from POST request.`returnnext()},function*(next)=>{this.body=`${this.body} Incoming data is`this.body=`${this.body}${JSON.stringify(this.request.fields,null,2)}`yieldnext}]// create actual resourceletcats=router.createResource('cats',{list:[(ctx,next)=>{ctx.body=`This is GET${ctx.route.path} route with multiple middlewares`returnnext()},function*(next){this.body=`${this.body} and combining old and modern middlewares.`yieldnext}],read:(ctx,next)=>{ctx.body=`This is${ctx.route.path} route.`ctx.body=`${ctx.body} And param ":cat" is${ctx.params.cat}.`ctx.body=`${ctx.body} By default this method is called "show".`returnnext()},update:updateMiddlewares,destroy:(ctx,next)=>{ctx.body=`This route should be called with DELETE request, by default.`ctx.body=`${ctx.body} But now it request is POST.`returnnext()}},{map:map,methods:methods})console.log(cats)// => array of "Route Objects"// router.routes array is emptyconsole.log(router.getRoutes())// => []// register the resourcerouter.addResource(cats)console.log(router.routes.length)// => 7console.log(router.getRoutes().length)// => 7console.log(router.getRoutes())// or router.routes// => array of "Route Objects"app.use(router.middleware())app.listen(5000,()=>{console.log(`Server listening on http://localhost:5000`)console.log(`Try to open these routes:`)router.routes.forEach((route)=>{console.log(`${route.method}`http://localhost:5000${route.path}`)}))})
Simple method that is alias of
.addRoutesand.addResources, but for adding single resource. It can accepts only oneresourceobject.
Params
resource{Array}: array of route objects, known as"Resource Object"returns{KoaRestRouter}this: instance for chaining
Example
letRouter=require('koa-rest-router')letapi=newRouter({prefix:'/'})console.log(api.resources.length)// 0console.log(api.routes.length)// 0api.addResource(api.createResource('dragons'))console.log(api.resources.length)// 1console.log(api.routes.length)// 7console.log(api.getResource('dragons'))// array of route objects// => [// { prefix: '/', route: '/dragons', path: '/dragons', ... }// { prefix: '/', route: '/dragons/:dragon', path: '/dragons/:dragon', ... }// ... and 5 more routes// ]
Get single resource by
name. Special case is resource to the/prefix. So pass/asname. See more on what are the"Route Objects" in thekoa-better-router docs. What that method returns, I call"Resource Object" - array of"Route Objects"
Params
name{String}: name of the resource, pluralreturns{Array|Null}: if resource withnamenot found `null, otherwise array of route objects - that array is known as Resource Object
Example
letapi=require('koa-rest-router')({prefix:'/api/v2'})letfrogs=api.createResource('frogs')letdragons=api.createResource('dragons')console.log(api.getResource('frogs'))// array of route objects// => [// { prefix: '/api/v2', route: '/frogs', path: '/api/v2/frogs', ... }// { prefix: '/api/v2', route: '/frogs/:frog', path: '/api/v2/frogs/:frog', ... }// ... and 5 more routes// ]console.log(api.getResources().length)// 2
Creates a resource using
.createResourceand adds the resource routes to thethis.routesarray, using.addResource. This is not an alias! It is combination of two methods. Methods that are not defined in givenctrl(controller) returns by default501 Not Implemented. You can override any defaults - default request methods and default controller methods, just by passing respectivelyopts.methodsobject andopts.mapobject.
Params
name{String|Object}: name of the resource orctrlctrl{Object}: controller object to be called on each endpoint, oroptsopts{Object}: optional, merged with options from constructorreturns{KoaRestRouter}this: instance for chaining
Example
letRouter=require('koa-rest-router')letapi=newRouter({prefix:'/api/v3'})letrouter=newRouter()// on `/` prefix by default// All of the controller methods// can be remap-ed. using `opts.map`// try to pass `{ map: { index: 'home' }}` as optionsapi.resource('users',{// GET /usersindex:[(ctx,next)=>{},(ctx,next)=>{}],// GET /users/newnew:(ctx,next)=>{},// POST /userscreate:(ctx,next)=>{},// GET /users/:usershow:[(ctx,next)=>{},function*(next){}],// GET /users/:user/editedit:(ctx,next)=>{},// PUT /users/:user// that `PUT` can be changed `opts.methods.put: 'post'`update:(ctx,next)=>{},// DELETE /users/:user// that `DELETE` can be changed `opts.methods.delete: 'post'`remove:(ctx,next)=>{}})// notice the `foo` methodrouter.resource({// GET /foo:[(ctx,next)=>{ctx.body=`GET${ctx.route.path}`returnnext()},function*(next){ctx.body=`${this.body}! Hello world!`yieldnext}],// GET /:id, like /123show:(ctx,next)=>{ctx.body=JSON.stringify(ctx.params,null,2)returnnext()}},{map:{index:'foo'}})api.routes.forEach(route=>console.log(route.method,route.path))router.routes.forEach(route=>console.log(route.method,route.path))// Wanna use only one router?letfooRouter=newRouter()letKoa=require('koa')letapp=newKoa()fooRouter.addRoutes(api.getResources(),router.getRoutes())console.log(fooRouter.routes)console.log(fooRouter.routes.length)// 14app.use(fooRouter.middleware())app.listen(4433,()=>{console.log('Cool server started at 4433. Try these routes:')fooRouter.routes.forEach((route)=>{console.log(`${route.method} http://localhost:4433${route.path}`)})})
Just an alias ofkoa-better-router's'
.addRoutesmethod.
Params
...args{Array}: any number of arguments (arrays of route objects)returns{KoaRestRouter}this: instance for chaining
As we have
.getRoutesmethod for gettingthis.routes, so we have.getResourcesfor gettingthis.resourcesarray, too. Each.createResourcereturns array of route objects with length of 7, so 7 routes. So if you call.createResourcetwo times thethis.resources(what this method returns) will contain 2 arrays with 7 routes in each of them.
returns{Array}: array of arrays of route objects
Example
letrouter=require('koa-rest-router')().loadMethods()console.log(router.routes.length)// 0console.log(router.getRoutes().length)// 0console.log(router.resources.length)// 0console.log(router.getResources().length)// 0router.get('/about',(ctx,next)=>{})router.resource('dogs')router.resource('cats')console.log(router.routes.length)// 15console.log(router.getRoutes().length)// 15console.log(router.resources.length)// 2console.log(router.getResources().length)// 2
Powerful method for grouping couple of resources into one resource endpoint. For example you have
/catsand/dogsendpoints, but you wanna create/cats/:cat/dogs/:dogendpoint, so you can do such things with that. You can group infinite number of resources. Useful methods that gives you what you should pass as arguments here are.createResource,.createRoute,.getResources,.getResourceand.getRoutes.Note: Be aware of that it replaces middlewares ofdestwith the middlewares of lastsrc.
Params
dest{Array}: array of"Route Objects" or"Resource Object" (both are arrays)src1{Array}: array of"Route Objects" or"Resource Object" (both are arrays)src2{Array}: array of"Route Objects" or"Resource Object" (both are arrays)returns{Array}: new array with grouped resources
Example
letrouter=require('koa-rest-router')({prefix:'/api/v3'})letdepartments=router.createResource('departments')letcompanies=router.createResource('companies')letprofiles=router.createResource('profiles')letclients=router.createResource('clients')letusers=router.createResource('users')letcats=router.createResource('cats')letdogs=router.createResource('dogs')// endpoint: /companies/:company/departments/:departmentletone=router.groupResources(companies,departments)// endpoint: /profiles/:profile/clients/:client/cats/:catlettwo=router.groupResources(profiles,clients,cats)// crazy? huh, AWESOME!// endpoint: /companies/:company/departments/:department/profiles/:profile/clients/:client/cats/:catletfoo=router.groupResources(one,two)// but actually just "register" `one` and `foo`// so you WON'T have `/profiles/:profile/clients/:client/cats/:cat`// endpoint in your APIrouter.addRoutes(one,foo)// Server partletKoa=require('koa')letapp=newKoa()app.use(router.middleware())app.listen(4000,()=>{console.log(`Mega API server on http://localhost:4000`)console.log(`Checkout these routes:`)// it will output 14 linksrouter.getRoutes().forEach((route)=>{console.log(`${route.method} http://localhost:4000${route.path}`)})})
- koa-bel: View engine for
koawithout any deps, built to be used…more |homepage - koa-better-body: Full-featuredkoa body parser! Support parsing text, buffer, json, json patch…more |homepage
- koa-better-ratelimit: Better, smaller, faster - koa middleware for limit request by ip…more |homepage
- koa-better-router: Stable and lovely router forkoa, usingpath-match. Foundation for building…more |homepage
- koa-better-serve: Small, simple and correct serving of files, usingkoa-send - nothing…more |homepage
- koa-ip-filter: Middleware forkoa that filters IPs against glob patterns, RegExp, string…more |homepage
- nanomatch: Fast, minimal glob matcher for node.js. Similar to micromatch, minimatch and…more |homepage
Pull requests and stars are always welcome. For bugs and feature requests,please create an issue.
Please read thecontributing guidelines for advice on opening issues, pull requests, and coding standards.
If you need some help and can spent some cash, feel free tocontact me at CodeMentor.io too.
In short: If you want to contribute to that project, please follow these things
- Please DO NOT editREADME.md,CHANGELOG.md and.verb.md files. See"Building docs" section.
- Ensure anything is okey by installing the dependencies and run the tests. See"Running tests" section.
- Always use
npm run committo commit changes instead ofgit commit, because it is interactive and user-friendly. It usescommitizen behind the scenes, which follows Conventional Changelog idealogy. - Do NOT bump the version in package.json. For that we use
npm run release, which isstandard-version and follows Conventional Changelog idealogy.
Thanks a lot! :)
Recipes are just different use cases, written in form of README in human language. Showing some "Pro Tips" and tricks, answering common questions and so on. They look liketests, but in more readable and understandable way for humans - mostly for beginners that not reads or understand enough the README or API and tests.
- They are in form of folders in the root
recipes/folder: for examplerecipes/[short-meaningful-recipe-name]/. - In recipe folder should exist
README.mdfile - In recipe folder there may have actual js files, too. And should be working.
- The examples from the recipe README.md should also exist as separate
.jsfiles. - Examples in recipe folder also should be working and actual.
It would be great if you follow these steps when you want tofix, update or create a recipes. 😎
- Title for recipe idea should start with
[recipe]: for example[recipe] my awesome recipe - Title for new recipe (PR) should also start with
[recipe]. - Titles of Pull Requests or Issues for fixing/updating some existing recipes should start with
[recipe-fix].
It will help a lot, thanks in advance! 😋
Documentation and that readme is generated usingverb-generate-readme, which is averb generator, so you need to install both of them and then runverb command like that
$ npm install verbose/verb#dev verb-generate-readme --global && verbPlease don't edit the README directly. Any changes to the readme must be made in.verb.md.
Clone repository and run the following in that cloned directory
$ npm install && npm testCharlike Mike Reagent
Copyright © 2016-2017,Charlike Mike Reagent. Released under theMIT license.
This file was generated byverb-generate-readme, v0.4.1, on February 14, 2017.
Project scaffolded usingcharlike cli.
About
Most powerful, flexible and composable router for building enterprise RESTful APIs easily!
Topics
Resources
License
Code of conduct
Contributing
Security policy
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Sponsor this project
Uh oh!
There was an error while loading.Please reload this page.
Packages0
Uh oh!
There was an error while loading.Please reload this page.
Contributors2
Uh oh!
There was an error while loading.Please reload this page.