Categories
Hapi

Server-Side Development with Hapi.js — Query Parameters and Request Payloads

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.

Query Parameters

We can get query parameters in our route handler with the request.query property.

For example, we can write:

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

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

  server.route({
    method: 'GET',
    path: '/',
    handler: function (request, h) {
      return `Hello ${request.query.name}!`;
    }
  });

  await server.start();
  console.log('Server running on %s', server.info.uri);
};

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

init();

In the route handler, we use the request.query.name property to get the name property value.

Then when we make a GET request to /?name=foo , we see:

Hello foo!

returned as the response.

We can parse query strings with the query string parser of our choice.

For example, we can write:

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

const init = async () => {
  const server = Hapi.server({
    port: 3000,
    host: '0.0.0.0',
    query: {
      parser: (query) => Qs.parse(query)
    }
  });

  server.route({
    method: 'GET',
    path: '/',
    handler: function (request, h) {
      return request.query;
    }
  });

  await server.start();
  console.log('Server running on %s', server.info.uri);
};

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

init();

We add the query property to the object we passed into the Hapi.server function.

Then we set the parser method to return the object parsed by the Qs.parse method.

Request Payload

We can get the request body with the request.payload property.

For instance, we can write:

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

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

  server.route({
    method: 'POST',
    path: '/',
    handler: function (request, h) {
      return request.payload;
    }
  });

  await server.start();
  console.log('Server running on %s', server.info.uri);
};

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

init();

We returned the request.payload property to return the request body as the response.

So when we make a POST request to the / route with the body:

{
    "foo": "bar"
}

Then we get the same thing back as the response.

Options

We can add some route options to configure our routes.

For example, we can write:

const Hapi = require('@hapi/hapi');
const Joi = require("@hapi/joi")

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

  server.route({
    method: 'POST',
    path: '/',
    options: {
      auth: false,
      validate: {
        payload: Joi.object({
          username: Joi.string().min(1).max(20),
          password: Joi.string().min(7)
        })
      }
    },
    handler: function (request, h) {
      return request.payload;
    }
  });

  await server.start();
  console.log('Server running on %s', server.info.uri);
};

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

init();

We add the @hapi/joi module with the validate.payload property.

We set that to the Joi.object property with an object that has the fields for the object.

username field is a string with a min length of 1 and a max length of 20 as specified by:

Joi.string().min(1).max(20)

And password is a string with a min length of 7 as specified by:

Joi.string().min(7)

auth is set to false to disable auth on the route.

Conclusion

Hapi routes can accept query parameters and request bodies.

Categories
Hapi

Server-Side Development with Hapi.js — Routing

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.

Routing

We can add routes easily to our Hapi app.

For example, we can write:

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

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

  server.route({
    method: 'GET',
    path: '/',
    handler (request, h) {
      return 'Hello World!';
    }
  });

  await server.start();
  console.log('Server running on %s', server.info.uri);
};

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

init();

to add a simple route with:

server.route({
  method: 'GET',
  path: '/',
  handler(request, h) {
    return 'Hello World!';
  }
});

The method has the request method the route accepts.

path has the route path.

handler has the route handler to handle the request.

request has the request data.

One route can handle multiple request methods.

For example, we can write:

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

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

  server.route({
    method: ['PUT', 'POST'],
    path: '/',
    handler (request, h) {
      return 'Hello World!';
    }
  });

await server.start();
  console.log('Server running on %s', server.info.uri);
};

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

init();

We have a route that handles both 'PUT' and 'POST' requests.

Path

A route can accept URL parameters if we put a parameter placeholder in our route path.

For example, we can write:

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

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

  server.route({
    method: 'GET',
    path: '/hello/{user}',
    handler: function (request, h) {
      return `Hello ${request.params.user}!`;
    }
  });

  await server.start();
  console.log('Server running on %s', server.info.uri);
};

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

init();

We have a route that takes the user request parameter.

Then we can get the route parameter value from the request.params.user property.

Now if we make a GET request to the /hello/bob route, we get Hello bob! .

The parameter value isn’t escaped by default. We can escape the string that’s passed by writing:

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

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

  server.route({
    method: 'GET',
    path: '/hello/{user}',
    handler: function (request, h) {
      return `Hello ${Hoek.escapeHtml(request.params.user)}!`;
    }
  });

  await server.start();
  console.log('Server running on %s', server.info.uri);
};

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

init();

We required the @hapi/hoek package and then call Hapi.escapeHtml on the request.params.user string to escape it.

This will prevent any cross-site scripting attacks since any code strings will be sanitized.

