Permalink
Fetching contributors…
Cannot retrieve contributors at this time
208 lines (178 sloc) 6.37 KB
id title sidebar_label
creating-endpoints
Creating Endpoints
Creating Endpoints

Creating endpoints with rest-hapi can be accomplished three different ways:

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 the config.modelPath property and you will need to set the config.absoluteModelPath property to true.

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 the config.apiPath property and you will need to set the config.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:

// 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('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
}