Command Line Job Search Application using Node.js
Tags: node, webdev, programming, javascript
Published: 2021-04-30 19:46:24

### Introduction

In this post, we'll build a command line job search application using **[Node](https://nodejs.org/en/)**. It's a JavaScript runtime that allows the JavaScript code to run outside the browser.

We'll use the [GitHub Jobs](https://jobs.github.com/api) 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:

```command
mkdir job-search-app
```

```command
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

```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

```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](https://www.npmjs.com/package/yargs) - it is used to parse the command line arguments
- [cli-table](https://www.npmjs.com/package/cli-table) - it is used to display the data in a tabular format
- [axios](https://www.npmjs.com/package/axios) - it is used to make HTTP requests

Use the following command to install all the packages

```command
npm install yargs cli-table axios
```

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

```js
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

```js
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

```command
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

```js
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

```js
....
....


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:

```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

```command
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:

```js
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

```command
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:

```js
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](https://twitter.com/_raunaq_). Please do share your valuable feedback and suggestions👋

You can also check my other posts:
- [Understanding the React useEffect() Hook](https://dev.to/raunaqchawhan/understanding-the-react-useeffect-hook-4jjo)
- [Understanding the React useState() Hook](https://dev.to/raunaqchawhan/understanding-the-react-usestate-hook-4d2d)