Categories
Hapi

Server-Side Development with Hapi.js — 400 Series Errors

Hapi.js is a small Node framework for developing back end web apps.

In this article, we’ll look at how to create back end apps with Hapi.js.

Conflict

We can return a 409 response ith @hapi/boom with the Boom.conflict method by writing:

const Hapi = require('@hapi/hapi');  
const Boom = require('@hapi/boom');  
const init = async () => {  
  const server = new Hapi.Server({  
    port: 3000,  
    host: '0.0.0.0'  
  });  
  server.route({  
    method: 'GET',  
    path: '/',  
    handler(request, h) {        
      throw Boom.conflict('conflict');  
    }  
  });  
  await server.start();  
  console.log('Server running at:', server.info.uri);  
};  
process.on('unhandledRejection', (err) => {  
  console.log(err);  
  process.exit(1);  
});  
init();

Then when we go to the / route, we get:

{"statusCode":409,"error":"Conflict","message":"conflict"}

as the response.

Resource Gone Error

We can return the resource gone error by writing:

const Hapi = require('@hapi/hapi');  
const Boom = require('@hapi/boom');  
const init = async () => {  
  const server = new Hapi.Server({  
    port: 3000,  
    host: '0.0.0.0'  
  });  
  server.route({  
    method: 'GET',  
    path: '/',  
    handler(request, h) {        
      throw Boom.resourceGone('gone');  
    }  
  });  
  await server.start();  
  console.log('Server running at:', server.info.uri);  
};  
process.on('unhandledRejection', (err) => {  
  console.log(err);  
  process.exit(1);  
});  
init();

We call Boom.resourceGone to create the error object. The argument we pass in is the message.

Then we get:

{"statusCode":410,"error":"Gone","message":"gone"}

as the response when we go to / .

Length Required

We call the Boom.lengthRequired method to return a 411 response.

For example, we can write:

const Hapi = require('@hapi/hapi');  
const Boom = require('@hapi/boom');  
const init = async () => {  
  const server = new Hapi.Server({  
    port: 3000,  
    host: '0.0.0.0'  
  });  
  server.route({  
    method: 'GET',  
    path: '/',  
    handler(request, h) {        
      throw Boom.lengthRequired('length required');  
    }  
  });  
  await server.start();  
  console.log('Server running at:', server.info.uri);  
};  
process.on('unhandledRejection', (err) => {  
  console.log(err);  
  process.exit(1);  
});  
init();

We pass in the message as the argument.

Then we get:

{"statusCode":411,"error":"Length Required","message":"length required"}

as the response.

Precondition Failed Error

We can call Boom.preconditionFailed to returns a 412 response.

For instance, we can write:

const Hapi = require('@hapi/hapi');  
const Boom = require('@hapi/boom');  
const init = async () => {  
  const server = new Hapi.Server({  
    port: 3000,  
    host: '0.0.0.0'  
  });  
  server.route({  
    method: 'GET',  
    path: '/',  
    handler(request, h) {        
      throw Boom.preconditionFailed('precondition failed');  
    }  
  });  
  await server.start();  
  console.log('Server running at:', server.info.uri);  
};  
process.on('unhandledRejection', (err) => {  
  console.log(err);  
  process.exit(1);  
});  
init();

to pass in a message.

Entity Too Large

To return a 413 entity too large response, we call Boom.entityTooLarge .

For instance, we can write:

const Hapi = require('@hapi/hapi');  
const Boom = require('@hapi/boom');  
const init = async () => {  
  const server = new Hapi.Server({  
    port: 3000,  
    host: '0.0.0.0'  
  });  
  server.route({  
    method: 'GET',  
    path: '/',  
    handler(request, h) {        
      throw Boom.entityTooLarge('too big');  
    }  
  });  
  await server.start();  
  console.log('Server running at:', server.info.uri);  
};  
process.on('unhandledRejection', (err) => {  
  console.log(err);  
  process.exit(1);  
});  
init();

to throw the error.

And we get:

{"statusCode":413,"error":"Request Entity Too Large","message":"too big"}

as the response.

Unsupported Media Type

We can call Boom.unsupportedMediaType to throw a 415 unsupported media type error:

