DEV Community

Lisa Armstrong
Lisa Armstrong

Posted on • Updated on

Using Vue for APIs

At some level, most apps use data and it's probably coming from an API. In this tutorial, we'll use Vue to fetch the data and display it.

This example

Let's say you need to display a list of countries in North America. The list will show the country, the capital and its population.

You found the API with the data called REST Countries, it has country size and population -- just what you need.

Welcome to API, Can I Take Your Order?

When you think about it, APIs are kind of like drive-through restaurants, you make a request and get a response. However, you need to know a few things such as:

1. Where is the food / data located?

You need to the address of the restaurant like you need to know the URL for the API.

2. What's on the menu?

Do you ask for 'chips' or 'fries'?
Yeah, they're the same, but you need to use the right word / parameters to get what you want.

3. What do you get?

When you're handed your food / data in a package, you need to know what's in there. Is ketchup included or vinegar? Likewise, what fields and formats are returned?

4. Can you make special requests?

How do you get extra napkins / request certain fields?

Read the Menu

It's good to have an idea of what's involved with the API before beginning your project. Have a look at the menu / documentation, it'll make the job easier.

Accessing APIs In Vue

Vue is a javascript framework, so it doesn't have a specific method to use APIs. Ajax or Fetch are ok. Axios is often recommended because it's simple to use and works, so we'll use that.

Enough theory, let's get our hands on the keyboard and start coding!

The Basic Set-Up

To keep things simple, we'll build the app in a web page.

For that, we'll use two files,

  1. HTML file to display the data and load the Vue file.
  2. The Vue file has the code.

HTML File:

<html lang="en">

<head>
    <!-- Load Vue -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

    <!-- Load script file.  'defer' loads after body is generated -->
    <script src="vue_lists.js" defer></script>

    <style>
        table,
        th,
        td {
            border: 1px solid #cccccc;
            border-collapse: collapse;
        }
    </style>
</head>

<body>
    <h1>North America</h1>
    <!-- Div where Vue runs -->
    <div id="app">
        <h2>Table</h2>
        <table>
            <tr>
                <th>Country</th>
                <th>Capital</th>
                <th>Population</th>
            </tr>
            <tr>
                <td></td>
                <td></td>
                <td></td>
            </tr>
        </table>
    </div>
</body>

</html>

Vue.js File:

In this file we're using 'countries_list' as the array that will contain the data. It's empty for now.

var app = new Vue({
    el: '#app',
    data: {
        countries_list: [],
    },
})

Loading Axios

To use Axios in your application, you need to load it. We're keeping it simple, so we'll use the cdn, which we'll add to the HTML header.

<!-- Load Axios -->
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script> 

Now that it's loaded, you can access it in Vue.

Get the Data

Back to the drive-through metaphor. We're in the car (Axios), ready to head out. We know the location (url), we read the menu (documentation) so we know what to ask for (url string).
Let's place an order!

Based on the documentation, the request string should look like: https://restcountries.eu/rest/v2/region/americas

Great! We know how to get the data, now lets get it on the screen. The data should display as soon as the page is loaded, so how do you do that?

Hang It On A Hook

Vue has a series of life cycle hooks that fire at certain stages during loading. The 'mounted' hook fires when the Dom is loaded. That works! We'll put the Axios call in there and set it up so we can have a look at the raw data in the console log.

mounted() {
        axios
            .get('https://restcountries.eu/rest/v2/region/americas')
            .then(response => (
                console.log(response)
                ))
    }

Breakdown:

mounted() { ...} When the dom is loaded
axios Tell Axios to...
.get (....) ...use 'get' to go to this url and return the data
.then (...)
Once the data is returned...
response => (
console.log(response)
)
... call the data 'response' and show it in the console log.

Putting It Together

The code should look something like this:

HTML

<html lang="en">

<head>
    <!-- Load Vue -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

    <!-- Load Axios -->
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>

    <!-- Load script file.  'defer' loads after body is generated -->
    <script src="vue_lists.js" defer></script>

    <style>
        table,
        th,
        td {
            border: 1px solid #cccccc;
            border-collapse: collapse;
        }
    </style>
</head>

<body>
    <h1>North America</h1>
    <!-- Div where Vue runs -->
    <div id="app">
        <h2>Table</h2>
        <table>
            <tr>
                <th>Country</th>
                <th>Capital</th>
                <th>Population</th>
            </tr>
            <tr>
                <td></td>
                <td></td>
                <td></td>
            </tr>
        </table>
    </div>
</body>

</html>

Vue.js File:

var app = new Vue({
    el: '#app',
    data: {
        countries_list: [],
    },

    mounted() {
        axios
            .get('https://restcountries.eu/rest/v2/region/americas')
            .then(response => (
                console.log(response)
                ))
    }

})

The data in the console log looks like this:

