in

Express + API: Dynamic API Requests

In this post we will combine API requests with Express. We will create a user interface for searching an API and display the results in an EJS template dynamically.

To start we will setup a bare bones Express application with these packages:

The API that I have chosen for this exercise is the GitHub Jobs API which lets you pull in the current directory of available programming jobs listed on GitHub.

Display Static Request

We will start by making a hard coded request for jobs related to “node”

//-----------------------------------
// SETUP
//-----------------------------------
console.log("app.js server is running");


// import express module
var express = require("express");
// set express to variable
var app = express();

// import request module
var request = require('request');

// import body-parser module
var bodyParser = require("body-parser");
// tell express to use body-parser. more detail on this in the body-parser docs
app.use(bodyParser.urlencoded({extended:true}));

// set listen port
app.listen(process.env.PORT, process.env.IP, function(){
    console.log("Listening on PORT: " + process.env.PORT + " at IP: " + process.env.IP);
});

// set Styles location
app.use(express.static("public"));

// set view engine to .ejs
app.set("view engine", "ejs");

//-----------------------------------
// API REQUESTS
//-----------------------------------

app.get("/results", (req, res) => {
    request('https://jobs.github.com/positions.json?search=node', (error, response, body) => {
        
        // error
         console.log('error:', error); 
        // response 
         console.log('statusCode:', response && response.statusCode); 
        // body
         var parsedBody = JSON.parse(body);
         res.send(parsedBody[0]);
    });
});

//-----------------------------------
// ROUTES
//-----------------------------------

This sample also shows that we have parsed the response body into a JSON object, which is something we covered here: Intro to API’s: Parse String to JSON Object

I send back just parsedBody[0] instead of the whole body, because the list is really long. So this will send just the first job data to the /results page

{
"id": "5c1e7811-25c1-4674-a7fb-ec277fcad643",
"type": "Full Time",
"url": "https://jobs.github.com/positions/5c1e7811-25c1-4674-a7fb-ec277fcad643",
"created_at": "Wed Mar 06 15:56:53 UTC 2019",
"company": "Bene Studio Ltd.",
"company_url": "http://www.benestudio.co",
"location": "Budapest",
"title": "Full-stack React & Node Engineer - Full Relocation to Budapest",
"description": "<p>Join bene : studio</p>\n<p>TO BUILD PRODUCTS</p>\n<ul>\n<li>which are diverse, from healthcare to real estate</li>\n<li>making better the lives of the people globally</li>\n<li>with the latest JavaScript technologies</li>\n<li>using your creativity, empowerment and team player attitude</li>\n</ul>\n<p>TO GAIN EXPERIENCE</p>\n<ul>\n<li>from the varied, 1-6 months long projects</li>\n<li>within the English-speaking, international, growing team of highly skilled engineers,</li>\n<li>who passed the famously challenging bene : studio assessment</li>\n<li>during the journey of your Professional Development Plan</li>\n<li>with the help of your dedicated Mentor</li>\n</ul>\n<p>TO REFRESH YOURSELF</p>\n<ul>\n<li>by enjoying the flexible schedule</li>\n<li>relying on the stable, calculable job and salary</li>\n<li>in the team which won the Loveable Workplace Prize</li>\n<li>at the bike, dog and family friendly Office of the Year 2016, where you can have a massage, play ping- pong or drink a coffee/fröccs at the in-house café</li>\n</ul>\n<p>TO SHARE YOUR KNOWLEDGE</p>\n<ul>\n<li>at our blog or one of the leading meetups, conferences</li>\n<li>at bene : studio Workshops!, the most popular cutting-edge tech workshops in Hungary</li>\n</ul>\n<p>TO RELOCATE</p>\n<ul>\n<li>into the heart of Europe, to the beautiful, safe and cheap Budapest</li>\n<li>with the adaptation guidance, flight ticket, temporary housing and visa services provided by us</li>\n</ul>\n<p>TO RECEIVE FROM US</p>\n<ul>\n<li>a MacBook Pro</li>\n<li>competitive salary</li>\n<li>with paid leaves and sick-leaves</li>\n<li>promotion reflecting the improvement of your skills, experiences and your contribution to the company</li>\n</ul>\n<p>IF YOU MET THE REQUIREMENTS OF</p>\n<ul>\n<li>2.5+ years experience in software engineering</li>\n<li>JavaScript ES6</li>\n<li>HTML5, CSS3</li>\n<li>React.js</li>\n<li>Redux</li>\n<li>Node.js</li>\n<li>SQL & noSQL</li>\n<li>Algorithms, data structures</li>\n<li>AWS &/or Azure</li>\n<li>Systems design skills</li>\n</ul>\n<p>OR EVEN GET ADVANTAGES HAVING</p>\n<ul>\n<li>Mobile development experience</li>\n<li>Knowledge of infrastructure as code software tools</li>\n</ul>\n",
"how_to_apply": "<p><a href=\"https://benestudio.co/join-us/full-stack-react-and-node-engineer\">https://benestudio.co/join-us/full-stack-react-and-node-engineer</a></p>\n",
"company_logo": "https://jobs.github.com/rails/active_storage/blobs/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBdkZ0IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--7cd53f169455894631c06e5a849b58b76ae79559/uxuibrand_creative.jpg"
}

