Building Job pages with Metalsmith with the Lever Postings API

November 10, 2018

One of my clients is using Lever to manage their recruiting process. Lever provides a Postings REST API that allows their customers to add job listings to their websites.

The client site is a static website that is build with Metalsmith.

Metalsmith is a NodeJS based Static Site Generator, a very simple “piece of machinery” that moves files from a source to a destination directory and in between it allows plugins to manipulate the files. Specifically:

  • Metalsmith reads source files from a source directory and converts the files into Javascript Objects
  • These Javascript Objects can now be manipulated with Plugins. These manipulations are applied to the JavaScript objects either by changing the properties or the property values. See my post Introducing Project Orca - Part 2 for a more detailed explanation of how Metalsmith works
  • Metalsmith then converts all JS objects back into files and writes them to a destination directory

Metalsmith is perfectly suited to add pages dynamically during its build cycle. During the build process, a custom Metalsmith plugin requests a JSON object with all jobs data from the Lever API and turns them into a Metalsmith file object. This object is then added to the Metalsmith files object and processed like any other page.

Essentially, we get the Lever API Jobs object, convert it into a Metalsmith file object and then Metalsmith does the rest.

Here is an example of a Lever Job object from their API docs:


{
    "additionalPlain": "The Lever Story\n\nWe participated in Y Combinator in summer 2012, … and hiring a diverse workforce.",
    "additional": "<div><b style="\"font-size:" 18px\"="">The Lever Story</b></div><div><br></div><div>We participated in Y Combinator in summer 2012, … hiring a diverse workforce.</div>",
    "categories": {
        "commitment": "Full Time",
        "department": "Sales",
        "location": "Toronto",
        "team": "Account Executive"
    },
        "createdAt": 1502907172814,
        "descriptionPlain": "Work at Lever\n\nLever builds software for teams…well-designed, real-time apps.",
        "description": "<div><b style="\"font-size:" 18px\"="">Work at Lever</b></div><div><br></div><div>Lever builds software for teams…well-designed, real-time apps.</div>",
        "id": "ff7ef527-b0d3-4c44-836a-8d6b58ac321e",
        "lists": [
            {
                "text": "About the Gig:",
                "content": "<li>You will be responsible …across North America</li><li>You will be a Top Hat … your territory</li><li>You will grow … relationships through the schools</li><li>You will educate professors…our platform in their classrooms</li><li>You will run demonstrations… North America)</li>"
            },
            {
                "text": "About You:",
                "content": "<li>You are highly motivated…communication skills</li><li>You have previous…are incredibly eager to learn!</li><li>You are detail-oriented and a self-starter</li><li>You have completed … experience</li><li>You will also … team as required</li>"
            }
        ],
    "text": "Account Director (Inside/Outside Hybrid Sales)",
    "hostedUrl": "https://jobs.lever.co/leverdemo/ff7ef527-b0d3-4c44-836a-8d6b58ac321e",
    "applyUrl": "https://jobs.lever.co/leverdemo/ff7ef527-b0d3-4c44-836a-8d6b58ac321e/apply"
}

Some of the fields are not really that descriptive but here is my interpretation of what we are looking at:

  • text : the job title
  • id : the job ID
  • categories/commitment : job status - Full/half time
  • categories/department : the job department:
  • categories/location : the job location
  • categories/team : job group - a team in a department
  • lists : this is a list of objects, each having a text and content property. This can be used to describe the various aspects of a job
  • description and descriptionPlain : describing jobs at this company. description is a html string while descriptionPlain is a plain text string
  • additional and additionalPlain : Used to describe the hiring company
  • hostedURL : URL of the Lever Job listing page
  • applyURL : URL of the form to apply for this job

To transform the above Lever job JSON into the corresponding Metalsmith file object I wrote a simple plugin. In this plugin I am using a couple of node modules to fetch the JSON from the Lever server and to format the template string for the target page html in a readable way .

  • request - a simple HTTP client to make calls to the Lever Postings API.
  • common-tags - a set of well-tested, commonly used template literal tag functions for use in ES2015+.


var request = require('request');
var commonTags = require('common-tags');

/**
 * Metalsmith plugin to create static pages from lever api data
 */
function plugin() {
    'use strict';

    return function (files, metalsmith, done) {

        var companyUID = "LEVER_CUSTOMER_ID";
        var url = "https://api.lever.co/v0/postings/";
	    var leverAPI = url + companyUID + "?mode=json";
        var jobObj = {};
        var j;
        var fileContent, fileName, page;
        var jobProse = "";

        // get data from the Lever API
        request.get(leverAPI, function (error, response, data) {
            if (error) {
                return console.dir(error);
            }
            // parse lever json into js object
            jobObj = JSON.parse(data);

            // build the job pages
            jobObj.forEach(function (job) {

                jobProse = "";
                for (j = 0; job.lists.length > j; j++) {
                    jobProse += "<h2>" + job.lists[j].text + "</h2>";
                    jobProse += "<ul>" + job.lists[j].content + "</ul>";
                }

                fileContent = commonTags.html`
                    <div class="content-wrapper">
                        <div class="careers-title-wrapper">
                            <div class="title-wrapper">
                                <h1>${job.text}</h1>
                                <ul class="list-unstyled list-inline">
                                    <li>${job.categories.location}</li>
                                    <li>${job.categories.team}</li>
                                    <li>${job.categories.commitment}</li>
                                </ul>
                            </div>
                            <div class="job-application">
                                <a href="${job.applyUrl}" class="btn btn-default btn-b3">Apply for this Job</a>
                            </div>
                        </div>
                        <div class="scroll-wrapper">
                            <div class="job-verbiage">
                                <div class="job-description">
                                    ${job.descriptionPlain}
                                </div>
                                <div class="job-requirements">
                                    ${jobProse}
                                </div>
                                <div class="additional-info">
                                ${job.additionalPlain}
                                </div>
                            </div>
                        </div>
                    </div>
                `;

                // key for the files array
                fileName = "jobs/" + job.id + ".html";
                page = {
                    layout: "jobs-page.html",
                    title: job.text,
                    description: “You really want this job",
                    body_classes: "careers",
                    contents: new Buffer(fileContent)
                };
                // add page to metalsmith object
                files[fileName] = page;
            });
            done();
        });
    };
}

module.exports = plugin;

The plugin code should be pretty self explainatory. In short:

We request the job listings from this endpoint:

https://api.lever.co/v0/postings/<LEVER_CUSTOMER_ID>?mode=json

Upon receiving the response JSON we convert the Lever JSON into a Javascript object

Then we build the job pages content one by one by inserting the appropriate object properties into the HTML template string

Then we define the job page key for the Metalsmith files object. This key is the path to the page, in our case, using the id from the example job above, it is:

jobs/ff7ef527-b0d3-4c44-836a-8d6b58ac321e.html

Then the page object is defined by the previously populated template string and by various metadata, including the page template that Metalsmith should use.

Lastly, we are adding this page to the Metalsmith files object.

Metalsmith does the rest. At the end of this build process there will be a folder jobs in the Metalsmith destination directory with our example file - among others - ff7ef527-b0d3-4c44-836a-8d6b58ac321e.html in it.