Categories
Express

Error Handling with Express

Like with any other apps, we have to make Express apps ready to handle errors like unexpected inputs or file errors.

In this article, we’ll look at how to handle errors with Express.

Catching Errors

Error handling is the process of processing any errors that comes up both synchronously and asynchronously. Express comes with a default error handler so that we don’t have to write our own.

For example, if we throw errors in our route handlers as follows:

app.get('/', (req, res, next) => {
  throw new Error('error');
});

Express will catch it and proceed. We should see error instead of the app crashing.

For asynchronous errors, we have to call next to pass the error to Express as follows:

app.get('/', (req, res, next) => {
  setTimeout(() => {
    try {
      throw new Error('error');
    }
    catch (ex) {
      next(ex);
    }
  })
});

The code above will throw an error in the setTimeout callback and the catch block has the next call with the error passed in to call the built-in error handler.

We should see error instead of the app crashing.

Likewise, we have to catch rejected promises. We can do it as follows:

app.get('/', (req, res, next) => {
  Promise
    .reject('error')
    .catch(next)
});

Or with the async and await syntax, we can write the following:

app.get('/', async (req, res, next) => {
  try {
    await Promise.reject('error');
  }
  catch (ex) {
    next(ex);
  }
});

We should see error displayed instead of the app crashing with the stack trace.

The same logic also applies to routes with a chain of event handlers. We can call next as follows:

app.get('/', [
  (req, res, next) => {
    setTimeout(() => {
      try {
        throw new Error('error');
      }
      catch (ex) {
        next(ex);
      }
    })
  },
  (req, res) => {
    res.send('foo');
  }
]);

We should see the error displayed with the stack trace.

Default Error Handler

The default error handler catches the error when we call next and don’t handle it with a custom error handler.

The stack trace isn’t displayed in production environments.

If we want to send a different response than the default, we have to write our own error handler.

The only difference between route handlers, middleware and error handlers is that error handler has the err parameter before the request parameter that contains error data.

We can write a simple route with a custom event handler as follows:

app.get('/', (req, res, next) => {
  setTimeout(() => {
    try {
      throw new Error('error');
    }
    catch (ex) {
      next(ex);
    }
  })
});

app.use((err, req, res, next) => {
  res.status(500).send('Error!')
})

Note that we have the error handler below the route. The order is important. It has to below all the routes that we want to handle with it so that the error handler will get called.

We can write more than one custom error handler as follows:

app.get('/', (req, res, next) => {
  setTimeout(() => {
    try {
      throw new Error('error');
    }
    catch (ex) {
      next(ex);
    }
  })
});

app.use((err, req, res, next) => {
  if (req.foo) {
    res.status(500).send('Fail!');
  }
  else {
    next(err);
  }
})

app.use((err, req, res, next) => {
  res.status(500).send('Error!')
})

What we have above is that if req.xhr is truthy in the first error handler, then it’ll send the Fail! response and not proceed to the second one. Otherwise, the second one will be called by calling next .

So if we add req.foo = true before the setTimeout in our route handler to have:

app.get('/', (req, res, next) => {
  req.foo = true;
  setTimeout(() => {
    try {
      throw new Error('error');
    }
    catch (ex) {
      next(ex);
    }
  })
});

Then we get Fail! . Otherwise, we get Error! .

Calling next will skip to the error handler even if there’re other route handlers in the chain.

Conclusion

To handle errors, we should call next to delegate the error handling to the default event handler if no custom event handler is defined.

We can also define our own error handler function by creating a function that has the err parameter before, req , res , and next . The err parameter has the error object passed from next .

Error handlers have to be placed after all the regular route handling code so that they’ll get run.

Also, we can have multiple error handlers. If we call next on it, then it’ll proceed to the next error handler.

Categories
Hapi

Server-Side Development with Hapi.js — Sessions and Advanced HTTP Requests

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.

Advanced HTTP Requests

We can make complex HTTP requests with the @hapi/wreck module.

For example, we can write:

const Wreck = require('@hapi/wreck');
const Https = require('https')
const Http = require('http')

const method = 'GET';
const uri = '/';
const readableStream = Wreck.toReadableStream('foo=bar');

