What is the Airbnb Mashvisor API?
The Mashvisor API is a real-estate analysis tool that provides more than just the basic facts about current or potential properties. Mashvisor can provide data, using statistical inference algorithms, that can stop someone from making a bad real-estate investment when it comes to Airbnb or traditional renting.
The API has two main categories:
- Short-term rentals (Airbnb) or vacation rental listings
- Long-term rentals (“traditional”)
The API can provide detailed information on specific Airbnb properties per month, including;
- Nightly price
- Occupancy
- Revenue
- Unbooked nights
The endpoints are divided into categories like Property, Rental Rates, Trends, Predictive Scores, etc. Someone could look for overall market trends in an area or zero-in on a specific Airbnb property. You may be wondering why we don’t use Airbnb’s API to get specific property listings since that’s what we are looking for.
Why Not Just Use Airbnb’s API?
“At this time, we are not accepting new access requests for our API. Our global team of partner managers will reach out to prospective partners based on the supply opportunity your business represents, strength of your technology, and ability to support our shared customers.” Airbnb website
If we were building out an application for users that would allow them to perform actions (i.e update listings) then we would love to have direct access to the Airbnb API. This access would most likely come through a third-party other than Airbnb.
Before building a project it’d be useful to know exactly what type of data you need for the questions you are trying to answer. The information that we get from the Mashvisor API could:
- Inform potential Airbnb hosts on whether they are making a good or bad decision to list in their area
- Help travelers determine if it’s economical to use Airbnb or other rental providers
Mashvisor is mostly analytical and informational and does not perform actions for users on their account.
There are many options with this API, but first I am going to discuss how to use it. Then we will be free to grab the statistics that we want to help ourselves (or our users) make data-based real-estate decisions.
How to Use the Mashvisor API to Get Airbnb Data
We are going to need an account with RapidAPI and a subscription to use the Mashvisor API (see the API documentation here).
Select the Basic subscription that gives us one-hundred API calls a month. After the first one-hundred, it will cost $0.10 per request. Remember, we can always view and manage our usage on the RapidAPI developer dashboard. You should not approach your quota in the example later, but if you feel like you’re getting close, have the dashboard ready.
With only one-hundred per month, we need to be smart about how we are grabbing data and how much of that data we are grabbing at a time. We will find helpful information on the API page. On RapidAPI, search for “Mashvisor” and select the option after submitting it.
At the top of the middle section, you will notice a short description for each endpoint.
The middle section’s parameters are actually inputs, and if we change those inputs the code snippets in the right section would change as well! The right section also has sample responses so we can evaluate what data we are going to receive from the endpoint.
At the top of the right section, there is a dropdown where we can select different languages and different libraries within that language. The code snippets stand out because of the added coloring that is specific to the language. We are going to use this snippet builder for our NodeJS command-line application.
Wherever you see “Short-term Rental”, the majority of the time, it means Airbnb.
Mashvisor has some predictive scores, that they generate with their own algorithms, that include;
- Mashmeter
- Property Recommender
- Investment Likelihood
Of these, we are only going to use the Mashmeter in the example, described as:
A unique Mashvisor designed score showing an overall evaluation of the neighborhood investment opportunity as a percentage.
You could read more about any of these scores on the Mashvisor API dashboard page.
I hope you are excited to build a NodeJS command-line application that will allow us to create personalized investment reports based on our local neighborhood!
Example Application: Should I Rent Traditionally or List on Airbnb?
Mashvisor provides helpful analytical information, so we are going to build a command-line-interface using NodeJS that will gather statistics to help answer the question, “Based on the neighborhood I live in, should I rent short-term with Airbnb or traditionally?” . The application will only hit a couple endpoints but could be expanded to output multiple types of reports.
It will utilize popular npm libraries like Yargs, Axios, and Chalk. It will use NodeJS tools like destructuring, async/await, try/catch blocks, and string literals.
I hope that it inspires you to build something even bigger and better, so let’s begin!
Prerequisites
- You’ll need to have some familiarity with how to open and use a command-line tool.
- Basic understanding of NodeJS or JavaScript
- You’ll want one of the newer versions of node. I am using 10.16 and a recent version of npm installed. I am using 6.13.0, but it’s good to have at least 5.6.
- An internet connection.
- I will be using the UNIX bash command-line tool on macOS. However, this command line can be installed on Windows 10.
- A RapidAPI account and a basic (free) subscription to the Mashvisor API.
Setting Up the Project
- Open up a new terminal (Command–Space bar, then search for ‘terminal’ on Mac).
- Navigate to a directory that makes sense using the
cd
command. To see what directories are below the current directory entercd
then hit Tab twice. The directory options will display. Now, commands can be run that follow this format –>cd lower_directory/directory_below_that_that
. - Create a new directory with the command
mkdir [name_of_directory]
cd
into that directory. You can check to make sure you are in the right place by runningpwd
. This stands for Print Working Directory.- Initialize a new npm module by running
npm init -y
. If the command was run without the -y then questions would display that would ask you to describe and define aspects of the new npm module. That is not necessary for this tutorial and running the command with the -y flag skips this process. Keep this terminal open because we will be installing npm packages later, and running commands from the root project directory. - Open the project directory in the code editor of your choice. I am using Visual Studio Code so I will execute
code .
which opens VSCode in the directory that I am currently in.
We now have a fresh NodeJS project to work with.
Understanding Command-Line Arguments
This project will build reports based on data returned by the Mashvisor API. We will want the ability to store that data. This means we need the add, view, list, and remove commands. We are going to use the library Yargs to do this in our project, but first, we need a little more understanding of how we are going to pass arguments with our commands.
- Create a file named
cli.js
in the project directory. - Insert the code
console.log(process.argv)
- Enter the following command into the terminal:
node cli.js "test"
. There should be a list displayed with a few items. One of those items should be “test”. The command we ran executes the filecli.js
using NodeJS and stores the argument we give it as in an array that we can access in the file. This is how we are going to pass data into our project.
Yet, accessing this variable could be messy. It would look something like process.argv[2]
. Thankfully, there is an npm library that attaches these arguments to an object and gives us the ability to build commands based on these arguments: Yargs.
4. Head back over to the terminal and install Yargs with the command npm install yargs --save
.
5. In cli.js
we need to import our new module and parse our new object. Add the code below to that file.
const yargs = require('yargs').argv console.log(process.argv) console.log(yargs)
node cli.js --argument="test"
. You should see our first argument logged and a new object that has a key ‘argument’ and value ‘test’Building Our Commands
- Make a new directory at our project root,
cmds
. - Make another directory inside that named
report_cmds
. - Create the file
add.js
insidereports_cmds
.
We could put all of our commands in the same file, but that may get messy. Instead, we are going to build our app with each command having it’s own file.
4. Add the following items to add.js
.
exports.command = 'add' // command name exports.desc = 'Adds a new report' // description of command exports.builder = {} // build and describe arguments exports.handler = function(argv) { console.log("Adds new report") // function to run when command gets called }
- command: the syntax for the command
- desc: the description of what the command does
- builder: Describe the arguments that our command accepts
- handler: function to run when the command is issued.
If we ran a command like node cli.js add
we would expect this to run. This won’t work though because Yargs is unaware of our command directory. We need to make a few changes so it can be found.
5. In cli.js
replace all the code we have in there with the code below;
require('yargs') .commandDir('cmds') // finds command directory .demandCommand() .help() // allows us to use the help command .argv // parses the arguments
cmds/
directory named reports.js
. This file will look a lot like cli.js
.// reports.js exports.command = 'reports <command>' exports.desc = 'Manage reports' exports.builder = function (yargs) { return yargs.commandDir('report_cmds') // locate report commands } exports.handler = function (argv) {}
You will notice that the command in this file looks a little different. The syntax <command>
is a placeholder conveying that the argument is mandatory. If the command were to be optional it would be wrapped in [ ]
.
7. In the terminal, still, at the root of our directory, issue the command node cli.js help
Commands: cli.js reports <command> Manage reports Options: --version Show version number [boolean] --help Show help [boolean]
You should see a description of our command (from reports.js
) as well as other options. Try it again, but this time enter node cli.js reports help
.
You should see our add
command with its description.
Commands: cli.js reports add Adds a new report
8. Back over in add.js
we need to set up arguments that we are going to pass as parameters to our API call. The command is going to look like this:
add <name> <location> <state>
exports.builder
object will be used to describe the argument, mark it as required or not, and detail the type of argument (string, number, etc.).// add.js ... exports.builder = { name: { describe: 'Report name.', demandOption: true, // makes it required type: 'string' // define object }, ... ...
9. Add the code below to add.js
exports.command = 'add <name> <location> <state>' exports.desc = 'Add a new report' exports.builder = { name: { describe: 'Report name.', demandOption: true, type: 'string' }, location: { describe: 'Mashvisor neighborhood ID to pull data for.', demandOption: true, type: 'number' }, state: { describe: 'State to pull data for.', demandOption: true, type: 'string' }, } exports.handler = function(argv) { console.log('Adds the report') }
This is our first real command. It takes three arguments. It does not have a function to run when called but it will soon.
10. Run node cli.js reports help
and view our add command with arguments and descriptors. Furthermore, you can run node cli.js reports add
, which will fail, but it will show the descriptions and types of arguments that the command needs.
11. The files for the list, view, and remove commands are very similar. For the sake of time, create each file in the report_cmds/
directory (list.js
, remove.js
, view.js
) and add the corresponding code to those files.
// list.js exports.command = 'list' exports.desc = 'List all reports' exports.handler = () => { console.log('List all report titles') }
// remove.js exports.command = 'remove <name>' exports.desc = 'Remove report' exports.aliases = ['delete'] exports.builder = { name: { describe: "Name to be removed", demandOption: true, type: "string" } } exports.handler = function(argv) { console.log('Remove report') }
// view.js exports.command = 'view <name>' exports.desc = 'View one report' exports.builder = { name: { describe: "Name of report you want to view.", demandOption: true, type: 'string' } } exports.handler = function(argv) { console.log('View report') }
Creating Basic Function Handlers
Each command has a handler. This is a function that is called when the command is issued. We need to create those functions, but we are going to do it in a separate file.
- In the root directory of the project create a file
reports.js
. - In that file, we are going to have functions for our commands. We need to export those functions from the file.
const listReports = () => { // lists report titles } const addReport = async (name, neighborhoodId) => { // adds a report } const viewReport = (name) => { // prints report data } const removeReport = (name) => { // removes report from reports.json } module.exports = { addReport, removeReport, listReports, viewReport, }
Our report data will be stored as a list in a local file. This is where we will read, save, load, and remove report data from.
3. Create the file reports.json
at the root of the directory and place a list in it that has an example object [{"name": "test", "location": 54678}]
We are going to need two helper functions that load the reports from reports.json
and one that saves the reports back into that file. We will do this using the built-in fs
module from NodeJS. We will need to require it at the top of reports.js
.
4. Add this code to the top of reports.js
.
const fs = require('fs') const loadReports = () => { try { let reportsBuffer = fs.readFileSync('./reports.json'); // find and load file let reportsJson = reportsBuffer.toString(); // convert buffer to string return JSON.parse(reportsJson); // parse string to object } catch(e) { return []; } } const saveReports = (reports) => { const dataJson = JSON.stringify(reports); fs.writeFileSync('reports.json', dataJson); } ...
5. Now we can create the list function. This is a simple function that grabs the reports, then prints the report titles to the console. We are going to spice it up with the Chalk library. Install the chalk library with npm install chalk --save
Chalk adds coloring to the console output. You can read more about chalk on its npm page.
6. Finish the list function in reports.js
and import Chalk at the top of the file
const chalk = require('chalk') ... const listReports = () => { const reports = loadReports(); reports.map((report, i) => { console.warn(chalk.white.bgBlue(` ${report.name} `)); }) } ...
We are almost ready to test our first command, but we haven’t included this function in the list.js
command file.
7. In list.js
replace console.log('List all report titles')
with reports.listReports()
and at the top of the file add;
const reports = require('../../reports');
8. Run node cli.js reports list
. You should see the name of our one, and only, report get printed to the console with a blue background.
reports.json
. It removes it by simply not saving it back to the file with the rest of the reports after it filters it out.reports.js
... const removeReport = (name) => { try { const reports = loadReports(); // load reports in let newReports = reports.filter(report => report.name !== name); // filter to get reports that don't match name if (reports.length > newReports.length) { saveReports(newReports); // save new reports list to file console.log(chalk.green.inverse('Report removed!')) } else { console.log(chalk.red.inverse('Report not found')); } } catch (e) { return e } } ...
10. Add the function to remove.js
in replace of console.log(...)
and import the reports object.
// remove.js const reports = require('../../reports') ... ... exports.handler = function(argv) { reports.removeReport(argv.name) }
Notice that we are passing the name argument to the function via the argv
object. Inside that object there will be the property “name” that holds the value passed from the command line.
It is finally time to build the add command that will retrieve Airbnb—and traditional—real-estate data from the Mashvisor API.
Fetching Data From Mashvisor (Airbnb API Integration)
The add command;
- Grab data from Mashvisor
- Select the data we want to keep
- Save that data to reports.json
We are going to build our HTTP calls in a separate file with Axios. Make sure you are still in the root of project directory.
- Install Axios with the command
npm install axios --save
2. Create the file api.js
at the root of the project. In this file import axios and add the following code so the file looks like this:
const axios = require('axios') const instance = axios.create({ timeout: 1000, headers: { // headers added here will be applied to all 'instances' // below in module.exports } }); module.exports = { util: { // we are going to add our API calls here getNeighborhoodOverview: (city, state) => instance({ }) }, airbnb: { // airbnb api calls here }, traditional: { // traditional api calls here } }
We can create all of our API calls in one file and export them as functions attached to different objects. Notice that we are creating a function on the util
object called ‘getNeighborhoodOverview’ and passing it two of our command-line arguments ‘neighborhoodId’ and ‘state’.
This helps organize our code and allows us to easily create an Axios instance that stores the headers we need for each request.
Unfortunately, I can’t go into details about the Axios library and instances, but you can read about it here.
3. In the browser, go to the Mashvisor API, and on the left panel, under the ‘Search’ tab select ‘Neighborhood Overview’.
4. In the right panel on that page select the code dropdown and find ‘Node.js’. There should be another sidebar that has ‘Axios’ listed. Find it and select ‘Axios’.
HTTP headers are going to be placed into the Axios instance at the top. The method, url, and params are going inside our function call. After extracting the relevant data from the code snippet, api.js
should have this code in the file.
const axios = require('axios') const instance = axios.create({ timeout: 1000, headers: { "content-type":"application/octet-stream", "x-rapidapi-host":"mashvisor-api.p.rapidapi.com", "x-rapidapi-key":"apikey" // good idea to not publish API keys on the web } }); module.exports = { util: { getNeighborhoodOverview: (neighborhoodId, state) => instance({ "method":"GET", "url":"https://mashvisor-api.p.rapidapi.com/neighborhood/268201/bar", "params":{ "state": state } }) }, airbnb: { // airbnb api calls here }, traditional: { // traditional api calls here } }
The problem with the current URL is that it’s not dynamic. We need the variables from the command-line to be passed directly into the string.
5. Replace each quotation on both ends of the URL with a backtick `
. This creates a string literal and allows us to input variables into the string with the syntax ${variableName}.
6. Replace the neighborhoodId in the URL with ${neighborhoodId} and the state parameter value with the state variable so our API call will look like this inside:
... "method":"GET", "url":`https://mashvisor-api.p.rapidapi.com/neighborhood/${neighborhoodId}/bar`,"params":{ "state": state } ...
Now that we have our first API call set up we need to import that function into reports.js
and call it inside our addReport function.
7. Import { util }
into reports.js
and call it asynchronously in the function.
... const { util } = require('./api') ... ... const addReport = async (name, neighborhoodId, state) => { try { // { data: [name] } pulls data object out of response and renames it // Get data for neighborhood overview const { data: overviewData } = await util.getNeighborhoodOverview(neighborhoodId, state) // Help shallow out object depth const neighborhoodOverview = overviewData.content; } catch (e) { console.log(e) return console.log(chalk.red.inverse('Error fetching data report')) } } ...
I snuck a few other things into this code addition. We are destructuring the Axios response by pulling the ‘data’ property off of that response object and renaming it ‘overviewData’.
Then, we are going deeper into the response body of the neighborhoodOverview variable because the content is a little further down. We know this by looking at the example responses in the RapidAPI dashboard.
There are four things that are missing in this function:
- Loading in reports
- Checking for duplicate name
- Selecting data from the response object
- Saving reports
8. Add a call to loadReports()
at the top the addReport()
function and save the result to a variable reports
.
9. Use the Javascript find()
function to iterate over reports and search for a duplicate name.
10. If there is not a duplicate, call the API, if there is, log the message that explains this. After these additions, the function will have the code;
... const addReport = async (name, neighborhoodId, state) => { const reports = loadReports(); const duplicateReport = reports.find(report => report.name === name ); if (!duplicateReport) { try { // { data: [name] } pulls data object out of response and renames it // Get data for neighborhood overview const { data: overviewData } = await util.getNeighborhoodOverview(neighborhoodId, state) // Help shallow out object depth const neighborhoodOverview = overviewData.content; } catch (e) { console.log(e) return console.log(chalk.red.inverse('Error fetching data report')) } } else { console.log(chalk.red.inverse('Report already exists')) } } ...
Saving the report data will be fairly easy. We just need to push it to our reports variable and then call the saveReports()
function. Before that, we need to actually parse out the data we want from the response.
11. Create an util
folder at the root of our directory and add the file cleanData.js
.
I know it can seem like a hassle to create all these files and folders, but if we hope to build bigger and more exciting applications it’s important we pay attention to the organization of our project. We don’t want to make a mess.
This function is going to accept the small response object, assign the values to new properties, and then return the object. I have already glanced over the response object in the RapidAPI dashboard and have selected Airbnb and traditional real-estate data from the object to use.
This is going to seem like a lot of code but most of it is simply variable and object assignment.
12. Add the below code to cleanData.js
.
const cleanData = async (name, neighborhoodId) => { // Grab data from response object and assign to new variables const airbnb_properties = neighborhoodOverview.num_of_airbnb_properties; const traditional_properties = neighborhoodOverview.num_of_traditional_properties; const mash_meter = neighborhoodOverview.mashMeter; const avg_occupancy = neighborhoodOverview.avg_occupancy; const roi_airbnb = neighborhoodOverview.airbnb_rental.roi; const roi_traditional = neighborhoodOverview.traditional_rental.roi; const cap_rate_airbnb = neighborhoodOverview.airbnb_rental.cap_rate; const cap_rate_traditional = neighborhoodOverview.traditional_rental.cap_rate; const rental_income_airbnb = neighborhoodOverview.airbnb_rental.rental_income; const rental_income_traditional = neighborhoodOverview.traditional_rental.rental_income; // Create object that will be returned and added to the reports const reportObj = { name, // name neighborhoodId and beds are going to be included in report neighborhoodId, airbnb_properties, traditional_properties, mash_meter, avg_occupancy, roi_airbnb, roi_traditional, cap_rate_airbnb, cap_rate_traditional, rental_income_airbnb, rental_income_traditional, } return reportObj } module.exports = cleanData;
We now have an object with ten useful data points about Airbnb and traditional rental properties in a given neighborhood.
13. Receive that object back in the addReport()
function and save it to our reports.json
file. We also need to make sure we are importing the cleanData()
function. The file and function should now resemble;
... const cleanData = require('./util/cleanData') ... const addReport = async (name, neighborhoodId) => { const reports = loadReports(); const duplicateReport = reports.find(report => report.name === name ); if (!duplicateReport) { try { // { data: [name] } pulls data object out of response and renames it // Get data for neighborhood overview const { data: overviewData } = await airbnb.getNeighborhoodOverview(neighborhoodId, state) // Help shallow out object depth const neighborhoodOverview = overviewData.content; // NEW: Get data from objects and combine into one object const newReportData = await cleanData( name, neighborhoodId, neighborhoodOverview ) reports.push(newReportData) // NEW: push new report object onto reports array saveReports(reports) // NEW: save reports back to file console.log(chalk.green.inverse('Report added')) // NEW: Log that is worked! } catch (e) { console.log(e) return console.log(chalk.red.inverse('Error fetching data report')) } } else { console.log(chalk.red.inverse('Report already exists')) } } ...
It’s been a while since we used the command-line, but it’s time to test the add command to make sure that our commands are actually reaching this function.
If we tried to pass the arguments for name, location, and state via the command line it would fail. We haven’t added the addReport(argv.name, argv.location, argv.state)
function call to our add command handler.
14. Import the addReport function and call it in add.js
. Be sure to include the three arguments being pulled from the argv
object.
The file will now have the code:
const reports = require('../../reports'); exports.command = 'add <name> <location> <state>' exports.desc = 'Add a new report' exports.builder = { name: { describe: 'Report name.', demandOption: true, type: 'string' }, location: { describe: 'Mashvisor neighborhood ID to pull data for.', demandOption: true, type: 'number' }, state: { describe: 'State to pull data for.', demandOption: true, type: 'string' }, } exports.handler = function(argv) { reports.addReport(argv.name, argv.location, argv.state) }
Let’s test that the arguments are making their way into the addReport()
function.
15. Comment out the API call in the function and add:
console.log(name, neighborhoodId, state)
Above the commented out code. Before the error is thrown it should log the arguments.
16. Run node cli.js reports add "test" 45624 "WA"
// console output ... test 45624 WA // our arguments that we entered! ReferenceError: overviewData is not defined // rest of the stack ...
Perfect! Our arguments are making their way to the function and can now be used in the API call to Mashvisor.
17. Uncomment the code and delete the console log. Run the following command to add our first actual report:
node cli.js reports add "first report" 271841 "WA"
Check inside of reports.json
and you should see our example data and our first report data!
There are two more things left that we need to do.
- Finish the
viewReport(name)
function so we can display this data in a nice way. - Write a command that finds the neighborhood closest to our current location.
The View Function
In this function we will need to:
- Load the reports (we have a function for that)
- See if the given name passed in the <name> argument is valid, and;
- Call a function that prints the reports
We are going to use almost identical code from the add function.
- Add the
find()
function andloadReports()
function to ourviewReport(name)
// reports.js ... const viewReport = (name) => { const reports = loadReports(); let found = reports.find((report) => report.name === name) // if the report is found call a function that prints the data to console } ...
2. Set up an if/else block to call a function if a report is found with that name. If not report matches, log that the report could not be found.
// reports.js ... const viewReport = (name) => { const reports = loadReports(); let found = reports.find((report) => report.name === name) if (found) { printBasicReport(found); // doesnt exist yet } else { console.log(chalk.red.inverse('That report could not be found.')) } } ...
The printBasicReport(found)
function does not exist yet.
3. Create a new directory at the root of the project named print_report
and inside that directory make a file with the name printBasicReports.js
.
We are going to construct an array with smaller arrays as values. Each array is going to have two things.
- The label for the data that we are printing.
- The correspoding data from the
found
object that we are passing it.
4. Enter the below code into our new file.
// printBasicReport.js const printBasicReport = (found) => { const reportLines = [ [" Report Name ", found.name], [" Location ", found.neighborhoodId], [" Airbnb Properties ",found.airbnb_properties], [" Traditional Properties ", found.traditional_properties], [" Mash Meter ",found.mash_meter], [" Avg. Occupancy ", found.avg_occupancy], [" ROI - Airbnb ", found.roi_airbnb], [" ROI - Traditional ", found.roi_traditional], [" Cap Rate - Airbnb ", found.cap_rate_airbnb], [" Cap Rate - Traditional ",found.cap_rate_traditional], [" Rental Income - Airbnb ", found.rental_income_airbnb], [" Rental Income - Traditional", found.rental_income_traditional], ] // iterate over the items and print them to console } module.exports = printBasicReport;
We are pulling items out of the found
object and matching it with an easier to read the label.
Next, add Chalk to the process and console.log()
our data and labels.
5. The final printBasicReports.js
file will have the code;
const chalk = require('chalk') // import chalk const printBasicReport = (found) => { const labelText = chalk.white.bgCyan; // create a function using chalk const reportLines = [ [" Report Name ", found.name], [" Location ", found.neighborhoodId], [" Airbnb Properties ",found.airbnb_properties], [" Traditional Properties ", found.traditional_properties], [" Mash Meter ",found.mash_meter], [" Avg. Occupancy ", found.avg_occupancy], [" ROI - Airbnb ", found.roi_airbnb], [" ROI - Traditional ", found.roi_traditional], [" Cap Rate - Airbnb ", found.cap_rate_airbnb], [" Cap Rate - Traditional ",found.cap_rate_traditional], [" Rental Income - Airbnb ", found.rental_income_airbnb], [" Rental Income - Traditional", found.rental_income_traditional], ] // iterate over the lines adding some formatting reportLines.map(line => { console.log( labelText(line[0]) + ":nt" + "---- " + line[1] ) }) } module.exports = printBasicReport;
6. Head back over to reports.js
and import the new function at the top
// reports. js ... const printBasicReport = require('./print_report/printBasicReport') ...
This command is almost ready to be used, but we need to add the function to view.js
7. Add the function to view.js
// view.js const reports = require('../../reports') // import function into file exports.command = 'view <name>' exports.desc = 'View one report' exports.builder = { name: { describe: "Name of report you want to view.", demandOption: true, type: 'string' } } exports.handler = function(argv) { reports.viewReport(argv.name) // call function and pass it name from argv object }
8. In the root of our directory execute the command
node cli.js reports view "first report"
The output in the console should look like this!
This is a lot of useful information from only one API call. We can use the Mash Meter as a predictive guide or simply compare R.O.I’s and rental income between Airbnb and traditional methods.
If you wanted to save the information for later, you could simply redirect the project output to a different file using the command;
node cli.js view [name_of_report] > fileName.txt
This will create the file and write them out like the contents of that file.
Find Your Closest Neighborhood
I am going to throw two files at you that will allow you to enter your coordinates and get the neighborhoodId that is closest to you.
The first is find-closest.js
. This is a new command and the new file will go in the cmds/
directory.
- Create the file and add this code to it. Nothing new as far as building a command goes.
const findClosestNeighborhood = require('../util/findClosestNeighborhood') exports.command = 'find-closest <lat> <long> <city> <state>' exports.desc = 'Finds closts neighborhood id' exports.builder = { lat: { describe: 'Latitude of current location', demandOption: true, type: 'number' }, long: { describe: 'Longitude of current location', demandOption: true, type: 'number' }, city: { describe: 'City the location is in', demandOption: true, type: 'string' }, state: { describe: 'State the location is in', demandOption: true, type: 'string' }, } exports.handler = function (argv) { findClosestNeighborhood(argv.lat, argv.long, argv.city, argv.state) }
The next file was the file imported at the top of this page (if you noticed).
2. In the util
directory create the file findClosestNeighborhood.js
and add the code:
const { util } = require('../api') const chalk = require('chalk') // I got the getDistance function from this SO article // https://stackoverflow.com/questions/27928/calculate-distance-between-two-latitude-longitude-points-haversine-formula?rq=1 function getDistance(lat1, lon1, lat2, lon2) { var p = 0.017453292519943295; // Math.PI / 180 var c = Math.cos; var a = 0.5 - c((lat2 - lat1) * p)/2 + c(lat1 * p) * c(lat2 * p) * (1 - c((lon2 - lon1) * p))/2; return 12742 * Math.asin(Math.sqrt(a)); // 2 * R; R = 6371 km } async function findClosestNeighborhood(lat, long, city, state) { try { const { data } = await util.getNeighborhoodsInArea(city, state); const neighborhoods = data.content.results; let closestNeighborhood; neighborhoods.map(nbh => { const distance = getDistance(lat, long, nbh.latitude, nbh.longitude) if (closestNeighborhood === undefined) { closestNeighborhood = {...nbh, distance} } else if (distance < closestNeighborhood.distance) { closestNeighborhood = {...nbh, distance} } }) console.log("The closest neighborhood is " + chalk.black.bgWhite(closestNeighborhood.name) + " with an ID of " + chalk.black.bgWhite(closestNeighborhood.id)) } catch (e) { console.log(e) console.log("Neigborhood list could not be retrived") } } module.exports = findClosestNeighborhood;
This file takes in your coordinates, sends a request to the ‘List Neighborhoods’ endpoint at Mashvisor and gets back a list of objects representing the neighborhoods in your city.
It then compares the provided coordinates to the coordinates for each neighborhood and determines which is closest. It’s not a perfect distance calculation function, but it’s close enough for us.
We need to add the API call to the util
object in api.js
.
3. Add the list neighborhood HTTP request to api.js
underneath our previous API call.
// api.js ... module.exports = { util: { ... getNeighborhoodsInArea: (city, state) => instance({ "method": "GET", "url":`https://mashvisor-api.p.rapidapi.com/city/neighborhoods/${state}/${city}`, }) }, ... }
4. Get the coordinates of your location or any location that you are interested in. If you need help getting latitude and longitude coordinates Google has steps to help.
5. Execute the find-closest command with coordinates, city, and state.
I ran the command for a location on Capitol Hill in Seattle.
node cli.js find-closest 47.627464 -122.321387 "Seattle" "WA"
And in the console, the closest neighborhood was logged.
The closest neighborhood is Broadway with an ID of 250150
I could now rerun my add report command with this new Mashvisor neighborhood ID!
That’s it!
Moving Forward
This application covered a lot of topics and skipped over a few as well. It is set up so the creator can make modular commands, reports, and simplify their HTTP request code.
One of the more difficult aspects of grabbing data from an API is pulling the data from the depths of the response object. This task is taxing especially if we built this application to make 10 or 20 API calls.
If we both agree that digging into objects is a bit annoying then you should check out GraphQL because RapidAPI recently added support for this type of query language.
However, feel free to add more HTTP calls to the application if you wish!
Leave a Reply