Optional Parameters

We can make a parameter optional with the ? .

For instance, we can write:

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

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

  server.route({
    method: 'GET',
    path: '/hello/{user?}',
    handler: function (request, h) {
      const user = request.params.user || 'anonymous'
      return `Hello ${user}!`;
    }
  });

  await server.start();
  console.log('Server running on %s', server.info.uri);
};

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

init();

We have {user?} in the route string to make the user parameter optional.

Then when we go to /hello , we get Hello anonymous!

And when we go to /hello/bob , we get Hello bob! .

Conclusion

We can add various kinds of routes with Hapi.

Categories
Hapi

Server-Side Development with Hapi.js — Plugins

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.

Create a Plugin

We can create plugins and use them with Hapi.

For example, we can write:

index.js

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

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

  await server.register(require('./myPlugin'));

  server.route({
    path: '/',
    method: 'GET',
    handler(request, h) {
      return h.response('hello')
    },
  });
  await server.start();
  console.log('Server running on %s', server.info.uri);
};

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

init();

myPlugin.js

const myPlugin = {
  name: 'myPlugin',
  version: '1.0.0',
  register: async function (server, options) {
    server.route({
        method: 'GET',
        path: '/test',
        handler: function (request, h) {
          return 'hello, world';
        }
    });
  }
};

module.exports = myPlugin

We have the myPlugin object with the register method to add the routes we want in the plugin.

We specify the name and version of the plugin to set the metadata.

Then in index.js , we have:

await server.register(require('./myPlugin'));

to register the plugin we added.

We can pass in some options by writing:

index.js

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

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

  await server.register({
    plugin: require('./myPlugin'),
    options: {
      name: 'Bob'
    }
  });

  server.route({
    path: '/',
    method: 'GET',
    handler(request, h) {
      return h.response('hello')
    },
  });
  await server.start();
  console.log('Server running on %s', server.info.uri);
};

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

init();

myPlugin.js

const myPlugin = {
  name: 'myPlugin',
  version: '1.0.0',
  register: async function (server, options) {
    server.route({
      method: 'GET',
      path: '/test',
      handler: function (request, h) {
        return options.name;
      }
    });
  }
};

module.exports = myPlugin

We get the option.name to get the name option in myPlugin.js .

Then to register the plugin, we write:

await server.register({
  plugin: require('./myPlugin'),
  options: {
    name: 'Bob'
  }
});

to register it with the plugins property to require the plugin file.

options lets us pass in the options.

We can also set a route prefix for the plugin.

For instance, we can write:

index.js

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

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

  await server.register(require('./myPlugin'),{
    routes: {
      prefix: '/plugins'
    }
  });

  server.route({
    path: '/',
    method: 'GET',
    handler(request, h) {
      return h.response('hello')
    },
  });
  await server.start();
  console.log('Server running on %s', server.info.uri);
};

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

init();

myPlugin.js

const myPlugin = {
  name: 'myPlugin',
  version: '1.0.0',
  register: async function (server, options) {
    server.route({
      method: 'GET',
      path: '/test',
      handler: function (request, h) {
        return 'hello world'
      }
    });
  }
};

module.exports = myPlugin

We create the plugin the same way as in the first plugin example.

Then in index.js , we write:

await server.register(require('./myPlugin'), {
  routes: {
    prefix: '/plugins'
  }
});

to set the route prefix.

Now when we go to the /plugins/test route, we see hello world as the response.

Conclusion

We can create plugins to modularize our Hapi app code.

Categories
Hapi

Server-Side Development with Hapi.js — Cookies and Logs

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.

Getting Cookies

We can get cookies with the reques.state property.

For instance, we can write:

const Hapi = require('@hapi/hapi');
const init = async () => {
  const server = Hapi.server({
    port: 3000,
    host: '0.0.0.0'
  });
  server.route({
    path: '/',
    method: 'GET',
    handler(request, h) {
      const value = request.state.data;
      return h.response(value);
    },
  });
  await server.start();
  console.log('Server running on %s', server.info.uri);
};
process.on('unhandledRejection', (err) => {
  console.log(err);
  process.exit(1);
});
init();

We get the data cookie with the request.state.data property.

When we send the Cookie request header with data=foo as the value, then we see foo in the response.

Clearing a Cookie

We can clear a cookie by calling the unstate method.

For example, we can write:

