To talk to APIs, we need to install a new package called superagent
npm install superagent
'use strict';
// Application Dependencies
require('dotenv').config();
const express = require('express');
const cors = require('cors');
const superagent = require('superagent');
// Application Setup
const app = express();
const PORT = process.env.PORT;
app.use(cors());
// Route Definitions
app.get('/', rootHandler);
app.get('/location', locationHandler);
app.get('/yelp', restaurantHandler);
app.use('*', notFoundHandler);
app.use(errorHandler);
// Route Handlers
function rootHandler(request, response) {
response.status(200).send('City Explorer back-end');
}
function locationHandler(request, response) {
const city = request.query.city;
const locationData = require('./data/location.json');
const location = new Location(city, locationData);
response.status(200).send(location);
}
function restaurantHandler(request, response) {
const restaurantsData = require('./data/restaurants.json');
const arrayOfRestaurants = restaurantsData.nearby_restaurants;
const restaurantsResults = [];
arrayOfRestaurants.forEach(restaurantObj => {
restaurantsResults.push(new Restaurant(restaurantObj));
});
response.send(restaurantsResults);
}
function notFoundHandler(request, response) {
response.status(404).send('Not found');
}
function errorHandler(error, request, response, next) {
response.status(500).json({ error: true, message: error.message });
}
// Constructors
function Location(city, locationData) {
this.search_query = city;
this.formatted_query = locationData[0].display_name;
this.latitude = parseFloat(locationData[0].lat);
this.longitude = parseFloat(locationData[0].lon);
}
function Restaurant(obj) {
this.name = obj.restaurant.name;
this.url = obj.restaurant.url;
this.rating = obj.restaurant.user_rating.aggregate_rating;
this.price = obj.restaurant.price_range;
this.image_url = obj.restaurant.featured_image;
}
// App listener
app.listen(PORT,() => console.log(`Listening on port ${PORT}`));
Instead of getting fake data we can use the LocationIQ API
Find the request URL from the API documentation.
https://us1.locationiq.com/v1/search.php?key=YOUR_PRIVATE_TOKEN&q=SEARCH_STRING&format=json
Every API is different.
Create a variable that contains the URL string (Line 28)
Use superagent's get method to call that URL (Line 29)
Use superagent's query method to pass all parts of the query string string as an object (Lines 30-34)
This will act similar to $.ajax('./data/whatever.json', ajaxSettings);
'use strict';
// Application Dependencies
require('dotenv').config();
const express = require('express');
const cors = require('cors');
const superagent = require('superagent');
// Application Setup
const app = express();
const PORT = process.env.PORT;
app.use(cors());
// Route Definitions
app.get('/', rootHandler);
app.get('/location', locationHandler);
app.get('/yelp', restaurantHandler);
app.use('*', notFoundHandler);
app.use(errorHandler);
// Route Handlers
function rootHandler(request, response) {
response.status(200).send('City Explorer back-end');
}
function locationHandler(request, response) {
const city = request.query.city;
const url = 'https://us1.locationiq.com/v1/search.php';
superagent.get(url)
.query({
key: YOUR_PRIVATE_TOKEN,
q: city,
format: 'json'
});
const location = new Location(city, locationData);
response.status(200).send(location);
}
function restaurantHandler(request, response) {
const restaurantsData = require('./data/restaurants.json');
const arrayOfRestaurants = restaurantsData.nearby_restaurants;
const restaurantsResults = [];
arrayOfRestaurants.forEach(restaurantObj => {
restaurantsResults.push(new Restaurant(restaurantObj));
});
response.send(restaurantsResults);
}
function notFoundHandler(request, response) {
response.status(404).send('Not found');
}
function errorHandler(error, request, response, next) {
response.status(500).json({ error: true, message: error.message });
}
// Constructors
function Location(city, locationData) {
this.search_query = city;
this.formatted_query = locationData[0].display_name;
this.latitude = parseFloat(locationData[0].lat);
this.longitude = parseFloat(locationData[0].lon);
}
function Restaurant(obj) {
this.name = obj.restaurant.name;
this.url = obj.restaurant.url;
this.rating = obj.restaurant.user_rating.aggregate_rating;
this.price = obj.restaurant.price_range;
this.image_url = obj.restaurant.featured_image;
}
// App listener
app.listen(PORT,() => console.log(`Listening on port ${PORT}`));
After we request the data, we use .then() with a promise to instruct what to do after we receive the data (Lines 35-37).
.then() is the standard way to wait for something to come back before doing something else. This is called a promise.
At this point, if we requested '/location?city=chicago' the server will never respond. What went wrong?
In addition to .then() we can use .catch() to respond to errors. (Lines 38-41)
Our error handler displays that there is an "Invalid key"
'use strict';
// Application Dependencies
require('dotenv').config();
const express = require('express');
const cors = require('cors');
const superagent = require('superagent');
// Application Setup
const app = express();
const PORT = process.env.PORT;
app.use(cors());
// Route Definitions
app.get('/', rootHandler);
app.get('/location', locationHandler);
app.get('/yelp', restaurantHandler);
app.use('*', notFoundHandler);
app.use(errorHandler);
// Route Handlers
function rootHandler(request, response) {
response.status(200).send('City Explorer back-end');
}
function locationHandler(request, response) {
const city = request.query.city;
const url = 'https://us1.locationiq.com/v1/search.php'
superagent.get(url)
.query({
key: 'XXX',
q: city,
format: 'json'
})
.then(locationIQResponse => {
console.log(locationIQResponse);
})
.catch(err => {
console.log(err);
errorHandler(err, request, response);
});
}
function restaurantHandler(request, response) {
const restaurantsData = require('./data/restaurants.json');
const arrayOfRestaurants = restaurantsData.nearby_restaurants;
const restaurantsResults = [];
arrayOfRestaurants.forEach(restaurantObj => {
restaurantsResults.push(new Restaurant(restaurantObj));
});
response.send(restaurantsResults);
}
function notFoundHandler(request, response) {
response.status(404).send('Not found');
}
function errorHandler(error, request, response, next) {
response.status(500).json({ error: true, message: error.message });
}
// Constructors
function Location(city, locationData) {
this.search_query = city;
this.formatted_query = locationData[0].display_name;
this.latitude = parseFloat(locationData[0].lat);
this.longitude = parseFloat(locationData[0].lon);
}
function Restaurant(obj) {
this.name = obj.restaurant.name;
this.url = obj.restaurant.url;
this.rating = obj.restaurant.user_rating.aggregate_rating;
this.price = obj.restaurant.price_range;
this.image_url = obj.restaurant.featured_image;
}
// App listener
app.listen(PORT,() => console.log(`Listening on port ${PORT}`));
DO NOT put API keys directly in the code. Put them in .env and on the individual Heroku pipeline app Settings > Config Vars.
You'll have to stop and restart nodemon for .env changes
Add "process.env.LOCATION_KEY" to the key (Line 31)
Request '/location?city=chicago' again to get the correct response
'use strict';
// Application Dependencies
require('dotenv').config();
const express = require('express');
const cors = require('cors');
const superagent = require('superagent');
// Application Setup
const app = express();
const PORT = process.env.PORT;
app.use(cors());
// Route Definitions
app.get('/', rootHandler);
app.get('/location', locationHandler);
app.get('/yelp', restaurantHandler);
app.use('*', notFoundHandler);
app.use(errorHandler);
// Route Handlers
function rootHandler(request, response) {
response.status(200).send('City Explorer back-end');
}
function locationHandler(request, response) {
const city = request.query.city;
const url = 'https://us1.locationiq.com/v1/search.php'
superagent.get(url)
.query({
key: process.env.LOCATION_KEY,
q: city,
format: 'json'
})
.then(locationIQResponse => {
console.log(locationIQResponse);
})
.catch(err => {
console.log(err);
errorHandler(err, request, response);
});
}
function restaurantHandler(request, response) {
const restaurantsData = require('./data/restaurants.json');
const arrayOfRestaurants = restaurantsData.nearby_restaurants;
const restaurantsResults = [];
arrayOfRestaurants.forEach(restaurantObj => {
restaurantsResults.push(new Restaurant(restaurantObj));
});
response.send(restaurantsResults);
}
function notFoundHandler(request, response) {
response.status(404).send('Not found');
}
function errorHandler(error, request, response, next) {
response.status(500).json({ error: true, message: error.message });
}
// Constructors
function Location(city, locationData) {
this.search_query = city;
this.formatted_query = locationData[0].display_name;
this.latitude = parseFloat(locationData[0].lat);
this.longitude = parseFloat(locationData[0].lon);
}
function Restaurant(obj) {
this.name = obj.restaurant.name;
this.url = obj.restaurant.url;
this.rating = obj.restaurant.user_rating.aggregate_rating;
this.price = obj.restaurant.price_range;
this.image_url = obj.restaurant.featured_image;
}
// App listener
app.listen(PORT,() => console.log(`Listening on port ${PORT}`));
PORT=3000
LOCATION_KEY=XXXXXXXXXXXXXXWe want to construct a new Location object (Line 37) passing the city and location object.
Update the constructor as necessary (Lines 65-70)
Send that to the client (Line 38).
DO NOT place response.send() outside of .then().
'use strict';
// Application Dependencies
require('dotenv').config();
const express = require('express');
const cors = require('cors');
const superagent = require('superagent');
// Application Setup
const app = express();
const PORT = process.env.PORT;
app.use(cors());
// Route Definitions
app.get('/', rootHandler);
app.get('/location', locationHandler);
app.get('/yelp', restaurantHandler);
app.use('*', notFoundHandler);
app.use(errorHandler);
// Route Handlers
function rootHandler(request, response) {
response.status(200).send('City Explorer back-end');
}
function locationHandler(request, response) {
const city = request.query.city;
const url = 'https://us1.locationiq.com/v1/search.php'
superagent.get(url)
.query({
key: process.env.LOCATION_KEY,
q: city,
format: 'json'
})
.then(locationIQResponse => {
const topLocation = locationIQResponse.body[0];
const myLocationResponse = new Location(city, topLocation);
response.status(200).send(myLocationResponse);
})
.catch(err => {
console.log(err);
errorHandler(err, request, response);
});
}
function restaurantHandler(request, response) {
const restaurantsData = require('./data/restaurants.json');
const arrayOfRestaurants = restaurantsData.nearby_restaurants;
const restaurantsResults = [];
arrayOfRestaurants.forEach(restaurantObj => {
restaurantsResults.push(new Restaurant(restaurantObj));
});
response.send(restaurantsResults);
}
function notFoundHandler(request, response) {
response.status(404).send('Not found');
}
function errorHandler(error, request, response, next) {
response.status(500).json({ error: true, message: error.message });
}
// Constructors
function Location(city, location) {
this.search_query = city;
this.formatted_query = location.display_name;
this.latitude = parseFloat(location.lat);
this.longitude = parseFloat(location.lon);
}
function Restaurant(obj) {
this.name = obj.restaurant.name;
this.url = obj.restaurant.url;
this.rating = obj.restaurant.user_rating.aggregate_rating;
this.price = obj.restaurant.price_range;
this.image_url = obj.restaurant.featured_image;
}
// App listener
app.listen(PORT,() => console.log(`Listening on port ${PORT}`));
'use strict';
// Application Dependencies
require('dotenv').config();
const express = require('express');
const cors = require('cors');
const superagent = require('superagent');
// Application Setup
const app = express();
const PORT = process.env.PORT;
app.use(cors());
// Route Definitions
app.get('/', rootHandler);
app.get('/location', locationHandler);
app.get('/yelp', restaurantHandler);
app.use('*', notFoundHandler);
app.use(errorHandler);
// Route Handlers
function rootHandler(request, response) {
response.status(200).send('City Explorer back-end');
}
function locationHandler(request, response) {
const city = request.query.city;
const url = 'https://us1.locationiq.com/v1/search.php'
superagent.get(url)
.query({
key: process.env.GEO_KEY,
q: city,
format: 'json'
})
.then(locationResponse => {
const topLocation = locationIQResponse.body[0];
const myLocationResponse = new Location(city, topLocation);
response.status(200).send(myLocationResponse);
})
.catch(err => {
console.log(err);
errorHandler(err.request.response);
});
}
function restaurantHandler(request, response) {
const queryString = request.query;
console.log(queryString);
const lat = parseFloat(request.query.latitude);
const lon = parseFloat(request.query.longitude);
const currentPage = request.query.page;
const numPerPage = 4;
const start = ((currentPage - 1) * numPerPage + 1);
const restaurantData = require('./data/restaurants.json');
const arrayOfRestaurants = restaurantData.businesses;
const restaurantsResults = [];
arrayOfRestaurants.forEach(restaurantObj => {
restaurantsResults.push(new Restaurant(restaurantObj));
});
response.send(restaurantsResults);
}
function notFoundHandler(request, response) {
response.status(404).send('Not found');
}
function errorHandler(error, request, response, next) {
response.status(500).json({ error: true, message: error.message });
}
// Constructors
function Location(city, location) {
this.search_query = city;
this.formatted_query = location.display_name;
this.latitude = parseFloat(location.lat);
this.longitude = parseFloat(location.lon);
}
function Restaurant(obj) {
this.name = obj.restaurant.name;
this.url = obj.restaurant.url;
this.rating = obj.restaurant.user_rating.aggregate_rating;
this.price = obj.restaurant.price_range;
this.image_url = obj.restaurant.featured_image;
}
// App listener
app.listen(PORT,() => console.log(`Listening on port ${PORT}`));
Test your server: http://localhost:3001/location?city=cedar+rapids,+iowa
Go to the LocationIQ dashboard and see requests made.
Continue to process the remaining requests (yelp, weather).
Find the request URL from the Yelp API documentation.
Create a variable that contains the URL string (Line 52)
Use superagent's get method to call that URL (Line 53)
Use superagent's query method to pass all parts of the query string string as an object (Lines 54-59)
Note that Yelp's authentication process is different from LocationIQs (Line 60).
Add your YELP_KEY to .env and the Heroku Environment variables.
'use strict';
// Application Dependencies
require('dotenv').config();
const express = require('express');
const cors = require('cors');
const superagent = require('superagent');
// Application Setup
const app = express();
const PORT = process.env.PORT;
app.use(cors());
// Route Definitions
app.get('/', rootHandler);
app.get('/location', locationHandler);
app.get('/yelp', restaurantHandler);
app.use('*', notFoundHandler);
app.use(errorHandler);
// Route Handlers
function rootHandler(request, response) {
response.status(200).send('City Explorer back-end');
}
function locationHandler(request, response) {
const city = request.query.city;
const url = 'https://us1.locationiq.com/v1/search.php'
superagent.get(url)
.query({
key: process.env.GEO_KEY,
q: city,
format: 'json'
})
.then(locationResponse => {
const topLocation = locationIQResponse.body[0];
const myLocationResponse = new Location(city, topLocation);
response.status(200).send(myLocationResponse);
})
.catch(err => {
console.log(err);
errorHandler(err.request.response);
});
}
function restaurantHandler(request, response) {
const lat = parseFloat(request.query.latitude);
const lon = parseFloat(request.query.longitude);
const currentPage = request.query.page;
const numPerPage = 4;
const start = ((currentPage - 1) * numPerPage + 1);
const url = 'https://api.yelp.com/v3/businesses/search';
superagent.get(url)
.query({
latitude: lat,
longitude: lon,
limit: numPerPage,
offset: start
})
.set('Authorization', `Bearer ${process.env.YELP_KEY}`)
const restaurantsData = require('./data/restaurants.json');
const arrayOfRestaurants = restaurantsData.nearby_restaurants;
const restaurantsResults = [];
arrayOfRestaurants.forEach(restaurantObj => {
restaurantsResults.push(new Restaurant(restaurantObj));
});
response.send(restaurantsResults);
}
function notFoundHandler(request, response) {
response.status(404).send('Not found');
}
function errorHandler(error, request, response, next) {
response.status(500).json({ error: true, message: error.message });
}
// Constructors
function Location(city, location) {
this.search_query = city;
this.formatted_query = location.display_name;
this.latitude = parseFloat(location.lat);
this.longitude = parseFloat(location.lon);
}
function Restaurant(obj) {
this.name = obj.restaurant.name;
this.url = obj.restaurant.url;
this.rating = obj.restaurant.user_rating.aggregate_rating;
this.price = obj.restaurant.price_range;
this.image_url = obj.restaurant.featured_image;
}
// App listener
app.listen(PORT,() => console.log(`Listening on port ${PORT}`));
Use .then() with a promise to instruct what to do after we receive the data (Lines 61-68).
Move all remaining code from yesterday's class into .then(). Remove the require() for restaurants.json (Lines 62-67)
Use .catch() to respond to errors. (Lines 69-72)
Intentionally create errors so you can read and understand error messages.
'use strict';
// Application Dependencies
require('dotenv').config();
const express = require('express');
const cors = require('cors');
const superagent = require('superagent');
// Application Setup
const app = express();
const PORT = process.env.PORT;
app.use(cors());
// Route Definitions
app.get('/', rootHandler);
app.get('/location', locationHandler);
app.get('/yelp', restaurantHandler);
app.use('*', notFoundHandler);
app.use(errorHandler);
// Route Handlers
function rootHandler(request, response) {
response.status(200).send('City Explorer back-end');
}
function locationHandler(request, response) {
const city = request.query.city;
const url = 'https://us1.locationiq.com/v1/search.php'
superagent.get(url)
.query({
key: process.env.GEO_KEY,
q: city,
format: 'json'
})
.then(locationResponse => {
const topLocation = locationIQResponse.body[0];
const myLocationResponse = new Location(city, topLocation);
response.status(200).send(myLocationResponse);
})
.catch(err => {
console.log(err);
errorHandler(err.request.response);
});
}
function restaurantHandler(request, response) {
const lat = parseFloat(request.query.latitude);
const lon = parseFloat(request.query.longitude);
const currentPage = request.query.page;
const numPerPage = 4;
const start = ((currentPage - 1) * numPerPage + 1);
const url = 'https://api.yelp.com/v3/businesses/search';
superagent.get(url)
.query({
latitude: lat,
longitude: lon,
limit: numPerPage,
offset: start
})
.set('Authorization', `Bearer ${process.env.YELP_KEY}`)
.then(yelpResponse => {
const arrayOfRestaurants = restaurantsData.nearby_restaurants;
const restaurantsResults = [];
arrayOfRestaurants.forEach(restaurantObj => {
restaurantsResults.push(new Restaurant(restaurantObj));
});
response.send(restaurantsResults);
})
.catch(err => {
console.log(err);
errorHandler(err, request, response);
});
}
function notFoundHandler(request, response) {
response.status(404).send('Not found');
}
function errorHandler(error, request, response, next) {
response.status(500).json({ error: true, message: error.message });
}
// Constructors
function Location(city, location) {
this.search_query = city;
this.formatted_query = location.display_name;
this.latitude = parseFloat(location.lat);
this.longitude = parseFloat(location.lon);
}
function Restaurant(obj) {
this.name = obj.restaurant.name;
this.url = obj.restaurant.url;
this.rating = obj.restaurant.user_rating.aggregate_rating;
this.price = obj.restaurant.price_range;
this.image_url = obj.restaurant.featured_image;
}
// App listener
app.listen(PORT,() => console.log(`Listening on port ${PORT}`));
Use console.log for the response received (Line 62). Yelp is using a "body" property with a "businesses" property inside it. (Line 63)
We want to construct new Restaurant objects passing in each raw data object from the array. (Lines 65-67)
Update the constructor as necessary (Lines 92-98)
Send the restaurantsResults array as our response to the client (Line 68).
'use strict';
// Application Dependencies
require('dotenv').config();
const express = require('express');
const cors = require('cors');
const superagent = require('superagent');
// Application Setup
const app = express();
const PORT = process.env.PORT;
app.use(cors());
// Route Definitions
app.get('/', rootHandler);
app.get('/location', locationHandler);
app.get('/yelp', restaurantHandler);
app.use('*', notFoundHandler);
app.use(errorHandler);
// Route Handlers
function rootHandler(request, response) {
response.status(200).send('City Explorer back-end');
}
function locationHandler(request, response) {
const city = request.query.city;
const url = 'https://us1.locationiq.com/v1/search.php'
superagent.get(url)
.query({
key: process.env.GEO_KEY,
q: city,
format: 'json'
})
.then(locationResponse => {
const topLocation = locationIQResponse.body[0];
const myLocationResponse = new Location(city, topLocation);
response.status(200).send(myLocationResponse);
})
.catch(err => {
console.log(err);
errorHandler(err.request.response);
});
}
function restaurantHandler(request, response) {
const lat = parseFloat(request.query.latitude);
const lon = parseFloat(request.query.longitude);
const currentPage = request.query.page;
const numPerPage = 4;
const start = ((currentPage - 1) * numPerPage + 1);
const url = 'https://api.yelp.com/v3/businesses/search';
superagent.get(url)
.query({
latitude: lat,
longitude: lon,
limit: numPerPage,
offset: start
})
.set('Authorization', `Bearer ${process.env.YELP_KEY}`)
.then(yelpResponse => {
console.log(yelpResponse);
const arrayOfRestaurants = yelpResponse.body.businesses;
const restaurantsResults = [];
arrayOfRestaurants.forEach(restaurantObj => {
restaurantsResults.push(new Restaurant(restaurantObj));
});
response.send(restaurantsResults);
})
.catch(err => {
console.log(err);
errorHandler(err, request, response);
});
}
function notFoundHandler(request, response) {
response.status(404).send('Not found');
}
function errorHandler(error, request, response, next) {
response.status(500).json({ error: true, message: error.message });
}
// Constructors
function Location(city, location) {
this.search_query = city;
this.formatted_query = location.display_name;
this.latitude = parseFloat(location.lat);
this.longitude = parseFloat(location.lon);
}
function Restaurant(obj) {
this.name = obj.name;
this.url = obj.url;
this.rating = obj.rating;
this.price = obj.price;
this.image_url = obj.image_url;
}
// App listener
app.listen(PORT,() => console.log(`Listening on port ${PORT}`));
Test your server
Continue to process the remaining requests (weather).
Look at front-end templates or Trello cards for expected responses
Remove the data folder that contains location.json and restaurants.json
Remove any extra console.log statements when finished.
The console.log(err) statements might be helpful to keep