Command Line Job Search Application using Node.js

Command Line Job Search Application using Node.js

Introduction

In this post, we'll build a command line job search application using Node. It's a JavaScript runtime that allows the JavaScript code to run outside the browser.

We'll use the GitHub Jobs API to fetch data and present it in a tabular format on the command line interface.

We'll focus on the following three scenarios for this application:

  • Job search command without any filter criteria that returns all the data
  • Job search command with location filter returning data specific to that location
  • Job search command with location and technology filter returning data specific to that location as well as technology

Step 1 - Setting up the project

First, create your project folder and navigate into it using the following commands:

mkdir job-search-app
cd job-search-app

Now, we'll be creating a package.json file which holds the information about the project and the packages/dependencies we install. This is done using the following command

npm init

npm will prompt few questions about your project before creating package.json file. In case you want to escape the questions and have all the details filled with default values, you can use the following command

npm init -y

This will save you from the questions and generate a package.json file directly.

Now, create a file called index.js in the job-search-app folder. We'll write the entire code of the application in this file.

The project structure should look like the following:

job-search-app
    ├──index.js
    └──package.json

Step 2 - Installing the packages

  • yargs - it is used to parse the command line arguments
  • cli-table - it is used to display the data in a tabular format
  • axios - it is used to make HTTP requests

Use the following command to install all the packages

npm install yargs cli-table axios

Now, we'll import all the packages in the index.js file using the following lines of code.

const yargs = require('yargs');
const Table = require("cli-table");
const axios = require("axios");

Now, we'll define the header and the structure of the table that will contain the data

const dataTable = new Table({
    head: ["Company Name", "Job Role", "Location", "Full Time", "Job Description"],
    chars: { 'top': '' , 'top-mid': '' , 'top-left': '' , 'top-right': ''
         , 'bottom': '' , 'bottom-mid': '' , 'bottom-left': '' , 'bottom-right': ''
         , 'left': '' , 'left-mid': '' , 'mid': '' , 'mid-mid': ''
         , 'right': '' , 'right-mid': '' , 'middle': '|' },
    colWidths: [25, 30, 20, 11, 72],
    style: {
        head: ['bgBlue', 'white', 'bold'],
        border: ['yellow']
    }
});

We create a dataTable instance using the Table constructor function provided by the cli-table package. An object is passed as an argument that sets the header and different properties of the table as defined below:

  • head - it is an array of column header names for the table
  • chars - it is an object defining how the table is drawn. Each property in the chars object represents the separator at the respective position
  • colWidths - it is an array that defines the column width
  • style - it is an object that defines the style of the table. head property is an array that defines the background color, text color and font weight of the header. border property is an array that defines the color of the separator

Step 3 - Creating the first command (Job search without any filter parameters)

In this step, you'll be creating your first job search command without any filter parameters.

Before writing code for the first command, let's look at the command that we'll type to run the application

node index.js searchAll

node index.js is used to run the application and searchAll is the command line argument passed along with it. searchAll informs the application that it needs to do a job search and return the data on the commannd line interface. yargs is used to parse this command line argument and perform the necessary operation.

Now, let's start with the code

yargs.command({
    command: 'searchAll',
    describe: 'Default search',
    handler() {
        axios.get("https://jobs.github.com/positions.json?markdown=true").
        then(response => {
            response.data.forEach(jobData => {
                dataTable.push(
                    [jobData.company, jobData.title, jobData.location, jobData.type, jobData.url]
                    );
                });

                console.log(dataTable.toString());
            });
    }
});

yargs.command() method from the yargs package is used to create the command. An object is passed as an argument to it which defines all the options related to a particular command. The command property is used to provide a name to the command. Here, searchAll is the command name that we are going to use. You are free to provide any command name. The describe property is used to provide description about the command. Here, "Default search" is the description that we are giving to the command. You can provide any description as desired.

The handler method holds the implementation of what the command will do. We'll call the GitHub Jobs API using axios.get() method. axios.get() returns a promise and therefore, then is chained to it which receives the response from the API.

response is an object that is returned and response.data is an array that holds the job data. You can now loop through the array and push the required details in the dataTable instance as it's an array too. Once the data is pushed, the last line of code console.log(dataTable.toString()) displays the data on the command line interface in a tabular format.

At the end of the entire code, include the following line

....
....


yargs.parse();

This code is required at the end for yargs to parse our command line arguments.

Now, we can run our application using the following command:

node index.js searchAll

Step 4 - Creating the second command (Job search based on location)

In this step, you'll be creating your second command which will return job data based on the location option that you pass along with the command.

Let's look at the command first that we'll type to run the application

node index.js searchByLocation --location="india"  // location can be a city name, zip code, or any other location search term.

Here, searchByLocation is the command which is passed along with --location option to return the job data of a particular location.

Now, add the following code block before yargs.parse() and after the first command code:

yargs.command({
    command: "searchByLocation",
    describe: "Job search by location",
    builder: {
        location: {
            describe: "Location",
            demandOption: true,
            type: 'string'
        }
    },
    handler(argv) {
        axios.get(`https://jobs.github.com/positions.json?markdown=true&location=${argv.location}`).
        then(response => {
            response.data.forEach(jobData => {
                dataTable.push(
                    [jobData.company, jobData.title, jobData.location, jobData.type, jobData.url]
                    );
                });

                console.log(dataTable.toString());
            });
    }
});

This code block is similar to the previous one expect that it has an additional builder property. The value of this property is an object which hold the options that were passed with the command; like the location in this case.

location property is an object where describe is used to provide a description about the option. demandOption is a boolean which when true makes this option as required. type property is used to provide the datatype of the option.

The handler method provides the implementation of the command. Here, handler takes an argument argv which is an object that holds the value of options passed in the command. It can be accessed using argv.OPTION_NAME. In this case, it is argv.location. This value is used in the API url which then returns data for a particular location. The data is then pushed into the dataTable instance and displayed on the command line interface.

Step 5 - Creating the third command (Job search based on location as well as technology)

Based on the previous command, we'll creating our third and final command that will return job data based on location as well as technology.

Let's look at the command first that we'll type to do a job search based on location and technology

node index.js search --location="india" --technology="react"

Here, search is the command and --location and --technology are the options passed with it.

Now, add the following code block before yargs.parse() and after the second command code:

yargs.command({
    command: "search",
    describe: "Job search by location and technology",
    builder: {
        location: {
            describe: "Location",
            demandOption: true,
            type: 'string'
        },
        technology: {
            describe: "Technology",
            demandOption: true,
            type: "string"
        }
    },
    handler(argv) {
        axios.get(` https://jobs.github.com/positions.json?markdown=true&location=${argv.location}&search=${argv.technology}`).
        then(response => {
            response.data.forEach(jobData => {
                dataTable.push(
                    [jobData.company, jobData.title, jobData.location, jobData.type, jobData.url]
                    );
                });

                console.log(dataTable.toString());
            });
    }
});

This is similar to the previous command. Only the technology filter is an additional parameter here which is defined in the builder property too. The handler holds the implementation here just like the previous commands .

Conclusion

In this post, we learned to create a command line job search application. We learned how to create a command using yargs.command() method and how yargs.parse() is an important line of code to be included at the end of the entire code. We also used the axios package to trigger our API request and cli-table to present our data in a tabular format.✌


Thanks for taking the time to read this post. I hope this post helped you!!😊😃 If you liked it, please share.

It would be great to connect with you on Twitter. Please do share your valuable feedback and suggestions👋

You can also check my other posts: