cw-json-routing
Version:
JSON configurable MVC routing for express 4
578 lines (443 loc) • 14.7 kB
Markdown
Express JSON Routes
===================
Make routes much easier to use in MVC format.
I've been searching for a while for a nodejs routing solution with a:
- simple configuration,
- super performance,
- simple code,
- MVC organization,
- manage only routing, no other auto magic api creation
- customizable
- least possible dependency, which uses only underscore
This is: json-routes.
How It Works
-------------
**The basic concepts.**
Create a json file with your routing config and add code logic in a external file called *controller* , creating an MVC style structure.
I followed the Expressjs 4 routing standards, helping the developer to manage the routes creation and project organization faster and in the standard synthax.
Proposed Structure
-------------
This is an example of the default proposed structure, you can customize and change as you prefer.
```
project root
├── controllers/
│ ├── IndexController.js
│ ├── AuthController.js
│ ├── UsersController.js
├── policy/
│ │ ├── AuthorizationPolicy.js
│ │ ├── mymiddleware.js
├── routes/
│ │ │ ├── auth.json
│ │ │ ├── users.json
│ │ │ ├── index.json
├── app.js/
├── package.json/
├── README.md/
```
- **Controller**: contains the code's logic
- **Policy**: contains the function called before the controller = middleware
- **Routes**: contains all the `*.json` routing configuration. You can create as many *.json files as you need. By default, all routes within a file look up the corresponding controller (= modules = controllers = middleware) inside the controller's default directory `./controller` (or within the directory specified by the user), which uses the following naming format: Route-name (without .json) + "Controller.js (the first letter must be capitalized)".
> **EXAMPLE:**
> If you have a definition file called `users.json`, by default the route searches the controller `UsersControllers.js`.
For routes *auth.json* all routes call the controller `AuthController.js` ecc.. ecc..
> **NOTE:** this is a proposed structure but you can configure the module for your structure, you can change dir structure or add all routes in a single file.
### Creating JSON Configuration file
The routing file is encoded in the JSON format and by **default is in `./routes.`**
Router is created using this syntax:
`{ "RoutePath" : {"verb": {options} } }`
*Example of extended config*
```javascript
{
"routePath": {
"VERB": {
"route": "controller:method",
"policy": [
"controller:method",
]
}
},
"/admin": {
"GET": {
"route": "action",
"policy": [
"./demo/policycustom/test:check",
"test:all",
"subfolder/test2:index"
]
},
"POST": {
"route": "./mycustomfolder/controllername:index",
"policy": [
"./demo/policycustom/test:check",
"test:all",
"subfolder/test2:index"
],
"cors":true
}
},
...
more routes
}
```
### RoutePath
This is the routing path and it follows the express4 standard routing. You can use jolly character and other type syntax `/admin*,` `/admin/:name` etc. etc.;
### Verb
Relates to the call verb and can assume any valid http verbs like GET, POST, PUT, DELETE etc etc. You can add more verbs for a single routePath:
```javascript
{
"/admin": {
"GET": {
"route": "action",
"policy": [
"./demo/policycustom/test:check",
"test:all",
"subfolder/test2:index"
]
},
"POST": {
"route": "action",
"policy": [
"test:all",
]
}
}
```
`/admin` has GET and POST verbs.
### Route
Relates to `file:method` to call a route address.
By default, the routing search controller file inside the default controller directory is: `./controlles`, and you can change it using the global options explained in this document.
If the controller is not set, the routing will search a file called with the same name as the json file, with "Controller" as suffix.
> **Example:**
> If you have a definition file called `users.json`, by default the route searches the controller `UsersControllers.json`.
For routes *auth.json* all routes call the controller `AuthController.js` etc.. etc..
**Summarize route params**
If you omit the route params, the system routing assumes you have a default route controller path/name and a method called "index".
If you add only a parameter, it assumes that the controller is in the default directory with standard name `nameController.js` , and the parameter is the method that should be called. example route: "testall"
If the route params contain both values `controllername:method` (user:index) it will search the controller using the default directory path structured as controller name followed by method. For example, route: "user:index" searches for a controller called user.js with method index.
If you **need to call a controller in a subfolder**, simply add the path before the controller name. Example route: "/afolder/user:index", fire ./controller/afolder/user.js with method index.
If you **need to call a controller starting to your project root** simply add `.` before the path. Example route: "./lib/user:index", fire ./lib/user.js with method index.
### Policy
Is a module/function called before the controller (= middleware), by default it calls a file in ./policy named as you set in parameters "fileName" and a function named as you set in "functionName".
Example: policy: "auth/index" calls ./policy/auth.js and method index
**The syntax is the same as `route` params**
It can be a string for a single policy or an array for multiple policy files.
### CORS
Enable or disable Cross-origin resource sharing. default is false and disabled.
### Regex
You can set a regex to validate your route, however I discourage using it. Instead, I prefer to add this logic in the controller for better code speed.
```javascript
{
"/admin": {
"GET": {
"route": "action",
"policy": [
"./demo/policycustom/test:check",
"test:all",
"subfolder/test2:index"
],
"regex" : true | false
}
}
```
### Init Module
Configure the routing modules in your main js file, as any other nodes modules.
```javascript
// Includes
var express = require('express');
var app = express();
var routes = require('cw-json-routing'); // add module
...
// your code..
app.set(...);
app.use(...);
// this is the magic!
routes(app); //init modules
```
Change default Options
-----------------
When you initialize the module, you can specify a few options to customize the directory structure.
All are listed below with the default values. An explanation follows.
your main.js file
```javascript
// Includes
var express = require('express');
var app = express();
var routes = require('cw-json-routing'); // add module
// your code..
app.set(...);
app.use(...);
//define routes default options
var routeOptions = {
routesPath : "./routes",
controllerPath : "./controllers",
policyPath : "./policy",
cors : false,
displayRoute : true,
defaultAction : "index"
}
//init routes
routes(app, routeOptions);
```
- routesPath : the path to your routes folder. `Default ./routes`
- controllerPath : the path to your controller folder. `Default ./controllers`
- policyPath : the path to your policy folder. `Default ./policy`
- cors : enable cross origin resource sharing for all routes. (more cors options coming soon..). `Default false`
- displayRoute : display in console loading route info, `default true`.
- defaultAction : the function called in route if not specified. It's not so useful, but it's here!.`Default index`
If you omit routeOptions or some params it use defaults values.
Change json file Global Options
-----------------
If you need to change options for all routes only for a specific *.json file, you can set in your file the key `GLOBAL` as in the following example:
user.json
```javascript
{
"GLOBAL": {
"controllerPath": "./customdir",
"controller": "test",
"policyPath":"./lib",
"policy":["config:setall","config:connection"],
"baseUrl":"/user"
},
"/create": {
"PUT": {
"route": "index",
"policy": [
"auth:check",
"auth:adduserparams"
]
}
}
}
```
Example: route controller is ./customdir/UserController.js
- controllerPath: set a controller path for all routing file
- controller: set a custom name controller for all routing file
- policyPath: set a custom base policy dir for all rout
- policy: is an array of policy `file:action` to fire before controller
- baseUrl: is a base path for all url routes in file. Example, inside a file all routes start with `/api/*`, i can set base url as `/api`. Now all file routes start with `/api`. If i have a routes `/users`, it fired when user called `/api/users`
> **NOTE:** the key "GLOBAL" must be uppercase.
Full extended example
-----------------
*app.js*
```javascript
var express = require('express')
, app = express()
, port = process.env.PORT || 3000
, routing = require('./lib/route');
/**
* global options for routing
*
* set all file inside /api/* for a more cleaner code
*/
var routeOptions = {
routesPath: "./api/routes"
, controllersPath: "./api/controllers"
, policyPath: './api/policy'
, cors: false
};
/**
* init cw-json-routing
*/
routing(app, routeOptions);
/**
* standard express 4 routing
* yes.. you can use both routing together if you need
*/
var router = express.Router();
router.get('/express/', function (req, res) {
res.send(' this is a standard routing ');
});
app.use('/', router);
/**
* server start
*
* @type {http.Server}
*/
var server = app.listen(port, function () {
console.log('Listening on port %d', server.address().port);
});
```
This is the main file, we set routing and add global setting to use ./api as root directory
*./api/routes/users.json*
```javascript
{
"/banned": {
"GET": {
"route": "bannedCustom:index",
}
},
"/user": {
"GET": {
"route": "find",
"policy": [
"auth:check",
"auth:adduserparams"
]
},
"PUT": {
"route": "create",
"policy": [
"auth:check",
]
}
}
}
```
define the routes
*./api/controllers/UsersController.js*
```javascript
exports.index = function(req,res,next) {
res.send(' index routes ');
};
exports.create = function(req,res,next) {
res.send(' create routes params:'+req.params.name);
};
```
a basic controller logic
*./api/controllers/bannedCustom.js*
```javascript
exports.getbanned = function(req,res,next) {
res.send(' custom controller name ');
};
```
this is the controller with custom name
*./api/policy/auth.js*
```javascript
exports.check = function(req,res,next) {
if (!req.session.isLogged){
return res.redirect('http://'+req.hostname+":3000/403");
}
next();
};
```
Let me explain this policy: it checks if a user is logged, else set a redirect, so we can use the middleware to check ACL, authorization or get/set global vars, and this is very useful.
Create a Policy File and Pass vars to controller
-----------------
We encourage to use standard tecnique for best performance: use middleware.
using the full example described below we can create a standard policy file to attach a global var using `req`
*./api/policy/auth.js*
```javascript
exports.getbanned = function(req,res,next) {
if (!req.session.isLogged){
return res.redirect('http://'+req.hostname+":3000/403");
}
//use req
req.session.lastPing = new Date();
next();
};
```
**Read the value in the controller or policy**
*./api/controllers/bannedCustom.js*
```javascript
exports.getbanned = function(req,res,next) {
res.send(' custom controller name, middleware loaded at: '+req.session.lastPing);
};
```
Case: using middleware
-----------------
A special case: if we want to add an authentication before some route, take a look at this example:
```javascript
{
"/admin*": {
"GET": {
"route": "./policy/auth:check",
},
"POST": {
"route": "auth:check",
},
"PUT": {
"route": "auth:check",
},
"DELETE": {
"route": "auth:check",
},
},
"/admin/dashboard": {
"GET": {
"route": "getItem",
}
},
"/admin/user": {
"GET": {
"route": "find",
},
"PUT": {
"route": "create",
}
}
}}
```
All `admin*` route calls the controller `auth`, so now `auth:check` is executed before all `admin*` controller and it becomes
a policy (=middleware) and for a clear structure i put the file in policy dir.
An alternative example use the global file option:
```javascript
{
"GLOBAL": {
"policy":["auth:check"],
"baseUrl":"/admin"
},
"/dashboard": {
"GET": {
"route": "getItem",
}
},
"/user": {
"GET": {
"route": "find",
},
"PUT": {
"route": "create",
}
}
}}
```
Changelog 0.1.6
-------------
- added extra option for supporting custom global policy
Changelog 0.1.5
-------------
- added extra option for supporting custom global policy
Changelog 0.1.0
-------------
- minor fix
Changelog 0.1.0
-------------
- removed not working cors features for file definition and route... working on it.. cors for global setting work good.
Changelog 0.0.27
-------------
- improve log info
Changelog 0.0.26
-------------
- add `defaultAction`, not so useful, but it's here!.
- start cleaning code
- add `CORS` Global file options, to enable CORS only in specific *.json routes
- add `CORS` for specific routes.
- route log info display CORS status
Changelog 0.0.25
-------------
- improve route info on load, it can disabled with global options "displayRoute:false"
Changelog 0.0.24
-------------
- initial CORS support (look at "Change default Options"), more CORS options coming soon...
Changelog 0.23
-------------
- fix url union for windows platform
Changelog 0.0.20
-------------
- fix policy string is not added if global policy is set
- working test
Changelog 0.0.19
-------------
- add Global base Url
Changelog 0.0.17
-------------
- fix default route
- add mre error check
Changelog 0.0.15
-------------
- add goblal file policy (=middleware)
Changelog from version 0.0.13
-------------
- No longer compatible with <0.13 version
- new json syntax