Hapi.js + Vue.js modelos mejorados

Hapi.js + Vue.js modelos mejorados

En este caso voy a explicar cómo añadir una ruta en el backend para gestionar usuarios.

Antes de nada vamos a instalar tres paquetes:

  • @hapi/joi para validar los datos de entrada
  • @hapi/boom para mostrar errores HTTP de forma sencilla
  • bcrypt para encriptar la contraseña
npm i @hapi/joi
npm i @hapi/boom
npm i bcrypt

Tendremos que añadir la ruta al manifest de glee

const manifest = {
	server: {
		port: Config.get( '/server/port' ),
	},
	register: {
		plugins: [
			{
				plugin: './api/home',
			},
			{
				plugin: './api/user',
			},
			{
				plugin: './plugins/db',
				options: Config.get( '/db' ),
			},
		],
	},
};

Ahora solo falta crear el controlador para las rutas de usuarios, dos en este caso:

  • GET /user/[user] para recuperar un usuario
  • PUT /user para crear un nuevo usuario

Lógicamente aún no hay nada de autenticación, por lo que cualquiera puede crear un usuario realizando una llamada PUT a la URL indicando userName, email y password.

Para comprobar la validez de los datos introducidos, usaremos joi. Usando las opciones de la ruta, indicaremos las reglas que deberá cumplir cada parámetro introducido. Así, para recuperar un usuario, se comprobará que user sea string, alfanumérico y que tenga una longitud de 3 a 20 caracteres:

validate: {
	params: {
		user: Joi.string().alphanum().min( 3 ).max( 20 ),
	},
},

Así de fácil.

En el caso de comprobar los datos de entrada de la llamada PUT, en vez de usar params, usaremos payload:

validate: {
	payload: {
		userName: Joi.string().alphanum().min( 3 ).max( 20 ).required(),
		email: Joi.string().email().required(),
		password: Joi.string().min( 8 ).required(),
	},
},

Por último mostrar el código para crear un nuevo usuario. Primero se comprueba si existe un usuario con ese nickname o email. Si es así, se devuelve error usando boom, si no, se genera la contraseña encriptada (aquí no me he molestado mucho en ello, ya lo haré más adelante), y se crea el usuario usando el método create de moongose:

/**
 * Route handler
 *
 * @param {object} request
 * @param {object} h Hapi object
 * @returns {object}
 */
handler: async( request, h ) => { // eslint-disable-line
	try {
		// TODO: Add role
		const user = await User
			.findOne( {
				$or: [
					{ userName: request.payload.username },
					{ email: request.payload.email },
				],
			} )
			.exec();
		if ( user ) {
			return Boom.badData( 'User exists' );
		}
		const password = await bcrypt.hash( request.payload.password, Config.get( '/hash/PASSWORD_HASH' ) );
		const userData = Object.assign( {}, request.payload, { password } );
		const newUser = await User.create( userData );

		return newUser ?
			{
				response: true,
				message: 'User created',
				userId: newUser.id,
			} :
			Boom.boomify( {
				response: false,
				message: 'There was an error during user creation',
			}, { statusCode: 400 } );
	} catch ( error ) {
		return Boom.badImplementation( 'Error', { error } );
	}
},

Como último apunte, he modificado la configuración para que admita ficheros .env con los datos necesarios.

Como siempre te puedes bajar el código aquí