const Hapi = require('@hapi/hapi');
const init = async () => {
  const server = Hapi.server({
    port: 3000,
    host: '0.0.0.0'
  });
  server.route({
    path: '/',
    method: 'GET',
    handler(request, h) {
      return h.response('hello').unstate('data');
    },
  });
  await server.start();
  console.log('Server running on %s', server.info.uri);
};
process.on('unhandledRejection', (err) => {
  console.log(err);
  process.exit(1);
});
init();

We call unstate with the 'data' key to clear the cookie with the key data .

Logging

We can use the server.log or request.log method to log data.

For example, we can write:

const Hapi = require('@hapi/hapi');
const init = async () => {
  const server = Hapi.server({
    port: 3000,
    host: '0.0.0.0'
  });
  server.route({
    path: '/',
    method: 'GET',
    handler(request, h) {
      request.log('error', 'something wrong');
      return h.response('hello')
    },
  });
  await server.start();
  console.log('Server running on %s', server.info.uri);
};
process.on('unhandledRejection', (err) => {
  console.log(err);
  process.exit(1);
});
init();

to call request.log with the tag to log and the log content respectively.

The first argument can be a string or an array of strings.

The 2nd argument is optional.

If we want to log something outside a request handler, we can use the server.log method.

For example, we can write:

const Hapi = require('@hapi/hapi');
const init = async () => {
  const server = Hapi.server({
    port: 3000,
    host: '0.0.0.0'
  });

  server.log(['test', 'error'], 'Test event');

  server.route({
    path: '/',
    method: 'GET',
    handler(request, h) {
      return h.response('hello')
    },
  });
  await server.start();
  console.log('Server running on %s', server.info.uri);
};
process.on('unhandledRejection', (err) => {
  console.log(err);
  process.exit(1);
});
init();

Retrieving and Displaying Logs

To get the log data and display it, we listen to the log event.

For example, we can write:

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

  server.route({
    path: '/',
    method: 'GET',
    options: {
      log: {
        collect: true
      }
    },
    handler(request, h) {
      request.log('error', 'Event error');
      return h.response('hello')
    },
  });
  await server.start();
  console.log('Server running on %s', server.info.uri);
};

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

We add the debug property to the object in Hapi.server to display the logs.

Then we add options.log.collect property in the route handler object and set that to true .

And then we call request.log to log the event in the request handler.

Then we get the event logged.

Conclusion

We can get and clear cookies and log data with Hapi.

Categories
Careers

More Mistakes Freelancers Should Avoid

Freelancing is something that may be tough for some people. If we avoid some mistakes, we can save ourselves from some grief.

In this article, we’ll look at some advice to follow as freelancers.

Overestimating What You can do Well in a day or Week

It’s better to underbook than overbook yourself so you won’t be too stressed.

It’s hard to do a good job if you’re in a rush.

Leaving Holes in a Contract

Make sure you think of all the points to protect yourself and your client and all the expectations from both sides are met.

Not Calculating Your Rate

You have to calculate your rate in order for your work to be profitable.

Compromising Work-Life Balance

Everyone needs to take a break once in a while.

Working too hard will leave you stressed and unhappy.

It’s hard to do a good job in this situation.

Venturing Too Far From Your Niches

Better stick to what you’re good at so you can do a good job.

Make sure you can actually do a good job before you accept a project.

Trust Your Gut When You Choose What Project to Accept

Your gut feeling is usually accurate when choosing what to pick.

Getting Too Emotional About Business Relationships

Business relationships should stay as such.

Never get too emotional about business relationships and make yourself miserable.

Not Tracking Your Time

Time tracking is needed for accurate billing, project time estimation, and managing productivity.

Not Taking Care of Yourself

Never skip meals, take breaks often, don’t sit on your chair all day, and enjoy the sunshine once in a while.

Selling Yourself Too Hard

Being genuine helps with getting sales than focusing only on sales.

Being too sales-y will be noticed by your prospect and that won’t leave a good impression.

Trying to Sell to High-Quality Clients With the Same Methods That Works for Low-Quality Clients

High-quality and low-quality clients are different.

So they should be targeted differently.

Sales and marketing strategies should be different for both.

Not Giving Samples Much Thought

You should retain rights to your sample work whenever possible.

This way, you can refer to them whenever you want since you’re in control of them.

Not Asking for Help

Asking for help will help you solve problems much faster.

It’s also better to work with a support network that can help you get through the problems you face.

Not Having an Onboarding Checklist

Having a checklist will make sure you won’t forget what you have to do to onboard clients.

Forgetting to Celebrate Small Wins

Celebrating small wins is a great way to keep yourself positive and become more successful.

Conclusion

If we follow the advice above, we can all be successful freelancers.