On Youtube: Web API's
Over web API's
Wat is een web API
Algemeen in programmeren is een API of Application Programming Interface, een set van methodes die je als developer kan gebruiken om informatie uit te wisselen met jouw toepassing.
Een Web API is hetzelfde maar dan op het web, dus een dienst die je kan integreren in jouw webtoepassing. Denk aan:
- opvragen van de laatste Twitter tweets rond een bepaald onderwerp
- integratie Google ads in je website
- gebruik route-informatie van Google Maps (b.v. afstanden)
- random quotes opvragen van een random quote generator
- machine learning & AI, bv. tekst- of fotoherkenning
- weather service gebruiken
- sportuitslagen opvragen
- ...
Web API's vinden
Er zijn massa's dergelijke API's te vinden! Bijna alle grote webtoepassingen als Youtube, Google Maps, Dropbox, Twitter, Flickr enz... hebben er één (zoek via google, bv. "Flickr API"). Kleinere (en vaak makkelijker te gebruiken) API's bestaan ook: weather service, random quotes...
RapidAPI heeft een uitgebreide collectie op haar website, en je hoeft maar één keer te registreren:
Web API's gebruiken
Hoe je een Web API moet gebruiken varieert enorm, van heel eenvoudig tot zeer moeilijk. Enkele aspecten om rekening mee te houden:
-
wat biedt de API?
- welke informatie, welke methodes en parameters zijn beschikbaar?
- hoe goed is de documentatie (vaak een zwak punt)?
- welke formaten worden gebruikt voor de request en response informatie?
-
hoe verloopt de authenticatie?
- volledig open (geen authenticatie nodig) → super simpel; komt minder en minder voor
- registratie nodig; je krijgt een API key of token die je met elke request moet meesturen → goed te doen
- registratie nodig; complexe authenticatiemethodes als OAuth → te moeilijk voor deze cursus
-
wat zijn de prijzen/quota?
- volledig gratis en zonder limieten → komt minder en minder voor
- gratis maar gelimiteerd, bv. dagelijkse / maandelijkse quota gelinkt aan de API key → kunnen we gebruiken
- betalend, bv. een vast bedrag per request → geen optie voor deze cursus
Dog API voorbeeld: open API, geen API key
Een API waar geen registratie of key voor nodig is, heet een open API. Er is geen enkele controle of limiet op het gebruik. Een voorbeeld is de Dog API, waar je informatie kan vinden over honden:
- lijst van alle hondenrassen
- een random foto
- foto's van een specifiek hondenras
- ...
documentatie
Zoals je ziet in de documentatie, bestaat deze eenvoudige API uit een set URL's waarmee je
informatie kan opvragen, bv.
https://dog.ceo/api/breeds/list/all
https://dog.ceo/api/breeds/image/random
https://dog.ceo/api/breed/schnauzer/images
request en response
Vragen we bijvoorbeeld via https://dog.ceo/api/breed/hound/images/random/5 vijf random foto's op van het ras "hound" (de request), dan krijg je volgende informatie terug (de response):
- de response is geen HTML, maar JSON (Javascript Object Notation)
- er bestaan andere response formaten (XML, SOAP, CSV...) maar dit formaat is het meest gebruikt bij API's
JSON
JSON of Javascript Object Notation is technisch gezien de notatie van een object in Javascript
{"message":["https:\/\/images.dog.ceo\/breeds\/hound-afghan\/n02088094_2292.jpg","https:\/\/images.dog.ceo\/breeds\/hound-blood\/n02088466_7091.jpg","https:\/\/images.dog.ceo\/breeds\/hound-blood\/n02088466_7609.jpg","https:\/\/images.dog.ceo\/breeds\/hound-english\/n02089973_208.jpg","https:\/\/images.dog.ceo\/breeds\/hound-ibizan\/n02091244_2820.jpg"],"status":"success"}
Je zou de response zelfs kunnen kopiëren in een Javascript variabele en gebruiken in je pagina:
const data = {
message: [
'https://images.dog.ceo/breeds/hound-afghan/n02088094_2292.jpg',
'https://images.dog.ceo/breeds/hound-blood/n02088466_7091.jpg',
'https://images.dog.ceo/breeds/hound-blood/n02088466_7609.jpg',
'https://images.dog.ceo/breeds/hound-english/n02089973_208.jpg',
'https://images.dog.ceo/breeds/hound-ibizan/n02091244_2820.jpg'
],
status: 'success'
}
Verwerkt in een eenvoudige toepassing:
<button type="button" id="btnRandomDogs">FETCH DOGS</button>
<div id="dogs"></div>
img {
margin: 10px;
height: 200px;
}
const btnRandomDogs = document.querySelector('#btnRandomDogs');
const divDogs = document.querySelector('#dogs');
function fetchRandomDogs() {
// data
const data = {
message: [
'https://images.dog.ceo/breeds/hound-afghan/n02088094_2292.jpg',
'https://images.dog.ceo/breeds/hound-blood/n02088466_7091.jpg',
'https://images.dog.ceo/breeds/hound-blood/n02088466_7609.jpg',
'https://images.dog.ceo/breeds/hound-english/n02089973_208.jpg',
'https://images.dog.ceo/breeds/hound-ibizan/n02091244_2820.jpg'
],
status: 'success'
};
// generate images
if (data.status == 'success') {
data.message.forEach(url => {
const img = `<img src="${url}" alt="">`;
divDogs.innerHTML += img;
});
}
}
btnRandomDogs.addEventListener('click', function() {
fetchRandomDogs();
});
toepassing 1: 5 random foto's ophalen
Met fetch()
kan je zo'n URL dynamisch opvragen en de data gebruiken:
<button type="button" id="btnRandomDogs">FETCH DOGS</button>
<div id="dogs"></div>
img {
margin: 10px;
height: 200px;
}
const btnRandomDogs = document.querySelector('#btnRandomDogs');
const divDogs = document.querySelector('#dogs');
async function fetchRandomDogs() {
// build request
const url = 'https://dog.ceo/api/breed/hound/images/random/5';
// fetch data
const resp = await fetch(url);
if (!resp.ok) {
console.log('opvragen random dogs mislukt');
return;
}
const data = await resp.json();
// process data
data.message.forEach(url => {
divDogs.innerHTML += `<img src="${url}" alt="">`;
});
}
// start fetching on click
btnRandomDogs.addEventListener('click', async function() {
console.log('start feching dogs...');
await fetchRandomDogs();
console.log('...fetch finished');
});
De uitleg over async/await volgt later nog, maar kort gezegd betekent async: "dit kan even duren, doe alvast verder met de rest", en await: "nee nee, ik wacht wel tot het resultaat binnen is"
toepassing 2: foto's van een specifiek ras ophalen
Een iets uitgebreider voorbeeld, waarbij eerst de hondenrassen opgehaald worden in een lijstje:
<select id="selBreeds" class="hide"></select>
<p id="parMessage"> </p>
<div id="divImages"></div>
img {
margin: 10px;
height: 200px;
}
const selBreeds = document.querySelector('#selBreeds');
const parMessage = document.querySelector('#parMessage');
const divImages = document.querySelector('#divImages');
selBreeds.addEventListener('change', async function() {
// build request
parMessage.innerHTML = `fetching images for ${this.value}`;
const url = `https://dog.ceo/api/breed/${this.value}/images`;
// fetch data
const resp = await fetch(url);
const data = await resp.json();
if (data.status != 'success') {
console.log('fetching breeds failed');
return;
}
// show photos
divImages.innerHTML = '';
data.message.forEach(src => {
divImages.innerHTML += `<img src="${src}" alt="">`;
});
parMessage.innerHTML = `done fetching images for ${this.value}!`;
});
async function fetchBreeds() {
// build request
const url = 'https://dog.ceo/api/breeds/list/all';
// fetch data
const resp = await fetch(url);
const data = await resp.json();
if (data.status != 'success') {
console.log('fetching breeds failed');
return;
}
// populate list of breeds and show list
selBreeds.innerHTML = `<option value="">selecteer...</option>`;
for (const breed in data.message) {
selBreeds.innerHTML += `<option>${breed}</option>`;
}
selBreeds.classList.remove('hide');
}
async function doFetch() {
parMessage.innerHTML = 'fetching breeds...';
await fetchBreeds();
parMessage.innerHTML = 'done fetching breeds - select one from the list';
}
doFetch();
Flickr API voorbeeld: API key, request parameters
API key aanvragen
Flickr is een foto community met een zeer uitgebreide API. Het verschil met het vorige voorbeeld is dat je (na registratie) een API key moet aanvragen, en die meesturen met elke request. De key is gratis, maar het geeft hen, in tegenstelling tot open API's, wel een beetje controle op je verbruik.
documentatie
In de uitgebreide documentatie vinden we een methode flickr.photos.search, met alle mogelijke opties. Een aantal van deze opties zullen we meesturen als request parameters.
toepassing: zoeken op foto's
Verwerkt in een eenvoudige toepassing:
<form action="" id="frmSearch">
<label>
search:
<input id="inpSearch" type="text" placeholder="enter keyword (.e.g. Eiffel)">
</label>
</form>
<div id="pics"></div>
#pics {
display: flex;
flex-flow: row wrap;
}
#pics img {
height: 200px;
display: block;
margin-top: 20px;
margin-right: 20px;
}
#search {
width: 200px;
}
const pics = document.querySelector('#pics');
const frmSearch = document.querySelector('#frmSearch');
const inpSearch = frmSearch.querySelector('#inpSearch');
const API_KEY = '60dce28390e0a3...'; // vul je eigen key in!
async function makeSearch(searchval) {
// build request
let url = 'https://api.flickr.com/services/rest/';
const params = new URLSearchParams();
params.append('api_key', API_KEY);
params.append('extras', 'url_m');
params.append('format', 'json');
params.append('method', 'flickr.photos.search');
params.append('per_page', 20);
params.append('nojsoncallback', 1);
params.append('text', searchval);
url += '?' + params.toString();
// fetch data
const resp = await fetch(url);
if (!resp.ok) {
console.log('opvragen foto\'s mislukt');
return;
}
const data = await resp.json();
// process data
console.log(data)
data.photos.photo.forEach(photo => {
pics.innerHTML += `<img src="${photo.url_m}" alt="">`;
});
}
frmSearch.addEventListener('submit', async function (e) {
e.preventDefault();
pics.innerHTML = '';
console.log('start fetching photos...');
await makeSearch(inpSearch.value);
console.log('...done!');
});
Github API voorbeeld: token, request headers
open gebruik — beperkt
Je kan in principe de Github API open gebruiken, zonder API key of token:
<ul id="lstRepos"></ul>
const lstRepos = document.querySelector('#lstRepos');
async function getMyRepos() {
// build request
const username = 'rogiervdl';
const url = `https://api.github.com/users/${username}/repos`;
// fetch data
const resp = await fetch(url);
if (!resp.ok) {
console.log('opvragen github repos mislukt');
return;
}
const data = await resp.json();
// show repos
data.forEach(entry => {
lstRepos.innerHTML += `<li>
<a href="${entry.html_url}">${entry.full_name}</a>
— ${entry.description}</li>`;
});
}
async function doGetRepos() {
console.log('fetching repos...');
await getMyRepos();
console.log('...done');
}
doGetRepos();
Het aantal requests is voor anonieme gebruikers echter standaard beperkt tot amper 60 per uur. Je kan het je kan het aantal resterende requests terugvinden in de response headers onder Network (klik links op de AJAX request):
met bearer token
Je kan als geregistreerde gebruiker dit optrekken naar 5000 per uur door een token te genereren:
- ga naar je profiel, settings, developer settings, personal access tokens
- klik op Generate new token
- kies een naam, expiration date, en duid de nodige rechten aan
- je krijgt een token string, b.v.
ghp_fIWhq1DrL4668baCWMuF7WNO56TvdT1DCdXc
Deze token geef je mee met de headers van je request:
<ul id="lstRepos"></ul>
const lstRepos = document.querySelector('#lstRepos');
async function getMyRepos() {
// build request
const username = 'rogiervdl';
const url = `https://api.github.com/users/${username}/repos`;
const options = {
headers: {
'Authorization': 'Bearer ghp_fIWhq1DrL...' // geef hier je eigen token mee!
}
};
// fetch data
const resp = await fetch(url, options); // voeg options toe als tweede parameter
if (!resp.ok) {
console.log('opvragen github repos mislukt');
return;
}
const data = await resp.json();
// show repos
data.forEach(entry => {
lstRepos.innerHTML += `<li>
<a href="${entry.html_url}">${entry.full_name}</a>
mdash; ${entry.description}</li>`;
});
}
async function doGetRepos() {
console.log('fetching repos...');
await getMyRepos();
console.log('...done');
}
doGetRepos();
Je krijgt nu 5000 requests per uur:
Rapid API voorbeeld: API key, request headers
registratie en API key aanmaken
Rapid API is niet één API, maar een grote verzameling API's die je kan gebruiken met wisselende gebruiksvoorwaarden. Het grote voordeel is dat je maar één keer een API key hoeft aan te vragen voor alle API's. Maak na registratie een app aan en ontvang je API key:
toepassing: Currency Exchange API
Bij wijze van voorbeeld testen we de Currency Exchange API. Het biedt twee methodes: listquotes en exchange (panel links) met telkens een code voorbeeld (panel rechts; kies Javascript uit de dropdown).
Het (kleine) verschil met het Flickr voorbeeld is dat je de API key als header, en niet als URL parameter moet meegeven.
<form id="frmCurrencies">
<p>
<label for="selFrom">Van:</label>
<select id="selFrom"></select>
</p >
<p >
<label for="selTo">Naar:</label>
<select id="selTo"></select >
</p>
<p>
<button type="submit">zoek wisselkoers</button>
</p>
</form>
body, input {
font-family: Verdana;
font-size: 14px;
}
button, select {
border: 1px solid #666;
padding: 3px 5px;
}
form > p {
display: flex;
}
form > p label {
width: 100px;
}
form > p:last-child {
padding-left: 100px;
}
.hide {
visibility: hidden;
}
const frmCurrencies = document.querySelector('#frmCurrencies');
const parMessage = document.querySelector('#parMessage');
const selFrom = document.querySelector('#selFrom');
const selTo = document.querySelector('#selTo');
const fetchOptions = {
method: 'GET',
headers: {
'X-RapidAPI-Key': '8bd0826a8amshbaea...', // vul je eigen key in!
'X-RapidAPI-Host': 'currency-exchange.p.rapidapi.com'
}
};
// get an array of all available currencies
async function getAllCurrencies() {
// build request
const url = 'https://currency-exchange.p.rapidapi.com/listquotes';
// fetch
const resp = await fetch(url, fetchOptions);
if (!resp.ok) {
console.log('opvragen lijst munten mislukt');
return;
}
const data = await resp.json();
// return data
return data;
}
// get the exchange rate for two currencies
async function getExchangeRate(curr1, curr2) {
// build request
const params = new URLSearchParams();
params.append('from', curr1);
params.append('to', curr2);
const url = `https://currency-exchange.p.rapidapi.com/exchange?${params.toString()}`;
// fetch
const resp = await fetch(url, fetchOptions);
if (!resp.ok) {
console.log('opvragen wisselkoers mislukt');
return;
}
const data = await resp.json();
// return data
return data;
}
// populate dropdowns
async function startApp() {
const currencies = await getAllCurrencies();
currencies.sort().forEach(curr => {
selFrom.innerHTML += `<option>${curr}</option>`;
selTo.innerHTML += `<option>${curr}</option>`;
});
frmCurrencies.classList.remove('hide');
selFrom.value = 'EUR';
selTo.value = 'USD';
parMessage.innerHTML = 'selecteer twee munten...';
}
// handle form submits
frmCurrencies.addEventListener('submit', async function(e) {
e.preventDefault();
const rate = await getExchangeRate(selFrom.value, selTo.value);
parMessage.innerHTML = `1 ${selFrom.value} = ${rate} ${selTo.value}`;
});
// start your engines
startApp();
Async/await
Asynchrone functies
Bij sommige functies — zoals fetch() — weet je niet wanneer ze resultaat geven. Men zegt dat ze asynchroon zijn: de rest van de code wacht niet en loopt ondertussen gewoon verder! Een screenshot:
async/await
Met async ("dit kan even duren, wacht niet op het resultaat") en await ("ik wacht liever op het resultaat") kan je het asynchroon gedrag sturen. Weer een screenshot:
Async / await is in het begin een beetje verwarrend, maar volgende principes kunnen helpen:
- bepaalde functies waarvan de resultaten even op zich kunnen laten wachten — zoals fetch() — worden asynchroon uitgevoerd: het vervolg van de code wacht niet met uitvoering
- als je toch wil wachten op het resultaat, roep je de functie op met await
- functies waarin await gebruikt wordt, zijn zelf async — de resultaten kunnen hier immers ook even op zich laten wachten
Er kunnen situaties zijn waarin het wenselijk is asynchrone functies zonder await op te roepen, bijvoorbeeld als de uitvoering geen gevolgen heeft voor het verdere verloop van de code, of als je meerdere processen in parallel wil laten lopen, maar je ben altijd veilig met volgende regels:
- roep asynchrone functies standaard altijd op met await
- maak functies die await bevatten asynchroon met async
API tester — Postman app
Met de postman app kan je API's testen zonder code. Je kan URL's ingeven, request parameters, headers enz... en de response inspecteren in verschillende formaten.
Een aanrader voor wie in detail de request/responses wil testen.