{data: Array(57), status: 200, statusText: "", headers: {…}, config: {…}, …}
config: {adapter: ƒ, transformRequest: {…}, transformResponse: {…}, timeout: 0, xsrfCookieName: "XSRF-TOKEN", …}
data: (57) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
headers: {content-type: "application/json;charset=utf-8", cache-control: "public, max-age=86400"}
request: XMLHttpRequest {onreadystatechange: ƒ, readyState: 4, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, …}
status: 200
statusText: ""
__proto__: Object

It has everything we need, plus a whole lot more!

Pulling Out What You Need

Like a drive-through, you need to unpack the package to get to the food / data.

What we're seeing here is the full response, the status, the headers, the whole package. It's like being handed the bag at the drive-through window, we don't need the wrappers, just the food (data), so we need to unpack it a bit. You can do that by tweaking the code to return the response.data

.then(response => (
                console.log(response.data)
                ))

Tip: Because response returns everything, it's a good debugging tool.

The console log should look something like:

(57) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
0: {name: "Anguilla", topLevelDomain: Array(1), alpha2Code: "AI", alpha3Code: "AIA", callingCodes: Array(1), …}
1: {name: "Antigua and Barbuda", topLevelDomain: Array(1), alpha2Code: "AG", alpha3Code: "ATG", callingCodes: Array(1), …}
2: {name: "Argentina", topLevelDomain: Array(1), alpha2Code: "AR", alpha3Code: "ARG", callingCodes: Array(1), …}

That's better!

Making the Data Workable

The next step is to assign the data to a variable we can use to in the HTML file.

.then(response => (
                this.countries_list = response.data
                ))

Now countries_list has the data, we'll set up the HTML page to display it.

Displaying Data

We already have the table set up in the HTML file, we just need to get the data in there.

Right now, data is in an array called 'countries_list'. The trick here is to loop through it and display it in the table. This is done by using 'v-for', it's a kind of 'for loop' for Vue. You put inside an element that you want repeated for every record in the database.

Our example looks like:

<tr v-for="country in countries_list">
    <td>{{country.name}}</td>
    <td>{{country.capital}}</td>
    <td>{{country.population}}</td>
</tr>

Breakdown:

<tr v-for="country in countries_list"> Create a <tr>
for every record in 'coutry_list',
each record will be called 'country'

<td>{{country.name}}</td>
<td>{{country.capital}}</td>
<td>{{country.population}}</td>
To display data in Vue, you use the double curly brackets.
So, for each record, create the <td>
and wrap the data (called 'country')
and the field (name, capital population etc.) in the curly brackets
</tr> End of the row.

The HTML page should look like:

<html lang="en">

<head>
    <!-- Load Vue -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

    <!-- Load Axios -->
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>

    <!-- Load script file.  'defer' loads after body is generated -->
    <script src="vue_lists.js" defer></script>

    <style>
        table,
        th,
        td {
            border: 1px solid #cccccc;
            border-collapse: collapse;
        }
    </style>
</head>

<body>
    <h1>North America</h1>
    <!-- Div where Vue runs -->
    <div id="app">
        <h2>Table</h2>
        <table>
            <tr>
                <th>Country</th>
                <th>Capital</th>
                <th>Population</th>
            </tr>
            <tr v-for="country in countries_list">
                <td>{{country.name}}</td>
                <td>{{country.capital}}</td>
                <td>{{country.population}}</td>
            </tr>
        </table>
    </div>
</body>

</html>

The table should look like:

Country Capital Population
Anguilla The Valley 13452
Antigua and Barbuda Saint John's 86295
Argentina Buenos Aires 43590400
Aruba Oranjestad 107394
Bahamas Nassau 378040
Barbados Bridgetown 285000

We're getting somewhere!

Using API's in an Element
(or How to Use Vue in <img and Other Tags)

This API has a flag image for each country. That's a nice visual cue, so let's add that beside the country.

Although you use the double curly brackets for displaying API data in Vue, when you're applying it to an HTML element like an image tag, it works differently.

Here you use v-bind to tie data to an attribute. So the image tag would look something like:

<img v-bind:src="country.flag" alt="Flag" height="26" width="42">

Let's add it to the table.

<tr v-for="country in countries_list">
  <td>
     <img v-bind:src="country.flag" alt="Flag" height="26" width="42"> 
      {{country.name}}
   </td>
   <td>{{country.capital}}</td>
   <td>{{country.population}}</td>
 </tr>

Passing Parameters

With this API, you can pass a parameter to it telling it what fields to return. We only need

  • Country
  • Short form
  • Size
  • Population
  • Flag

Based on the documentation, the URL should look like:
https://restcountries.eu/rest/v2/region/americas?fields=name;capital;flag;population

We can change the Axios call to:

mounted() {
        axios
            .get('https://restcountries.eu/rest/v2/region/americas?fields=name;capital;flag;population')
            .then(response => (
                this.countries_list = response.data,
                console.log(response.data)
                ))
    }