const wreck = Wreck.defaults({
  headers: { 'x-foo-bar': 123 },
  agents: {
    https: new Https.Agent({ maxSockets: 100 }),
    http: new Http.Agent({ maxSockets: 1000 }),
    httpsAllowUnauthorized: new Https.Agent({ maxSockets: 100, rejectUnauthorized: false })
  }
});

const wreckWithTimeout = wreck.defaults({
  timeout: 5
});

const options = {
  baseUrl: 'https://www.example.com',
  payload: readableStream || 'foo=bar' || Buffer.from('foo=bar'),
  headers: {},
  redirects: 3,
  beforeRedirect: (redirectMethod, statusCode, location, resHeaders, redirectOptions, next) => next(),
  redirected: function (statusCode, location, req) {},
  timeout: 1000,
  maxBytes: 1048576,
  rejectUnauthorized: false,
  agent: null,
};

const example = async () => {
  const promise = wreck.request(method, uri, options);
  try {
    const res = await promise;
    const body = await Wreck.read(res, options);
    console.log(body.toString());
  }
  catch (err) {
    console.log(err)
  }
};

example()

The headers property has the request headers.

timeout has the request timeout in milliseconds.

maxBytes has the max size of a request.

agent has the user agent.

We can add the secureProtocol is the SSL method to use.

And the ciphers property can be added to choose the TLS cipher.

Then we use Wreck.read to read the response.

We can also call promise.req.abort() to abort the request.

Session Handling

We can handle session data with the @hapi/yar module.

For instance, we can write:

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

const server = Hapi.Server({ port: 3000 });
const options = {
  storeBlank: false,
  cookieOptions: {
    password: 'the-password-must-be-at-least-32-characters-long'
  }
};

const init = async () => {
  await server.register({
    plugin: require('@hapi/yar'),
    options
  });

  server.route({
    method: 'GET',
    path: '/',
    handler: (request, h) => {
      request.yar.set('example', { key: 'value' });
      return 'hello';
    }
  });

  server.route({
    method: 'GET',
    path: '/session',
    handler: (request, h) => {
      const example = request.yar.get('example');
      return example;
    }
  });

  await server.start();
  console.log('Server running at:', server.info.uri);
};
init();

to register the @hapi/yar plugin with:

await server.register({
  plugin: require('@hapi/yar'),
  options
});

The options property has the cookieOptions.password property to set the password for encrypting the session.

Then in the / route, we call request.yar.set method to add the key and value to the session.

And then in the /session route, we call request.yar.get method with the key we want to get to get the data.

We should get:

{
  "key": "value"
}

as the response.

Conclusion

We can use the @hapi/wreck module to make advanced HTTP requests.

And the @hapi/yar module lets us manage session data across our app.

Categories
Hapi

Server-Side Development with Hapi.js — Rendering Templates and HTTP Requests

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.

Rendering Templates

We can render Nunjucks templates in our Hapi app with the @hapi/vision plugin.

To do this, we write:

index.js

const Nunjucks = require('nunjucks');
const Hapi = require('@hapi/hapi');
const Vision = require('@hapi/vision');

const server = Hapi.Server({ port: 3000 });

const rootHandler = (request, h) => {
  return h.view('index', {
    title: request.server.version,
    message: 'Hello!'
  });
};

const init = async () => {
  await server.register(Vision);

server.views({
    engines: {
      html: {
        compile: (src, options) => {
          const template = Nunjucks.compile(src, options.environment);
          return (context) => {
            return template.render(context);
          };
        },
        prepare: (options, next) => {
          options.compileOptions.environment = Nunjucks.configure(options.path, { watch : false });
          return next();
        }
      }
    },
    relativeTo: __dirname,
    path: 'templates'
  });

server.route({ method: 'GET', path: '/', handler: rootHandler });

await server.start();
  console.log('Server running at:', server.info.uri);
};

init();

templates/index.html

{{message}}

We require the Nunjucks module.

Then we call Nunjucks.compile to with the src parameter to compile the source to the template.

Then we return a function that calls template.render with the context to render the template.

The context has the 2nd argument of h.view as its value.

The prepare method is called before the compile method and it sets the environment option set environment-specific settings.

index.html has the template content.

@hapi/vision also supports render Twig templates.

To do this, we write:

const Twig = require('twig');
const Hapi = require('@hapi/hapi');
const Vision = require('@hapi/vision');

const server = Hapi.Server({ port: 3000 });