and it just prints the whole JSON object to the browser

Success.

Next we need to create an EJS template for our results, render that template in our route instead the API request, and put the API request in the template. Shifting things around.

Putting the Request in a Template

First we change the response of /views to render our results.ejs template that we made like so

app.get("/results", (req, res) => {
    request('https://jobs.github.com/positions.json?search=node', (error, response, body) => {
        
        // error
         console.log('error:', error); 
        // response 
         console.log('statusCode:', response && response.statusCode); 
        // body
         var parsedBody = JSON.parse(body);
         res.render("results", {jobs: parsedBody});
    });
});

and in our template we simply have

<h1>Job Search Results:</h1>

<%= jobs %>

If you need a refresher on how we pass variables from our application to our templates go back to: Adding Variables to EJS Templates

and we can see that our API request is now populating our results template. The only trick is that we can see that we have an object, but we aren’t displaying the actual data. To do that we need to define specific strings and display them <%= %>

Display Object Data

Now that we are working in an HTML document we can’t just display the whole JSON object. We need to identify specific strings by key and then display them inside of HTML.

<h1>Job Search Results:</h1>

<% jobs.forEach(function(job) { %>
<ul>
    <li><%= job.title %></li>
</ul>    
<% }); %>

Here we just loop over each job in the array and display the title

It’s so beautiful…

Creating the Search Form

Now we create a form so that users can search for their own jobs

<h1>Search Page</h1>

<form action="/results" method="GET">
   <label>Job Description: <input type="text" placeholder="python" name="jobDescription"></label>
   <input type="submit">
</form>

and we are going to have to pass this information to our route in our app, which is why we have added the “name” property. We covered this in the post HTML 5 Basics: Name Property

Passing User Input to Route

Going back to our application, we now want to capture the user input in our results route

// results is where the search results are rendered
app.get("/results", (req, res) => {
    var jobDescription = req.query.jobDescription;
    var url = "https://jobs.github.com/positions.json?search=" + jobDescription;
    
    request(url, (error, response, body) => {
        
        // error
         console.log('error:', error); 
        // response 
         console.log('statusCode:', response && response.statusCode); 
        // body
         var parsedBody = JSON.parse(body);
         
         // render results.ejs
         res.render("results", {jobs: parsedBody});
    });
});

We have created a variable called jobDescription that captures the data from the input name jobDescription.

We have also created a new variable called url where we construct the string of our API request, so that we can more cleanly pass that into our request.

Now we can go back and test our input.

and we get our search results

Additional Inputs and Info

This is great but lets add another input field so that we can also input a location, and display more of the information that we are getting back from the API.

I’m making a new GIT branch for this titled “two_inputs”.

We will now capture the location as well

<form action="/results" method="GET">
   <label>Job Description: <input type="text" placeholder="python" name="jobDescription"></label>
   <label>Location: <input type="text" placeholder="San Francisco" name="jobLocation"></label>
   <input type="submit">
</form>

sent the location data to the app, set it to a variable, then construct a new API request URL, and finally send the new variables to the results.ejs template

//-----------------------------------
// API REQUESTS/ROUTES
//-----------------------------------

// homepage where the search form lives
app.get("/", (req, res) => {
    
    res.render("search");
});

// results is where the search results are rendered
app.get("/results", (req, res) => {
    
    // capturing form inputs to variables
    var jobDescription = req.query.jobDescription;
    var jobLocation = req.query.jobLocation;
    
    
  // sample format of API URL
  // https://jobs.github.com/positions.json?description=python&location=new+york
    
    
    // constructing URL for API request
    var url = "https://jobs.github.com/positions.json?description=" + jobDescription + "&location=" + jobLocation;
    
    request(url, (error, response, body) => {
        
        // error
         console.log('error:', error); 
        // response 
         console.log('statusCode:', response && response.statusCode); 
        // body
         var parsedBody = JSON.parse(body);
         
         // render results.ejs and pass in variables
         res.render("results", {jobs: parsedBody,
                                description: jobDescription,
                                location: jobLocation
                               });
    });
});

and then we can display more information in our results template

<h1>Job Search Results:</h1>
<p> You Search For: <strong><%= description %></strong> In: <strong><%= location %></strong><p>

<% jobs.forEach(function(job) { %>

<p><h2>Description: <%= job.title %></h2></p>
<p><h3>Location: <%=job.location %></h3></p>
<p>Company: <%=job.company %></p>
<p>URL: <a href="<%=job.url %>"><%=job.url %></a></p>
   
<% }); %>

and we get a much more comprehensive list of job results that now returns based on job description and location.

Combining Search and Results Into One Page

This is all great, but lets combine search and results onto one page with partials and make it a little more presentable. Let’s make a new branch called partials.

GitHub Repo

Javascript – New Syntax

NoSQL logo

Introduction to Databases