const Hapi = require('@hapi/hapi');  
const Boom = require('@hapi/boom');  
const init = async () => {  
  const server = new Hapi.Server({  
    port: 3000,  
    host: '0.0.0.0'  
  });  
  server.route({  
    method: 'GET',  
    path: '/',  
    handler(request, h) {        
      throw Boom.unsupportedMediaType('that media is not supported');  
    }  
  });  
  await server.start();  
  console.log('Server running at:', server.info.uri);  
};  
process.on('unhandledRejection', (err) => {  
  console.log(err);  
  process.exit(1);  
});  
init();

Then we get:

{"statusCode":415,"error":"Unsupported Media Type","message":"that media is not supported"}

as the response.

Conclusion

We can return kinds of error responses with Hapi Boom.

Categories
Hapi

Server-Side Development with Hapi.js — Throw 400-Series Errors

Hapi.js is a small Node framework for developing back end web apps.

In this article, we’ll look at how to create back end apps with Hapi.js.

Method Not Allowed Error

We can throw a method not allowed error with @hapi/boom ‘s methodNotAllowed method.

For instance, we can write:

const Hapi = require('@hapi/hapi');
const Boom = require('@hapi/boom');

const init = async () => {
  const server = new Hapi.Server({
    port: 3000,
    host: '0.0.0.0'
  });

  server.route({
    method: 'GET',
    path: '/',
    handler(request, h) {
      throw Boom.methodNotAllowed('that method is not allowed');
    }
  });

  await server.start();
  console.log('Server running at:', server.info.uri);
};
process.on('unhandledRejection', (err) => {
  console.log(err);
  process.exit(1);
});
init();

Then we get:

{"statusCode":405,"error":"Method Not Allowed","message":"that method is not allowed"}

as the response.

We can set the Allow header with the 3rd argument.

To do this, we write:

const Hapi = require('@hapi/hapi');
const Boom = require('@hapi/boom');

const init = async () => {
  const server = new Hapi.Server({
    port: 3000,
    host: '0.0.0.0'
  });

  server.route({
    method: 'GET',
    path: '/',
    handler(request, h) {
      throw Boom.methodNotAllowed('that method is not allowed', 'data', ['foo', 'bar']);
    }
  });

  await server.start();
  console.log('Server running at:', server.info.uri);
};
process.on('unhandledRejection', (err) => {
  console.log(err);
  process.exit(1);
});
init();

Now we should see that Allow response header has foo, bar as its value.

Not Acceptable Error

We can call the Boom.notAcceptable method to return a 406 error.

For instance, we can write:

const Hapi = require('@hapi/hapi');
const Boom = require('@hapi/boom');

const init = async () => {
  const server = new Hapi.Server({
    port: 3000,
    host: '0.0.0.0'
  });

  server.route({
    method: 'GET',
    path: '/',
    handler(request, h) {
      throw Boom.notAcceptable('unacceptable');
    }
  });

  await server.start();
  console.log('Server running at:', server.info.uri);
};
process.on('unhandledRejection', (err) => {
  console.log(err);
  process.exit(1);
});
init();

Then we get:

{
    "statusCode": 406,
    "error": "Not Acceptable",
    "message": "unacceptable"
}

as the response.

We can also pass in extra data into the 2nd argument.

Proxy Required Error

We call the Boom.proxyAuthRequired method to return a 407 response.

For instance, we can write:

const Hapi = require('@hapi/hapi');
const Boom = require('@hapi/boom');

const init = async () => {
  const server = new Hapi.Server({
    port: 3000,
    host: '0.0.0.0'
  });

  server.route({
    method: 'GET',
    path: '/',
    handler(request, h) {
      throw Boom.proxyAuthRequired('auth missing');
    }
  });

  await server.start();
  console.log('Server running at:', server.info.uri);
};
process.on('unhandledRejection', (err) => {
  console.log(err);
  process.exit(1);
});
init();

Then we get:

{
    "statusCode": 407,
    "error": "Proxy Authentication Required",
    "message": "auth missing"
}

as the response.

We can also pass in more data as the 2nd argument.

Request Timeout

We can throw a request timeout error with the Boom.clientTimeout method:

const Hapi = require('@hapi/hapi');
const Boom = require('@hapi/boom');

