diff --git a/docs/creating-endpoints.md b/docs/creating-endpoints.md index ac8fd36a..f1de12c7 100644 --- a/docs/creating-endpoints.md +++ b/docs/creating-endpoints.md @@ -146,7 +146,7 @@ module.exports = function (mongoose) { let collectionName = model.collectionDisplayName || model.modelName - Log.note("Generating Password Update endpoint for " + collectionName) + Log.info("Generating Password Update endpoint for " + collectionName) let handler = async function (request, h) { try { diff --git a/seed/user.model.js b/seed/user.model.js index 86e59ede..bfb31596 100644 --- a/seed/user.model.js +++ b/seed/user.model.js @@ -59,7 +59,7 @@ module.exports = function(mongoose) { const collectionName = model.collectionDisplayName || model.modelName - Log.note('Generating Password Update endpoint for ' + collectionName) + Log.info('Generating Password Update endpoint for ' + collectionName) const handler = async function(request, h) { try { diff --git a/utilities/handler-helper-factory.js b/utilities/handler-helper-factory.js index dd39ece3..8b7054b7 100644 --- a/utilities/handler-helper-factory.js +++ b/utilities/handler-helper-factory.js @@ -138,7 +138,7 @@ function generateListHandler(model, options, logger) { return async function(request, h) { try { - Log.log( + Log.info( 'params(%s), query(%s), payload(%s)', JSON.stringify(request.params), JSON.stringify(request.query), @@ -167,7 +167,7 @@ function generateFindHandler(model, options, logger) { return async function(request, h) { try { - Log.log( + Log.info( 'params(%s), query(%s), payload(%s)', JSON.stringify(request.params), JSON.stringify(request.query), @@ -200,7 +200,7 @@ function generateCreateHandler(model, options, logger) { return async function(request, h) { try { - Log.log( + Log.info( 'params(%s), query(%s), payload(%s)', JSON.stringify(request.params), JSON.stringify(request.query), @@ -228,7 +228,7 @@ function generateUpdateHandler(model, options, logger) { return async function(request, h) { try { - Log.log( + Log.info( 'params(%s), query(%s), payload(%s)', JSON.stringify(request.params), JSON.stringify(request.query), @@ -261,7 +261,7 @@ function generateDeleteHandler(model, options, logger) { return async function(request, h) { try { - Log.log( + Log.info( 'params(%s), query(%s), payload(%s)', JSON.stringify(request.params), JSON.stringify(request.query), @@ -310,7 +310,7 @@ function generateAssociationAddOneHandler( return async function(request, h) { try { - Log.log( + Log.info( addMethodName + ' + params(%s), query(%s), payload(%s)', JSON.stringify(request.params), JSON.stringify(request.query), @@ -357,7 +357,7 @@ function generateAssociationRemoveOneHandler( return async function(request, h) { try { - Log.log( + Log.info( removeMethodName + ' + params(%s), query(%s), payload(%s)', JSON.stringify(request.params), JSON.stringify(request.query), @@ -402,7 +402,7 @@ function generateAssociationAddManyHandler( return async function(request, h) { try { - Log.log( + Log.info( addMethodName + ' + params(%s), query(%s), payload(%s)', JSON.stringify(request.params), JSON.stringify(request.query), @@ -447,7 +447,7 @@ function generateAssociationRemoveManyHandler( return async function(request, h) { try { - Log.log( + Log.info( removeMethodName + ' + params(%s), query(%s), payload(%s)', JSON.stringify(request.params), JSON.stringify(request.query), @@ -492,7 +492,7 @@ function generateAssociationGetAllHandler( return async function(request, h) { try { - Log.log( + Log.info( getAllMethodName + ' + params(%s), query(%s), payload(%s)', JSON.stringify(request.params), JSON.stringify(request.query), diff --git a/utilities/handler-helper.js b/utilities/handler-helper.js index da6ed635..913754b6 100644 --- a/utilities/handler-helper.js +++ b/utilities/handler-helper.js @@ -159,7 +159,7 @@ async function _listHandler(model, request, Log) { Log ).lean() const result = await mongooseQuery.exec() - Log.log('Result: %s', JSON.stringify(result)) + Log.note('Result: %s', JSON.stringify(result)) return result } @@ -218,7 +218,7 @@ async function _listHandler(model, request, Log) { } } - Log.log('Result: %s', JSON.stringify(result)) + Log.note('Result: %s', JSON.stringify(result)) return result }) @@ -404,7 +404,7 @@ async function _findHandler(model, _id, request, Log) { } } - Log.log('Result: %s', JSON.stringify(result)) + Log.note('Result: %s', JSON.stringify(result)) return result } else { diff --git a/utilities/rest-helper-factory.js b/utilities/rest-helper-factory.js index 804cc392..325408d6 100644 --- a/utilities/rest-helper-factory.js +++ b/utilities/rest-helper-factory.js @@ -153,7 +153,7 @@ module.exports = function(logger, mongoose, server) { options = options || {} if (config.logRoutes) { - Log.note('Generating List endpoint for ' + collectionName) + Log.info('Generating List endpoint for ' + collectionName) } let resourceAliasForRoute @@ -280,7 +280,7 @@ module.exports = function(logger, mongoose, server) { const collectionName = model.collectionDisplayName || model.modelName if (config.logRoutes) { - Log.note('Generating Find endpoint for ' + collectionName) + Log.info('Generating Find endpoint for ' + collectionName) } let resourceAliasForRoute @@ -407,7 +407,7 @@ module.exports = function(logger, mongoose, server) { const collectionName = model.collectionDisplayName || model.modelName if (config.logRoutes) { - Log.note('Generating Create endpoint for ' + collectionName) + Log.info('Generating Create endpoint for ' + collectionName) } options = options || {} @@ -602,7 +602,7 @@ module.exports = function(logger, mongoose, server) { const collectionName = model.collectionDisplayName || model.modelName if (config.logRoutes) { - Log.note('Generating Delete One endpoint for ' + collectionName) + Log.info('Generating Delete One endpoint for ' + collectionName) } options = options || {} @@ -738,7 +738,7 @@ module.exports = function(logger, mongoose, server) { const collectionName = model.collectionDisplayName || model.modelName if (config.logRoutes) { - Log.note('Generating Delete Many endpoint for ' + collectionName) + Log.info('Generating Delete Many endpoint for ' + collectionName) } options = options || {} @@ -877,7 +877,7 @@ module.exports = function(logger, mongoose, server) { const collectionName = model.collectionDisplayName || model.modelName if (config.logRoutes) { - Log.note('Generating Update endpoint for ' + collectionName) + Log.info('Generating Update endpoint for ' + collectionName) } options = options || {} @@ -1052,7 +1052,7 @@ module.exports = function(logger, mongoose, server) { childModel.collectionDisplayName || childModel.modelName if (config.logRoutes) { - Log.note( + Log.info( 'Generating addOne association endpoint for ' + ownerModelName + ' -> ' + @@ -1263,7 +1263,7 @@ module.exports = function(logger, mongoose, server) { childModel.collectionDisplayName || childModel.modelName if (config.logRoutes) { - Log.note( + Log.info( 'Generating removeOne association endpoint for ' + ownerModelName + ' -> ' + @@ -1456,7 +1456,7 @@ module.exports = function(logger, mongoose, server) { childModel.collectionDisplayName || childModel.modelName if (config.logRoutes) { - Log.note( + Log.info( 'Generating addMany association endpoint for ' + ownerModelName + ' -> ' + @@ -1674,7 +1674,7 @@ module.exports = function(logger, mongoose, server) { childModel.collectionDisplayName || childModel.modelName if (config.logRoutes) { - Log.note( + Log.info( 'Generating removeMany association endpoint for ' + ownerModelName + ' -> ' + @@ -1870,7 +1870,7 @@ module.exports = function(logger, mongoose, server) { ownerModel.collectionDisplayName || ownerModel.modelName if (config.logRoutes) { - Log.note( + Log.info( 'Generating list association endpoint for ' + ownerModelName + ' -> ' + diff --git a/website/versioned_docs/version-2.2.x/creating-endpoints.md b/website/versioned_docs/version-2.2.x/creating-endpoints.md new file mode 100644 index 00000000..7df73025 --- /dev/null +++ b/website/versioned_docs/version-2.2.x/creating-endpoints.md @@ -0,0 +1,214 @@ +--- +id: version-2.0.x-creating-endpoints +title: Creating Endpoints +sidebar_label: Creating Endpoints +original_id: creating-endpoints +--- + +Creating endpoints with rest-hapi can be accomplished three different ways: + +- generating endpoints based off of [model definitions](#model-endpoints) +- defining [standalone endpoints](#standalone-endpoints) +- [adding endpoints](#additional-endpoints) to a model + +## Model endpoints +Restful endpoints are automatically generated based off of any mongoose models that you add to your ``models`` directory with the file structure of ``{model name}.model.js``. These models must adhere to the following format: + +```javascript +module.exports = function (mongoose) { + let Schema = new mongoose.Schema({ + /*fill in schema fields*/ + }); + + Schema.statics = { + collectionName: /*your model name*/, + routeOptions: {} + }; + + return Schema; +}; +``` + +As a concrete example, here is a ``user`` model: + +```javascript +// models/user.model.js +module.exports = function (mongoose) { + let modelName = "user"; + let Types = mongoose.Schema.Types; + let Schema = new mongoose.Schema({ + email: { + type: Types.String, + required: true, + unique: true + }, + password: { + type: Types.String, + required: true, + exclude: true, + allowOnUpdate: false + } + }); + + Schema.statics = { + collectionName: modelName, + routeOptions: {} + }; + + return Schema; +}; +``` + +This will generate the following CRUD endpoints: + +``` +DELETE /user Delete multiple users +POST /user Create one or more new users +GET /user Get a list of users +DELETE /user/{_id} Delete a user +GET /user/{_id} Get a specific user +PUT /user/{_id} Update a user +``` + +Association endpoints can also be generated based on model definitions, see the [Associations](associations.md) section. + +> **NOTE:** If your ``models`` directory is not in your projects root directory, you will need to +> specify the path (relative to your projects root directory) by assigning the path to the +> [`config.modelPath`](configuration.md#modelpath) property and you will need to set the +> [`config.absoluteModelPath`](configuration.md#absolutemodelpath) property to ``true``. + +> **NOTE:** For standalone and additional endpoints, make sure to note the validation specifics +> mentioned [here](validation.md#using-joi-in-custom-endpoints) + +## Standalone endpoints +Standalone endpoints can be generated by adding files to your ``api`` directory. The content of these files must adhere to the following format: + +```javascript +module.exports = function (server, mongoose, logger) { + /*register hapi endpoint here*/ +}; +``` + +As a concrete example, here is a ``hello-world`` endpoint that will show in the generated swagger docs: + +```javascript +// api/hello.js +module.exports = function (server, mongoose, logger) { + server.route({ + method: 'GET', + path: '/hello-world', + config: { + handler: function(request, h) { return "Hello World" }, + tags: ['api'], + plugins: { + 'hapi-swagger': {} + } + } + }) +} +``` + +> **NOTE:** If your ``api`` directory is not in your projects root directory, you will need to specify the path (relative to your projects root directory) by assigning the path to the [`config.apiPath`](configuration.md#apipath) property and you will need to set the [`config.absoluteApiPath`](configuration.md#absoluteapipath) property to ``true``. + +## Additional endpoints +If endpoints beyond the generated CRUD endpoints are needed for a model, they can easily be added as an item in the ``routeOptions.extraEndpoints`` array. The endpoint logic should be contained within a function using the footprint: ``function (server, model, options, logger)``. For example, if we wanted to add a ``Password Update`` endpoint to the ``user`` model, it could look like this: + +```javascript +// models/user.model.js +let Joi = require('@hapi/joi') +let bcrypt = require('bcrypt') +let RestHapi = require('rest-hapi') + +module.exports = function (mongoose) { + let modelName = "user" + let Types = mongoose.Schema.Types + let Schema = new mongoose.Schema({ + email: { + type: Types.String, + required: true, + unique: true + }, + password: { + type: Types.String, + required: true, + exclude: true, + allowOnUpdate: false + } + }) + + Schema.statics = { + collectionName:modelName, + routeOptions: { + extraEndpoints: [ + // Password Update Endpoint + function (server, model, options, logger) { + const Log = logger.bind("Password Update") + let Boom = require('@hapi/boom') + + let collectionName = model.collectionDisplayName || model.modelName + + Log.info("Generating Password Update endpoint for " + collectionName) + + let handler = async function (request, h) { + try { + let hashedPassword = model.generatePasswordHash(request.payload.password) + let result = await RestHapi.update(model, request.params._id, {password: hashedPassword}, Log) + if (result) { + return h.response("Password updated.").code(200) + } + else { + throw Boom.notFound("No resource was found with that id.") + } + } catch(err) { + if (!err.isBoom) { + Log.error(err) + throw Boom.badImplementation(err) + } else { + throw err + } + } + } + + server.route({ + method: 'PUT', + path: '/user/{_id}/password', + config: { + handler: handler, + auth: null, + description: 'Update a user\'s password.', + tags: ['api', 'User', 'Password'], + validate: { + params: { + _id: Joi.objectId().required() + }, + payload: { + password: Joi.string().required() + .description('The user\'s new password') + } + }, + plugins: { + 'hapi-swagger': { + responseMessages: [ + {code: 200, message: 'Success'}, + {code: 400, message: 'Bad Request'}, + {code: 404, message: 'Not Found'}, + {code: 500, message: 'Internal Server Error'} + ] + } + } + } + }) + } + ] + }, + + generatePasswordHash: function(password) { + let salt = bcrypt.genSaltSync(10) + let hash = bcrypt.hashSync(password, salt) + return hash + } + } + + return Schema +} +```