const rootHandler = (request, h) => {
  return h.view('index', {
    title: request.server.version,
    message: 'Hello!'
  });
};

const init = async () => {
  await server.register(Vision);

  server.views({
    engines: {
      twig: {
        compile: (src, options) => {
          const template = Twig.twig({ id: options.filename, data: src });
          return (context) => {
            return template.render(context);
          };
        }
      }
    },
    relativeTo: __dirname,
    path: 'templates'
  });

  server.route({ method: 'GET', path: '/', handler: rootHandler });

  await server.start();
  console.log('Server running at:', server.info.uri);
};

init();

templates/index.twig

{{ message }}

We set the engines.twig.compile property to a function.

In it, we call Twig.twig to set the id and data of the template.

And we return a function that takes the context parameter and call template.render on it.

context has the object that we pass in as the 2nd argument.

index.twig is our Twig template.

Making HTTP Requests

We can make HTTP requests in our Node app with the @hapi/wreck module.

For instance, we can write:

const Wreck = require('@hapi/wreck');

const makeRequest = async () => {
  const { res, payload } = await Wreck.get('http://example.com');
  console.log(payload.toString());
};

try {
  makeRequest();
}
catch (ex) {
  console.error(ex);
}

We require the @hapi/wreck module.

Then we call Wreck.get to make a GET request.

Then we get the response with the payload property.

And we convert that to a string with the toString method.

Conclusion

We can render various kinds of templates with the @hapi/vision module.

Also, we can make HTTP requests with the @hapi/wreck module in our Node app.

Categories
Hapi

Server-Side Development with Hapi.js — Rendering Templates

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.

Rendering Templates

We can render templates with the @hapi/vision module.

For instance, we can write:

index.js

const Ejs = require('ejs');
const Hapi = require('@hapi/hapi');
const Vision = require('@hapi/vision');

const server = Hapi.Server({ port: 3000 });

const rootHandler = (request, h) => {
  return h.view('index', {
    title: request.server.version,
    message: 'Hello Ejs!'
  });
};

const init = async () => {
  await server.register(Vision);

  server.views({
    engines: { ejs: Ejs },
    relativeTo: __dirname,
    path: 'templates'
  });

  server.route({ method: 'GET', path: '/', handler: rootHandler });

  await server.start();
  console.log('Server running at:', server.info.uri);
};

init();

templates/index.ejs

<%= message %>

We have await server.register(Vision); to register the @hapi/vision plugin.

Then we call h.view to render the view.

title and message will be available in the template.

We set the engines.ejs property to the Ejs module to use it as our rendering engine.

We can use the Handlebars view engine by writing:

index.js

const Handlebars = require('handlebars');
const Hapi = require('@hapi/hapi');
const Vision = require('@hapi/vision');

const server = Hapi.Server({ port: 3000 });

const rootHandler = (request, h) => {
  return h.view('index', {
    title: request.server.version,
    message: 'Hello!'
  });
};

const init = async () => {
  await server.register(Vision);

  server.views({
    engines: { html: Handlebars },
    relativeTo: __dirname,
    path: 'templates'
  });

  server.route({ method: 'GET', path: '/', handler: rootHandler });

  await server.start();
  console.log('Server running at:', server.info.uri);
};

init();

templates/index.html

{{message}}

We change the engines property to { html: Handlebars } and require the handlebars module to use the Handlebars views engine.

To use the Pug view engine with Hapi, we write:

index.js

const Pug = require('pug');
const Hapi = require('@hapi/hapi');
const Vision = require('@hapi/vision');

const server = Hapi.Server({ port: 3000 });

const rootHandler = (request, h) => {
  return h.view('index', {
    title: request.server.version,
    message: 'Hello!'
  });
};

const init = async () => {
  await server.register(Vision);

  server.views({
    engines: { pug: Pug },
    relativeTo: __dirname,
    path: 'templates'
  });

  server.route({ method: 'GET', path: '/', handler: rootHandler });

  await server.start();
  console.log('Server running at:', server.info.uri);
};

init();

templates/index.pug

p #{message}

To use the Mustache view engine, we write:

const Mustache = require('mustache');
const Hapi = require('@hapi/hapi');
const Vision = require('@hapi/vision');

const server = Hapi.Server({ port: 3000 });

