prorestify v1.0.9
prorestify
Tired of spending time organizing your project as you scale?
Tired of all these APIs with random URLs and returning random HTTP codes?
This module is made for you!
- prorestify is a RESTful scalable express router using promises
- Version 1.0.0
- Authors: Javier Rodriguez-Uria & Patrick Mettraux
Installation
$ npm install prorestify
Setup
let app = require('express')();
let prorestify = new require('prorestify')();
prorestify.router(app);
app.listen(8080)
Options
You can pass an object to change the default behaviour. Available options:
Name | Description | Default |
---|---|---|
routeFolder | The path where your routes are located | ./routes/ |
allowUnknown | Defines if you allow unknown parameters during your endpoints' validation | false |
abortEarly | Defines if you stop validation on the first error | false |
WorkFlow
The module execute actions in this order:
HTTP Request -> Endpoint's Middlewares -> Endpoint's Validation -> Endpoint's Controller
Endpoints
Structure-wise
Endpoints are based on the folder structure of your project. The modules will scan the routes
folder.
Directory structure example:
├── project/
│ ├── routes/
│ │ ├── v1/
│ │ │ ├── groups/
│ │ │ | ├── users.js
│ │ │ ├── users/
│ │ │ | ├── groups.js
│ │ │ ├── users.js
│ │ │ ├── groups.js
│ ├── app.js
In the example above, if you want to create the following routes:
GET->https://yourwebsite.com/v1/users
you will have to put the code inside the file located in /project/routes/v1/users.jsGET->https://yourwebsite.com/v1/groups
you will have to put the code inside the file located in /project/routes/v1/groups.jsGET->https://yourwebsite.com/v1/users/:userId/groups
you will have to put the code inside the file located in /project/routes/v1/users/groups.jsPOST->https://yourwebsite.com/v1/groups/:groupId/users
you will have to put the code inside the file located in /project/routes/v1/groups/users.js
Code-wise
Now that you know how to organize your files and folders according to the kind of endpoint you are trying to do we can have a look at the code itself.
Inside the right file the module is looking for a specific function named accordingly to the type of endpoint you want to do. This function must return an object containing at least a key named controller assigned to the controller function of this endpoint. This is a list on how to name you function accordingly to what kind of endpoint you want to create.
CREATE
A create is a POST call on a collection. Eg: POST->https://yourwebsite.com/v1/users/
module.exports = {
create: function(app) {
let controller = body => {
// put your code inside here
return app.yourUserCreationFunction(body);
};
return {
controller,
};
}
};
LIST
A list is a GET call on a collection. Eg: GET->https://yourwebsite.com/v1/users/
module.exports = {
list: function(app) {
let controller = query => {
// put your code inside here
return app.yourUserListingFunction(query);
};
return {
controller,
};
}
};
GET
A get is a GET call on an item. Eg: GET->https://yourwebsite.com/v1/users/:userId
module.exports = {
get: function(app) {
let controller = (query, params) => {
// put your code inside here
return app.yourGetUserFunction(params.userId);
};
return {
controller,
};
}
};
UPDATE
An update is a PUT call on an item. Eg: PUT->https://yourwebsite.com/v1/users/:userId
module.exports = {
update: function(app) {
let controller = (body, params) => {
// put your code inside here
return app.yourUpdateUserFunction(params.userId);
};
return {
controller,
};
}
};
PATCH
A patch is a PATCH call on an item. Eg: PATCH->https://yourwebsite.com/v1/users/:userId
module.exports = {
patch: function(app) {
let controller = (body, params) => {
// put your code inside here
return app.yourPatchUserFunction(params.userId);
};
return {
controller,
};
}
};
DELETE
A delete is a DELETE call on an item. Eg: DELETE->https://yourwebsite.com/v1/users/:userId
module.exports = {
del: function(app) {
let controller = (body, params) => {
// put your code inside here
return app.yourDeleteUserFunction(params.userId);
};
return {
controller,
};
}
};
POST action
A post action is a non-CRUD POST call. It can be on an item or a collection. You need to specify in the returned object the target using a key set to item or collection. You can use the same code for both if you want as you can see in the following example.
- Eg:
POST->https://yourwebsite.com/v1/users/:userId/hire
=> type: item - Eg:
POST->https://yourwebsite.com/v1/users/hire
=> type: collection
module.exports = {
hire: function(app) {
let controller = (body, params) => {
// put your code inside here
return app.yourHireUserFunction(params.userId);
};
return {
item: {
controller
},
collection: {
controller
}
};
}
};
GET action
A get action is a non-CRUD GET call. It can be on an item or a collection. You need to specify in the returned object the target using a key set to item or collection. You can use the same code for both if you want as you can see in the following example.
- Eg:
GET->https://yourwebsite.com/v1/users/:userId/home-address
=> type: item - Eg:
GET->https://yourwebsite.com/v1/users/home-address
=> type: collection
module.exports = {
getHomeAddress: function(app) {
let controller = (query, params) => {
// put your code inside here
return app.yourGetHomeAddressUserFunction(params.userId);
};
return {
item: {
controller
},
collection: {
controller
}
};
}
};
Naming Conventions
URLs
Our RESTful API is going to generate URLs with the collections' names using the plural form. This is also why your files and folders must be using the plural form too. We do not allow camelCase inside an URL. You must you dashes. Eg:
- GET=>https://yourwebsite.com/v1/users/:userId/homeAddress - This is WRONG
+ GET=>https://yourwebsite.com/v1/users/:userId/home-address - This is CORRECT
POST actions
POST actions are meant to be read like an order. For an URL like POST=>https://yourwebsite.com/v1/users/login
you must read it like "User login!". An Action is like an order this is why unless a GET call we do not preppend post to the function's name. (reminder, a get action looks like this getHomeAddress)
Because of this naming all CRUD endpoints cannot be called like a post action. This means the following:
POST=>https://yourwebsite.com/v1/users/create
- this url is impossiblePOST=>https://yourwebsite.com/v1/users/get
- this url is impossiblePOST=>https://yourwebsite.com/v1/users/list
- this url is impossiblePOST=>https://yourwebsite.com/v1/users/patch
- this url is impossiblePOST=>https://yourwebsite.com/v1/users/delete
- this url is impossible
Middlewares
This module handles middleware.
Endpoint's specific
If you want to use some middlewares only on one specific endpoint your endpoint function must return a middlewares array containing your middlewares. See the following example.
module.exports = {
create: function(app) {
let middlewares = [
yourSpecificMiddleware1,
yourSpecificMiddleware2,
];
let controller = body => {
// put your code inside here
return app.yourUserCreationFunction(body);
};
return {
controller,
middlewares
};
}
};
Validation
This module uses joi for the validation of what comes into an endpoint.
By default the module will reject all sent parameters that are not specified in your validation schema. If you want to add a validation schema your endpoint function must return a validation Joi object. See the following for an example.
var Joi = require('prorestify').joi;
module.exports = {
create: function(app) {
let validation= Joi.object().keys({
firstName: Joi.string().min(3).max(50)required(),
lastName: Joi.string().min(3).max(50)required(),
email: Joi.string().email(),
}),
let controller = body => {
// put your code inside here
return app.yourUserCreationFunction(body);
};
return {
controller,
validation
};
}
};