Nothing has changed in the results but if you look in the console, you'll only see the data you need. Easy, huh?

[{"flag":"https://restcountries.eu/data/aia.svg","name":"Anguilla","capital":"The Valley","population":13452},{"flag":"https://restcountries.eu/data/atg.svg","name":"Antigua and Barbuda","capital":"Saint John's","population":86295},{"flag":"https://restcountries.eu/data/arg.svg","name":"Argentina","capital":"Buenos Aires","population":43590400},{"flag":"https://restcountries.eu/data/abw.svg","name":"Aruba","capital":"Oranjestad","population":107394},
.....

When passing the parameters in this case, it was simple -- a 'get' string. Of course some API's and apps are more complex and may require 'post' variables etc. Axios does a nice job of passing variables among many other things. For more info see: https://github.com/axios/axios

Handling Errors & Missing Data

What happened if there's a problem? Data is not returned, the restaurant is closed, then what?

You use catch. Catch will, well, catches the error so you handle it gracefully.

.catch(error =>(
                console.log(error)
                ));

As a rule you let the user know that there was a problem. Although you could set up some code within catch error, it's probably easier to set up an v-if statement in the HTML file.

In this case, if the 'countries_list' is empty, then let the user know. This can be done in the HTML page by adding another row that will display if the countries_list.length is 0.

<tr v-if="countries_list.length == 0">
    <td colspan="3">
        No records found.
    </td>
</tr>

Breakdown:

<tr v-if="countries_list.length == 0"> Create a <tr>
if the length of the array countries_list is zero

<td colspan="3">
No records found.
</td>
Only 1 td is need to expand all three columns that should be there. We still have 3 heading columns.
</tr> End of the row.

Important! Make sure your 'error row' is outside of the rows generated if there is a list. They are 2 different things!

Your table should look something like:

<table>
    <!-- Header -->
    <tr>
        <th>Country</th>
        <th>Capital</th>
        <th>Population</th>
    </tr>
    <!-- Country List -->
    <tr v-for="country in countries_list">
        <td>
            <img v-bind:src="country.flag" alt="Flag" height="26" width="42">
            {{country.name}}
        </td>
        <td>{{country.capital}}</td>
        <td>{{country.population}}</td>
    </tr>
    <!-- Error Row -->
    <tr v-if="countries_list.length == 0">
        <td colspan="3">
            No records found.
        </td>
    </tr>
</table>

Final Product

Our HTML code should look like:

<html lang="en">

<head>
    <!-- Load Vue -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

    <!-- Load Axios -->
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>

    <!-- Load script file.  'defer' loads after body is generated -->
    <script src="vue_lists.js" defer></script>

    <style>
        table,
        th,
        td {
            border: 1px solid #cccccc;
            border-collapse: collapse;
        }
    </style>
</head>

<body>
    <h1>North America</h1>
    <!-- Div where Vue runs -->
    <div id="app">
        <h2>Table</h2> 
        <table>
            <!-- Header -->
            <tr>
                <th>Country</th>
                <th>Capital</th>
                <th>Population</th>
            </tr>
            <!-- Country List -->
            <tr v-for="country in countries_list">
                <td>
                    <img v-bind:src="country.flag" alt="Flag" height="26" width="42">
                    {{country.name}}
                </td>
                <td>{{country.capital}}</td>
                <td>{{country.population}}</td>
            </tr>
            <!-- Error Row -->
            <tr v-if="countries_list.length == 0">
                <td colspan="3">
                    No records found.
                </td>
            </tr>
        </table>
    </div>
</body>

</html>

Our Vue script should look like:

var app = new Vue({
    el: '#app',
    data: {
        countries_list: [],
    },

    mounted() {
        axios
            .get('https://restcountries.eu/rest/v2/region/americas?fields=name;capital;flag;population')
            .then(response => (
                this.countries_list = response.data
                ))
            .catch(error =>(
                console.log(error)
                ));
    }

})

And our list should look something like:

Country Capital Population
Flag Anguilla The Valley 13452
Flag Antigua and Barbuda Saint John's 86295
Flag Argentina Buenos Aires 43590400
Flag Aruba Oranjestad 107394
Flag Bahamas Nassau 378040
Flag Barbados Bridgetown 285000
Flag Belize Belmopan 370300
Flag Bermuda Hamilton 61954

To Sum Up

Using API's in Vue is fairly straight forward. Make sure you know how to get your data (the drive-through), assign it to an array variable and display it in HTML.

Resources

For more information:

Discussion (4)

Collapse
chichiehliu profile image
ChiChieh-Liu

Thanks for sharing this!!! It's really help me a lot!!!

Collapse
moulinho profile image
moulinho

Thank you !

Collapse
bytomray profile image
Tom Ray

This is such an awesome post! Thank you Lisa 🙏

Collapse
workingwebsites profile image
Lisa Armstrong Author

Thanks Tom, I'm glad you got something out of it.