const init = async () => {
  const server = new Hapi.Server({
    port: 3000,
    host: '0.0.0.0'
  });

  server.route({
    method: 'GET',
    path: '/',
    handler(request, h) {
      throw Boom.clientTimeout('timed out');
    }
  });

  await server.start();
  console.log('Server running at:', server.info.uri);
};
process.on('unhandledRejection', (err) => {
  console.log(err);
  process.exit(1);
});
init();

Then we get:

{"statusCode":408,"error":"Request Time-out","message":"timed out"}

as the response.

We can also pass in more data as the 2nd argument.

Conclusion

We can throw many types of errors easily with the @hapi/boom module.

Categories
Hapi

Server-Side Development with Hapi.js — Throwing Errors

Hapi.js is a small Node framework for developing back end web apps.

In this article, we’ll look at how to create back end apps with Hapi.js.

Throw Unauthorized Errors

We can throw unauthorized errors with the @hapi/boom module.

It has the Boom.unauthorized method to throw the error.

For instance, we can write:

const Hapi = require('@hapi/hapi');
const Boom = require('@hapi/boom');

const init = async () => {
  const server = new Hapi.Server({
    port: 3000,
    host: '0.0.0.0'
  });

  server.route({
    method: 'GET',
    path: '/',
    handler(request, h) {
      throw Boom.unauthorized('invalid password');
    }
  });

  await server.start();
  console.log('Server running at:', server.info.uri);
};
process.on('unhandledRejection', (err) => {
  console.log(err);
  process.exit(1);
});
init();

We call Boom.unauthorized method with a message to create the 401 response.

Now we should get:

{"statusCode":401,"error":"Unauthorized","message":"invalid password"}

when we make a GET request to the / route.

We can pass in extra data in the 3rd argument.

For example, we can write:

const Hapi = require('@hapi/hapi');
const Boom = require('@hapi/boom');

const init = async () => {
  const server = new Hapi.Server({
    port: 3000,
    host: '0.0.0.0'
  });

  server.route({
    method: 'GET',
    path: '/',
    handler(request, h) {
      throw Boom.unauthorized('invalid password', 'sample', { ttl: 0, cache: null, foo: 'bar' });
    }
  });

  await server.start();
  console.log('Server running at:', server.info.uri);
};
process.on('unhandledRejection', (err) => {
  console.log(err);
  process.exit(1);
});
init();

The object in the 3rd argument will be added to the attributes property of the response.

So we get:

{"statusCode":401,"error":"Unauthorized","message":"invalid password","attributes":{"ttl":0,"cache":"","foo":"bar","error":"invalid password"}}

as the response.

The 2nd argument will be in the Www-Authenticate response header.

We get:

sample ttl="0", cache="", foo="bar", error="invalid password"

as its value.

Payment Required Response

We can return a 402 response with the Boom.paymentRequired method.

For instance, we can write:

const Hapi = require('@hapi/hapi');
const Boom = require('@hapi/boom');

const init = async () => {
  const server = new Hapi.Server({
    port: 3000,
    host: '0.0.0.0'
  });

  server.route({
    method: 'GET',
    path: '/',
    handler(request, h) {
      throw Boom.paymentRequired('bandwidth used');
    }
  });

  await server.start();
  console.log('Server running at:', server.info.uri);
};
process.on('unhandledRejection', (err) => {
  console.log(err);
  process.exit(1);
});
init();

We call Boom.paymentRequied with the message that we want to return.

Then we get:

{"statusCode":402,"error":"Payment Required","message":"bandwidth used"}

as the response.

Forbidden Error

We can throw a forbidden error with the Boom.forbidden method:

const Hapi = require('@hapi/hapi');
const Boom = require('@hapi/boom');

const init = async () => {
  const server = new Hapi.Server({
    port: 3000,
    host: '0.0.0.0'
  });

  server.route({
    method: 'GET',
    path: '/',
    handler(request, h) {
      throw Boom.forbidden('try again some time');
    }
  });

  await server.start();
  console.log('Server running at:', server.info.uri);
};
process.on('unhandledRejection', (err) => {
  console.log(err);
  process.exit(1);
});
init();

Then we get:

{"statusCode":403,"error":"Forbidden","message":"try again some time"}