const rootHandler = (request, h) => {
  return h.view('index', {
    title: request.server.version,
    message: 'Hello!'
  });
};

const init = async () => {
  await server.register(Vision);

  server.views({
    engines: {
      html: {
        compile: (template) => {
          Mustache.parse(template);
          return (context) => {
            return Mustache.render(template, context);
          };
        }
      }
    },
    relativeTo: __dirname,
    path: 'templates'
  });

  server.route({ method: 'GET', path: '/', handler: rootHandler });

  await server.start();
  console.log('Server running at:', server.info.uri);
};

init();

templates/index.html :

{{message}}

We have the engines.html.compile method to compile the template into rendered HTML.

context has the data we pass into the 2nd argument of h.view .

Conclusion

We can render various kinds of templates into HTML with the @hapi/vision plugin.

Categories
Hapi

Server-Side Development with Hapi.js — User-Agent, Payload, Semantic Version, and Sorting

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.

Get User Agent Info

We can get user agent info from the request with the @hapi/scooter module.

To use it, we write:

const Hapi = require('@hapi/hapi');
const Scooter = require('@hapi/scooter');

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

  server.route({
    method: 'GET',
    path: '/',
    config: {
      handler(request, h) {
        return request.plugins.scooter;
      },
    }
  });
  await server.register(Scooter);
  await server.start();
  console.log('Server running at:', server.info.uri);
};

process.on('unhandledRejection', (err) => {
  console.log(err);
  process.exit(1);
});

init();

We have await server.register(Scooter); to register the Scooter plugin.

And we use request.plugins.scooter to return the user agent object.

Semantic Versioning

We can use the @hapi/somever module to work with semantic version numbers.

For example, we can write:

const Hapi = require('@hapi/hapi');
const somever = require('@hapi/somever');

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

  server.route({
    method: 'GET',
    path: '/',
    config: {
      handler(request, h) {
        return somever.compare('1.2.3', '1.2.5');
      },
    }
  });
  await server.start();
  console.log('Server running at:', server.info.uri);
};

process.on('unhandledRejection', (err) => {
  console.log(err);
  process.exit(1);
});

init();

to compare 2 version numbers with the somever.compare method.

Then we get -1 returned since the first version number is earlier than the 2nd one.

Also, we can compare versions with the match method:

const Hapi = require('@hapi/hapi');
const somever = require('@hapi/somever');

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

  server.route({
    method: 'GET',
    path: '/',
    config: {
      handler(request, h) {
        return somever.match('1.2.x', '1.2.5');
      },
    }
  });
  await server.start();
  console.log('Server running at:', server.info.uri);
};

process.on('unhandledRejection', (err) => {
  console.log(err);
  process.exit(1);
});

init();

And we get true since '1.2.x' includes '1.2.5' .

Parsing HTTP Payloads

We can parse HTTP payload with the @hapi/subtext module.

For example, we can write:

const Http = require('http');
const Subtext = require('@hapi/subtext');

Http.createServer(async (request, response) => {
  const { payload, mime } = await Subtext.parse(request, null, { parse: true, output: 'data' });
  response.writeHead(200, { 'Content-Type': 'text/plain' });
  response.end(`Payload contains: ${JSON.stringify(payload)}`);

}).listen(1337, '0.0.0.0');

console.log('Server running/');

We call Subtext.parse with the request object to parse it.

It returns a promise with an object with the payload and mime properties.

payload has the payload sent.

mime has the MIME type.

Sorting Text

We can sort text with the @hapi/topo module.

To use it, we write:

const Hapi = require('@hapi/hapi');
const Topo = require('@hapi/topo');

const morning = new Topo.Sorter();

morning.add('Nap', { after: ['breakfast', 'prep'] });
morning.add([
  'Make toast',
  'Pour juice'
], { before: 'breakfast', group: 'prep' });
morning.add('Eat breakfast', { group: 'breakfast' });

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

We create the morning object with the Topo.Sorter constructor.

Then we call morning.add to add the items we want to sort.

The after property specifies the items that come after the item in the first argument.

group lets us add items into groups.

Then we can get the sorted items with the morning.nodes property.

Conclusion

We can sort items with the @hapi/topo module.

The @hapi/subtext module lets us parse request payloads.

And @hapi/scooter returns user agent info.

@hapi/somever lets us compare semantic version strings.