├── .gitignore ├── .npmignore ├── .travis.yml ├── History.md ├── LICENSE.txt ├── Makefile ├── README.md ├── example ├── app.js └── controllers │ ├── foo.js │ └── user.js ├── index.js ├── lib └── restful-router.js ├── logo.png ├── package.json └── test └── restful-router.test.js /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | coverage.html 3 | *.seed 4 | *.log 5 | *.csv 6 | *.dat 7 | *.out 8 | *.pid 9 | *.gz 10 | 11 | pids 12 | logs 13 | results 14 | 15 | node_modules 16 | npm-debug.log -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test/ 2 | coverage.html 3 | lib-cov/ 4 | Makefile 5 | .travis.yml 6 | logo.png 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.8' 4 | - '0.10' 5 | - '0.11' 6 | script: make test-coveralls 7 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | 2 | 0.1.1 / 2013-06-18 3 | ================== 4 | 5 | * support custom url id name by options.key params 6 | * remove 0.6 from travis 7 | 8 | 0.1.0 / 2013-06-18 9 | ================== 10 | 11 | * use PATCH method instead of PUT for update() 12 | * use blanket instead of jscover 13 | * update readme doc 14 | 15 | 0.0.2 / 2013-02-27 16 | ================== 17 | 18 | * add custom baseURL support, now can custom define "/users/:uid/post/:id" url 19 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | This software is licensed under the MIT License. 2 | 3 | Copyright (C) 2013 by fengmk2 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TESTS = test/*.test.js 2 | REPORTER = spec 3 | TIMEOUT = 1000 4 | MOCHA_OPTS = 5 | 6 | install: 7 | @npm install 8 | 9 | test: install 10 | @NODE_ENV=test ./node_modules/mocha/bin/mocha \ 11 | --reporter $(REPORTER) \ 12 | --timeout $(TIMEOUT) \ 13 | --bail \ 14 | $(MOCHA_OPTS) \ 15 | $(TESTS) 16 | 17 | test-cov: 18 | @rm -f coverage.html 19 | @$(MAKE) test MOCHA_OPTS='--require blanket' REPORTER=html-cov > coverage.html 20 | @$(MAKE) test MOCHA_OPTS='--require blanket' REPORTER=travis-cov 21 | @ls -lh coverage.html 22 | 23 | test-all: test test-cov 24 | 25 | test-coveralls: 26 | @$(MAKE) test 27 | @echo TRAVIS_JOB_ID $(TRAVIS_JOB_ID) 28 | @-$(MAKE) test MOCHA_OPTS='--require blanket' REPORTER=mocha-lcov-reporter | ./node_modules/coveralls/bin/coveralls.js 29 | 30 | .PHONY: test 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > [!CAUTION] 2 | > **This repository is archived and no longer actively maintained.** 3 | > 4 | > We are no longer accepting issues, feature requests, or pull requests. 5 | > For additional support or questions, please visit the [Express.js Discussions page](https://github.com/expressjs/express/discussions). 6 | 7 | 8 | restful-router [![Build Status](https://travis-ci.org/expressjs/restful-router.svg?branch=master)](http://travis-ci.org/expressjs/restful-router) [![Coverage Status](https://coveralls.io/repos/fengmk2/restful-router/badge.png)](https://coveralls.io/r/fengmk2/restful-router) 9 | ======= 10 | 11 | [![NPM](https://nodei.co/npm/restful-router.png?downloads=true&stars=true)](https://nodei.co/npm/restful-router/) 12 | 13 | ![logo](https://raw.github.com/fengmk2/restful-router/master/logo.png) 14 | 15 | Simple RESTful url router. 16 | 17 | ## Install 18 | 19 | ```bash 20 | $ npm install restful-router 21 | ``` 22 | 23 | ## RESTful routes 24 | 25 | ```js 26 | /** 27 | * Auto generate RESTful url routes. 28 | * 29 | * URL routes: 30 | * 31 | * GET /users[/] => user.list() 32 | * GET /users/new => user.new() 33 | * GET /users/:id => user.show() 34 | * GET /users/:id/edit => user.edit() 35 | * POST /users[/] => user.create() 36 | * PATCH /users/:id => user.update() 37 | * DELETE /users/:id => user.destroy() 38 | * 39 | * @param {Object} options 40 | * - {Object} app, must impl `app.get(), app.post(), app.patch(), app.delete()`. 41 | * - {String} name, resource's name. like `users, posts, tweets`. 42 | * - {Object} controller, controller module contains `CRUD List` methods. 43 | * - {String} [baseURL], default is '/'. e.g.: '/posts/:pid/' 44 | */ 45 | function restfulRouter(app, name, mod); 46 | ``` 47 | 48 | ## Usage 49 | 50 | ```js 51 | var restful = require('restful-router'); 52 | var connect = require('connect'); 53 | var urlrouter = require('urlrouter'); 54 | var user = require('./controllers/user'); 55 | var foo = require('./controllers/foo'); 56 | 57 | var server = connect( 58 | connect.query(), 59 | connect.bodyParser(), 60 | urlrouter(function (app) { 61 | app.get('/', function (req, res) { 62 | res.end('hello world'); 63 | }); 64 | 65 | /** 66 | * Users REST API 67 | * 68 | * GET /users => list() 69 | * GET /users/:id => show(req.params.id) 70 | * POST /users => create() 71 | * PATCH /users/:id => update(req.params.id) 72 | * DELETE /users/:id => delete(req.params.id) 73 | */ 74 | restful({ 75 | app: app, 76 | name: 'users', 77 | controller: user 78 | }); 79 | 80 | /** 81 | * Foos REST API 82 | * 83 | * GET /users/:uid/foos/:date => show(req.params.uid, req.params.date) 84 | */ 85 | restful({ 86 | app: app, 87 | key: 'date', 88 | baseURL: '/users/:uid', 89 | name: 'foos', 90 | controller: foo, 91 | }); 92 | 93 | }) 94 | ).listen(3000); 95 | ``` 96 | 97 | ## License 98 | 99 | (The MIT License) 100 | 101 | Copyright (c) 2013 fengmk2 <fengmk2@gmail.com> 102 | 103 | Permission is hereby granted, free of charge, to any person obtaining 104 | a copy of this software and associated documentation files (the 105 | 'Software'), to deal in the Software without restriction, including 106 | without limitation the rights to use, copy, modify, merge, publish, 107 | distribute, sublicense, and/or sell copies of the Software, and to 108 | permit persons to whom the Software is furnished to do so, subject to 109 | the following conditions: 110 | 111 | The above copyright notice and this permission notice shall be 112 | included in all copies or substantial portions of the Software. 113 | 114 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 115 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 116 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 117 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 118 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 119 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 120 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 121 | -------------------------------------------------------------------------------- /example/app.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * restful-router - example/app.js 3 | * Copyright(c) 2013 fengmk2 4 | * MIT Licensed 5 | */ 6 | 7 | "use strict"; 8 | 9 | /** 10 | * Module dependencies. 11 | */ 12 | 13 | var connect = require('connect'); 14 | var urlrouter = require('urlrouter'); 15 | var restful = require('../'); 16 | var user = require('./controllers/user'); 17 | var foo = require('./controllers/foo'); 18 | 19 | var server = connect( 20 | connect.query(), 21 | connect.bodyParser(), 22 | urlrouter(function (app) { 23 | app.get('/', function (req, res) { 24 | res.end('hello world'); 25 | }); 26 | 27 | /** 28 | * Users REST API 29 | * 30 | * GET /users => list() 31 | * GET /users/:id => show(req.params.id) 32 | * POST /users => create() 33 | * PATCH /users/:id => update(req.params.id) 34 | * DELETE /users/:id => delete(req.params.id) 35 | */ 36 | restful({ 37 | app: app, 38 | name: 'users', 39 | controller: user 40 | }); 41 | 42 | /** 43 | * Foos REST API 44 | * 45 | * GET /users/:uid/foos/:date => show(req.params.uid, req.params.date) 46 | */ 47 | restful({ 48 | app: app, 49 | key: 'date', 50 | baseURL: '/users/:uid', 51 | name: 'foos', 52 | controller: foo, 53 | }); 54 | 55 | }) 56 | ); 57 | 58 | if (process.env.NODE_ENV !== 'test') { 59 | server.listen(3000); 60 | } 61 | 62 | module.exports = server; -------------------------------------------------------------------------------- /example/controllers/foo.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * restful-router - example/controllers/foo.js 3 | * Copyright(c) 2013 fengmk2 4 | * MIT Licensed 5 | */ 6 | 7 | "use strict"; 8 | 9 | /** 10 | * Module dependencies. 11 | */ 12 | 13 | exports.list = function (req, res, next) { 14 | next(new Error('mock foo.list error, uid: ' + req.params.uid)); 15 | }; 16 | 17 | exports.show = function (req, res, next) { 18 | res.end(req.method + ' /users/:uid/foos/:date => show, params: ' + JSON.stringify(req.params)); 19 | }; 20 | -------------------------------------------------------------------------------- /example/controllers/user.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * restful-router - example/controllers/user.js 3 | * Copyright(c) 2013 fengmk2 4 | * MIT Licensed 5 | */ 6 | 7 | "use strict"; 8 | 9 | /** 10 | * Module dependencies. 11 | */ 12 | 13 | exports.list = function (req, res, next) { 14 | res.end(req.method + ' /users => list, query: ' + JSON.stringify(req.query)); 15 | }; 16 | 17 | exports.new = function (req, res, next) { 18 | res.end(req.method + ' /users/new => new, query: ' + JSON.stringify(req.query)); 19 | }; 20 | 21 | exports.show = function (req, res, next) { 22 | res.end(req.method + ' /users/:id => show, query: ' + JSON.stringify(req.query) + 23 | ', params: ' + JSON.stringify(req.params)); 24 | }; 25 | 26 | exports.edit = function (req, res, next) { 27 | res.end(req.method + ' /users/:id/edit => edit, query: ' + JSON.stringify(req.query) + 28 | ', params: ' + JSON.stringify(req.params)); 29 | }; 30 | 31 | exports.create = function (req, res, next) { 32 | res.end(req.method + ' /users => create, query: ' + JSON.stringify(req.query) + 33 | ', params: ' + JSON.stringify(req.params) + ', body: ' + JSON.stringify(req.body)); 34 | }; 35 | 36 | exports.update = function (req, res, next) { 37 | res.end(req.method + ' /users/:id => update, query: ' + JSON.stringify(req.query) + 38 | ', params: ' + JSON.stringify(req.params) + ', body: ' + JSON.stringify(req.body)); 39 | }; 40 | 41 | exports.destroy = function (req, res, next) { 42 | res.end(req.method + ' /users/:id => destroy, query: ' + JSON.stringify(req.query) + 43 | ', params: ' + JSON.stringify(req.params) + ', body: ' + JSON.stringify(req.body)); 44 | }; 45 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = process.env.RESTFUL_ROUTER_COV ? require('./lib-cov/restful-router') : require('./lib/restful-router'); -------------------------------------------------------------------------------- /lib/restful-router.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * restful-router - lib/restful-router.js 3 | * Copyright(c) 2013 fengmk2 4 | * MIT Licensed 5 | */ 6 | 7 | "use strict"; 8 | 9 | /** 10 | * Module dependencies. 11 | */ 12 | 13 | var debug = require('debug')('restful-router'); 14 | 15 | /** 16 | * Auto generate RESTful url routes. 17 | * 18 | * URL routes: 19 | * 20 | * GET /users[/] => user.list() 21 | * GET /users/new => user.new() 22 | * GET /users/:id => user.show() 23 | * GET /users/:id/edit => user.edit() 24 | * POST /users[/] => user.create() 25 | * PATCH /users/:id => user.update() 26 | * DELETE /users/:id => user.destroy() 27 | * 28 | * @param {Object} options 29 | * - {Object} app, must impl `app.get(), app.post(), app.put(), app.delete()`. 30 | * - {String} name, resource's name. like `users, posts, tweets`. 31 | * - {Object} controller, controller module contains `CRUD List` methods. 32 | * - {String} [baseURL], default is '/'. e.g.: '/posts/:pid/' 33 | * - {String} [key], default is 'id'. e.g.: 'name', 'date' 34 | */ 35 | module.exports = function restfulRouter(options) { 36 | var app = options.app; 37 | var controller = options.controller; 38 | var name = options.name || controller.name; 39 | var baseURL = options.baseURL || '/'; 40 | var key = options.key || 'id'; 41 | if (baseURL[baseURL.length - 1] !== '/') { 42 | baseURL += '/'; 43 | } 44 | var url = baseURL + name; 45 | debug('%s => %s', name, url); 46 | var routes = [ 47 | [ 'get', url, 'list' ], 48 | [ 'get', url + '/new', 'new' ], 49 | [ 'get', url + '/:' + key, 'show' ], 50 | [ 'get', url + '/:' + key + '/edit', 'edit' ], 51 | 52 | [ 'post', url, 'create' ], 53 | [ 'patch', url + '/:' + key, 'update' ], 54 | [ 'delete', url + '/:' + key, 'destroy' ], 55 | ]; 56 | 57 | for (var i = 0; i < routes.length; i++) { 58 | var route = routes[i]; 59 | var handle = controller[route[2]]; 60 | if (typeof handle === 'function') { 61 | app[route[0]](route[1], handle); 62 | debug('%s %s', route[0], route[1]); 63 | } 64 | } 65 | }; 66 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/expressjs/restful-router/7fbe28b6f4be8910b52830ae69e037ed2423befb/logo.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "restful-router", 3 | "version": "0.1.1", 4 | "description": "Simple RESTful url router.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "make test-all", 8 | "blanket": { 9 | "pattern": "restful-router/lib", 10 | "data-cover-flags": { 11 | "debug": false 12 | } 13 | }, 14 | "travis-cov": { 15 | "threshold": 100 16 | } 17 | }, 18 | "dependencies": { 19 | "debug": "0.7.2" 20 | }, 21 | "devDependencies": { 22 | "should": "*", 23 | "blanket": "*", 24 | "travis-cov": "*", 25 | "coveralls": "*", 26 | "mocha-lcov-reporter": "*", 27 | "urlrouter": ">=0.4.0", 28 | "supertest": "*", 29 | "connect": "*", 30 | "mocha": "*", 31 | "pedding": "*" 32 | }, 33 | "repository": { 34 | "type": "git", 35 | "url": "git://github.com/fengmk2/restful-router.git" 36 | }, 37 | "keywords": [ 38 | "restful-router", "restful", "urlrouter", "router", "mapping", "route" 39 | ], 40 | "author": "fengmk2 ", 41 | "license": "MIT" 42 | } 43 | -------------------------------------------------------------------------------- /test/restful-router.test.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * restful-router - test/restful-router.test.js 3 | * Copyright(c) 2012 fengmk2 4 | * MIT Licensed 5 | */ 6 | 7 | "use strict"; 8 | 9 | /** 10 | * Module dependencies. 11 | */ 12 | 13 | var pedding = require('pedding'); 14 | var should = require('should'); 15 | var request = require('supertest'); 16 | var restful = require('../'); 17 | var app = require('../example/app'); 18 | 19 | describe('restful-router.test.js', function () { 20 | it('should get /users => user.list', function (done) { 21 | done = pedding(2, done); 22 | request(app) 23 | .get('/users') 24 | .expect('GET /users => list, query: {}') 25 | .expect(200, done); 26 | 27 | request(app) 28 | .get('/users/') 29 | .expect('GET /users => list, query: {}') 30 | .expect(200, done); 31 | }); 32 | 33 | it('should get /users/new => user.new', function (done) { 34 | request(app) 35 | .get('/users/new') 36 | .expect('GET /users/new => new, query: {}') 37 | .expect(200, done); 38 | }); 39 | 40 | it('should get /users/:id => user.show', function (done) { 41 | request(app) 42 | .get('/users/123') 43 | .expect('GET /users/:id => show, query: {}, params: {"id":"123"}') 44 | .expect(200, done); 45 | }); 46 | 47 | it('should get /users/:id/edit => user.edit', function (done) { 48 | request(app) 49 | .get('/users/123/edit') 50 | .expect('GET /users/:id/edit => edit, query: {}, params: {"id":"123"}') 51 | .expect(200, done); 52 | }); 53 | 54 | it('should post /users => user.create', function (done) { 55 | done = pedding(2, done); 56 | request(app) 57 | .post('/users') 58 | .send({ name: 'foo' }) 59 | .expect('POST /users => create, query: {}, params: {}, body: {"name":"foo"}') 60 | .expect(200, done); 61 | 62 | request(app) 63 | .post('/users/') 64 | .send({ name: 'foo' }) 65 | .expect('POST /users => create, query: {}, params: {}, body: {"name":"foo"}') 66 | .expect(200, done); 67 | }); 68 | 69 | it('should put /users/:id => user.update', function (done) { 70 | request(app) 71 | .patch('/users/123') 72 | .send({ name: 'fooupdate' }) 73 | .expect('PATCH /users/:id => update, query: {}, params: {"id":"123"}, body: {"name":"fooupdate"}') 74 | .expect(200, done); 75 | }); 76 | 77 | it('should delete /users/:id => user.destroy', function (done) { 78 | request(app) 79 | .del('/users/123') 80 | .expect('DELETE /users/:id => destroy, query: {}, params: {"id":"123"}, body: {}') 81 | .expect(200, done); 82 | }); 83 | 84 | it('should post /users/:id => 404', function (done) { 85 | request(app) 86 | .post('/users/123') 87 | .send({ name: 'fooupdate' }) 88 | .expect(404, done); 89 | }); 90 | 91 | it('should put /users => 404', function (done) { 92 | request(app) 93 | .put('/users') 94 | .send({ name: 'fooupdate' }) 95 | .expect(404, done); 96 | }); 97 | 98 | it('should delete /users => 404', function (done) { 99 | request(app) 100 | .del('/users') 101 | .expect(404, done); 102 | }); 103 | 104 | it('should get /foos => 404', function (done) { 105 | request(app) 106 | .get('/foos') 107 | .expect(404, done); 108 | }); 109 | 110 | it('should get /users/mk2/foos => 500', function (done) { 111 | request(app) 112 | .get('/users/mk2/foos') 113 | .expect(/Error: mock foo.list error, uid: mk2/) 114 | .expect(500, done); 115 | }); 116 | 117 | it('should post /users/123/foos => 404', function (done) { 118 | request(app) 119 | .post('/users/123/foos') 120 | .expect(404, done); 121 | }); 122 | 123 | it('should get /users/123/foos/2013-06-18 => 200', function (done) { 124 | request(app) 125 | .get('/users/123/foos/2013-06-18') 126 | .expect('GET /users/:uid/foos/:date => show, params: {"uid":"123","date":"2013-06-18"}') 127 | .expect(200, done); 128 | }); 129 | 130 | }); --------------------------------------------------------------------------------