as the response.

Not Found Error

We can return a 404 error with the Boom.notFound method:

const Hapi = require('@hapi/hapi');
const Boom = require('@hapi/boom');

const init = async () => {
  const server = new Hapi.Server({
    port: 3000,
    host: '0.0.0.0'
  });

  server.route({
    method: 'GET',
    path: '/',
    handler(request, h) {
      throw Boom.notFound('missing');
    }
  });

  await server.start();
  console.log('Server running at:', server.info.uri);
};
process.on('unhandledRejection', (err) => {
  console.log(err);
  process.exit(1);
});
init();

Then we should get:

{"statusCode":404,"error":"Not Found","message":"missing"}

as the response.

Conclusion

We can throw various kinds of errors easily with the @hapi/boom module.

Categories
Hapi

Server-Side Development with Hapi.js — Base64 and Errors

Hapi.js is a small Node framework for developing back end web apps.

In this article, we’ll look at how to create back end apps with Hapi.js.

Base64 Encoding

We can handle base64 encoding and decoding with the @hapi/b64 module.

For instance, we can write:

const Path = require('path');
const Hapi = require('@hapi/hapi');
const Fs = require('fs');
const B64 = require('@hapi/b64');

const init = async () => {
  const server = new Hapi.Server({
    port: 3000,
    host: '0.0.0.0',
    debug: { request: ['error'] }
  });

  server.route({
    method: 'GET',
    path: '/',
    handler(request, h) {
      const stream = Fs.createReadStream(Path.join(__dirname, 'package.json'));
      const encoder = new B64.Encoder();
      stream.pipe(encoder).pipe(process.stdout);
      return 'success'
    }
  });

  await server.start();
  console.log('Server running at:', server.info.uri);
};
process.on('unhandledRejection', (err) => {
  console.log(err);
  process.exit(1);
});
init();

We call Fs.createReadStream to create the srean.

Then we create the encoder object with the B64.Encoder constructor.

And then we pass the encoder to the stream.pipe method to encode the stream into a base64 string.

And then we pass that into stdout with another pipe call.

Base64 Decoding

We can decode base64 text into its original content with the @hapi/b64 module.

To do this, we write:

const Path = require('path');
const Hapi = require('@hapi/hapi');
const Fs = require('fs');
const B64 = require('@hapi/b64');

const init = async () => {
  const server = new Hapi.Server({
    port: 3000,
    host: '0.0.0.0',
    debug: { request: ['error'] }
  });

  server.route({
    method: 'GET',
    path: '/',
    handler(request, h) {
      const stream = Fs.createReadStream(Path.join(__dirname, 'encodedfile.b64'));
      const decoder = new B64.Decoder();
      stream.pipe(decoder).pipe(process.stdout);
      return 'success'
    }
  });

  await server.start();
  console.log('Server running at:', server.info.uri);
};
process.on('unhandledRejection', (err) => {
  console.log(err);
  process.exit(1);
});
init();

We call the Fs.createReadStream to create the read stream and read the file with the given path.

Then we create the decoder object with the B64.Decoder constructor.

And then we call stream.pipe with the decoder to do the decoding.

Then we call pipe again to pipe the decoded content to stdout .

Error Response

We can add a login route easily with the @hapi/boom module.

To use it, we write:

const Hapi = require('@hapi/hapi');
const Boom = require('@hapi/boom');

const init = async () => {
  const server = new Hapi.Server({
    port: 3000,
    host: '0.0.0.0'
  });

  server.route({
    method: 'GET',
    path: '/',
    handler(request, h) {
      throw Boom.badRequest('invalid query');
    }
  });

  await server.start();
  console.log('Server running at:', server.info.uri);
};
process.on('unhandledRejection', (err) => {
  console.log(err);
  process.exit(1);
});
init();

We call Boom.badRequest to create an Error object that we can throw.

It takes the error message as the argument.

Then we throw that and we should see:

{"statusCode":400,"error":"Bad Request","message":"invalid query"}

returned as the response.

We can check if an object is an instance of the Boom.Boom constructor by writing:

const Hapi = require('@hapi/hapi');
const Boom = require('@hapi/boom');

