When building an ReST API project, what would be the best project structure to keep things HAPI?
I currently have the following structure
-node_modules
-server
-server.js
-routes.js
-config
-config.js
-db.js
-controllers
-example.js
-models
-example.js
-server.js
-package.json
@puchesjr +1 I'm interested in what others have to say. I personally take advantage of hapi's ability to separate logic into modules. Here is the structure try to follow
node_modules
lib
middleware
db.js
routes
user
user-routes.js
user-controllers.js
models
server.js
index.js
package.json
index.js
I treat everything I can as a plugin.
I try to use the plugin system as well and make every entity a plugin e.g. user plugin, contact plugin and then my handlers/controllers orchestrates what needs to happen
I have built a core base to handle schema loading, database connections, logging, configuration and loading core routes. I can then load different logic into the core via plugins which encapsulate specific logic. My current structure looks like this
node_modules
lib
logger.js
schema.js
config.js
dataStore.js
methods.js
routes.js
core.js
plugins.js
routes
endpoints
admin.js
api.js
dataStore.js
handlers
admin.js
api.js
dataStore.js
plugins
plugin1
plugin2
schemata
schema1
schema2
schema3
the problem with that is if you get a lot of endpoints the endpoints and handlers folder will quickly be unhandleable
The project is quite large and I haven't encountered that problem
@simon-p-r what do you use to distinguish 'endpoints' from 'handlers'? Are the endpoints the main configurations and the handlers the functions handling each of the routes?
Yes the endpoints are the config object and the handler is the request and reply function? I use a module to require directories of exposed endpoints and concat the arrays to load into hapi. It works well so far.
Cool. That would jive with the way I like to order the addition of my routes. I like to have one big table where you can see all the exposed endpoints within an application or plugin with clear code-paths to where the functionality supporting the Endpoints resides.
Yeah it gives some clarity over what you have loaded, I also expose an endpoint that is a viewer into the route table which also helps when developing.
@simon-p-r i get annoyed quicker than you I guess :D
Still fresh to it all at the moment, time could change that though!?
let me know ;)
It's worth checking out a project called stimpy-medium, which built upon work done in hapi-ninja. Like many hapi projects, it's a plugin that can be optionally run standalone as a server. It splits configuration, controllers, and route definitions up across several folders, organized by creating files of the same name in those folders. For example, controllers/api.js would hold controllers for any routes defined in routes/api.js.
project/
- app.js
- node_modules/
- bussines/
-- reports.js
- dam/
-- reports.js
- dtm/
-- report.js
- utils/
- public/
-- assets/
--- jQuery/
--- css/
--- font/
--- images/
--- etc/
- routes/
-- index.js
- utils/
-- dateTimeUtil.js
- view/
-- js/
-- css/
-- index.html
One of the best applications that i used and its quite handy and configurable.
-node_modules(0)
-private (0)
--modules (1)
---exampleModule (2)
---exampleAPI.js (2)
---exampleRouteController.js (2)
---exampleRoutes.js (2)
With this approach everything is clear and the only thing that you need to do is to load the modules that way:
const gitRoutes = require('./private/modules/exampleModule/exampleRoutes.js');
for (var route in gitRoutes){
server.route(gitRoutes[route]);
}
module.exports = function() {
/**
* @param {GET} METHOD
* @param {req} exampleParam
* @param {req} exampleParam
* @param {req} exampleParam
* @param {reply} exampleParam
*/
return [
{
method: `GET`,
path: `/api/user/{id}`,
config: {
handler: exampleRouteController.getUser,
description: `exampleDescription`,
notes: `exampleNotes`,
tags: [`api`, `exampleTag`],
validate: {
payload: {
name: Joi.string().min(1).required(),
id: Joi.number().min(100).max(999999999)
}
}
}
},
];
}();
get: function getUser(request, reply) {
var promise = request.models.User.find({
where: {
email: request.params.email
}
});
var source = Rx.Observable.fromPromise(promise);
var subscription = source.subscribe(
(next)=> {
return reply({
payload: {
response: next,
status: 'OK'
}
});
},
(e)=> {
log('onError: %s', e);
subscription.dispose();
return reply({
payload: {
err: e,
status: 'ERROR'
}
});
});
},
function ExampleAPI() {
console.log('Loaded Module: ExampleAPI, Path: /private/modules/User/exampleAPI.js');
}
ExampleAPI.prototype.spawnChildProcessSequence = function(obj, timeout){
};
ExampleAPI.prototype.childProcess = function(dir, cmd, arg, timeout){
};
---exampleAPI.js - Main module logic
---exampleRouteController.js - Handler only for routes and create your custom reply structure(errors, events etc.)
---exampleRoutes.js - All your routes configuration
Please share your opinion with this approach i will be really happy :)))
Have a hapi..... happy coding :D
I have a lot of custom sql in my handlers that I would like to abstract out as it is becoming hard to manage. What would the right way to handle that? Also I saw a lot of people referencing controllers, what do they do?
@sfabriece Post your questions to hapijs/discuss.
Most helpful comment
One of the best applications that i used and its quite handy and configurable.
With this approach everything is clear and the only thing that you need to do is to load the modules that way:
---exampleAPI.js - Main module logic
---exampleRouteController.js - Handler only for routes and create your custom reply structure(errors, events etc.)
---exampleRoutes.js - All your routes configuration
for example if over time you found out that most of your users are using 3 modules and your api power is overwhelmed with only these 3 modules in this case you can spawn 3 independent api which serve only for these modules and you can pass via socket for availability of the current api and switch the client to preferred api if you wish to for load balancing.Most of the times you just spawn new docker container and you don't give a shit about that :D.
Anyway there are some people which prefer to create a self sustained system and others choose just to use available resources and enjoy its your choise. :))
Please share your opinion with this approach i will be really happy :)))
Have a hapi..... happy coding :D