Creating Endpoints
Creating endpoints with rest-hapi can be accomplished three different ways:
- generating endpoints based off of model definitions
- defining standalone endpoints
- adding 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:
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:
// 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 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 theconfig.modelPath
property and you will need to set theconfig.absoluteModelPath
property totrue
.
NOTE: For standalone and additional endpoints, make sure to note the validation specifics mentioned here
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:
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:
// 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 theconfig.apiPath
property and you will need to set theconfig.absoluteApiPath
property totrue
.
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:
// models/user.model.js
let Joi = require('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.note("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
}