const init = async () => {
  const server = new Hapi.Server({
    port: 3000,
    host: '0.0.0.0'
  });

  server.route({
    method: 'GET',
    path: '/',
    handler(request, h) {
      const err = Boom.badRequest('invalid query');
      return Boom.isBoom(err);
    }
  });

  await server.start();
  console.log('Server running at:', server.info.uri);
};
process.on('unhandledRejection', (err) => {
  console.log(err);
  process.exit(1);
});
init();

We call Boom.isBoom to do the check.

It should return true since err is an instance of Boom.Boom .

Conclusion

We can return error responses easily with the @hapi/boom module.

The @hapi/base64 module lets us encode and decode base64 content.

Categories
Hapi

Server-Side Development with Hapi.js — Handling Accept Headers

Hapi.js is a small Node framework for developing back end web apps.

In this article, we’ll look at how to create back end apps with Hapi.js.

Parse Accept Header

We can parse the Accept header with the @hapi/accept module.

For example, we can write:

const Path = require('path');
const Hapi = require('@hapi/hapi');
const Accept = require('@hapi/accept');

const init = async () => {
  const server = new Hapi.Server({
    port: 3000,
    host: '0.0.0.0',
    debug: { request: ['error'] }
  });

  server.route({
    method: 'GET',
    path: '/',
    handler(request, h) {
      const charset = Accept.charsets("iso-8859-5, unicode-1-1;q=0.8");
      return charset
    }
});

  await server.start();
  console.log('Server running at:', server.info.uri);
};
process.on('unhandledRejection', (err) => {
  console.log(err);
  process.exit(1);
});
init();

We call Accept.charsets to return an array of encodings.

charset is [“iso-8859–5”,”unicode-1–1"] .

The Accept-Encoding header is checked to get the returned value

We can use this array in our requests handler to check the preferred encoding.

We can pass in a 2nd argument with the values we want to compare against the string in the first argument:

const Path = require('path');
const Hapi = require('@hapi/hapi');
const Accept = require('@hapi/accept');

const init = async () => {
  const server = new Hapi.Server({
    port: 3000,
    host: '0.0.0.0',
    debug: { request: ['error'] }
  });

  server.route({
    method: 'GET',
    path: '/',
    handler(request, h) {
      const encoding = Accept.encoding("gzip, deflate, sdch", ["deflate", "identity"]);
      return encoding
    }
});

  await server.start();
  console.log('Server running at:', server.info.uri);
};
process.on('unhandledRejection', (err) => {
  console.log(err);
  process.exit(1);
});
init();

We have the deflate in the string in the first argument and 'deflate' in the 2nd argument, so it’ll return 'deflate' .

We can do the same comparisons for language.

For instance, we can write:

const Path = require('path');
const Hapi = require('@hapi/hapi');
const Accept = require('@hapi/accept');

const init = async () => {
  const server = new Hapi.Server({
    port: 3000,
    host: '0.0.0.0',
    debug: { request: ['error'] }
  });

  server.route({
    method: 'GET',
    path: '/',
    handler(request, h) {
      const language = Accept.language("en;q=0.7, en-GB;q=0.8");
      return language
    }
  });

  await server.start();
  console.log('Server running at:', server.info.uri);
};
process.on('unhandledRejection', (err) => {
  console.log(err);
  process.exit(1);
});
init();

We pass in a string. Then it returns the string according to the value of the Accept-Language header.

Also, we can write:

const Path = require('path');
const Hapi = require('@hapi/hapi');
const Accept = require('@hapi/accept');

const init = async () => {
  const server = new Hapi.Server({
    port: 3000,
    host: '0.0.0.0',
    debug: { request: ['error'] }
  });

  server.route({
    method: 'GET',
    path: '/',
    handler(request, h) {
      const language = Accept.language("en;q=0.7, en-GB;q=0.8", ["en-gb"]);
      return language
    }
  });

  await server.start();
  console.log('Server running at:', server.info.uri);
};
process.on('unhandledRejection', (err) => {
  console.log(err);
  process.exit(1);
});
init();

to do the comparison with the value in the array.

Since it has 'en-gb' as its only entry, this is returned.

Conclusion

We can parse and check the Accept-Encoding and Accept-Language header with the @hapi/accept module.