Uwaga! Trwają prace nad nową wersją serwisu. Mogą występować przejściowe problemy z jego funkcjonowaniem. Przepraszam za niedogodności!
⛔ Potrzebujesz wsparcia? Oceny CV? A może Code Review? ✅ Dołącz do naszej społeczności na Discordzie!
Uczysz się obsługi kodu asynchronicznego w JavaScripcie, a tu nagle błąd: skończył Ci się limit zapytań! Spokojnie, da się to obejść – nawet na co najmniej dwa sposoby. Po lekturze tego artykułu będziesz wiedzieć, po co twórcy API nakładają limity i jak poradzić sobie z nimi podczas nauki programowania.
API, czyli Application Programming Interface, to zestaw reguł określających sposób komunikacji między systemami komputerowymi. Dzięki nim możemy wysyłać żądania (zapytania) do zewnętrznych zasobów (np. serwera z danymi o pogodzie) i otrzymywać odpowiedzi.
Ograniczenia mogą być różne: konkretny limit zapytań na minutę, godzinę czy dobę. Czemu to służy? Celem jest ochrona systemu przed przeciążeniem – duża liczba zapytań spowodowałaby, że usługa działałaby wolniej lub wcale.
Nie zdziwi Cię pewnie również to, że limity służą do kontrolowania kosztów i wydajności usługi. Stworzenie płatnych planów z większym limitem zapytań to nie tylko biznes, ale możliwość dostosowania infrastruktury do wymagań klientów. Przykładowo: jeśli wzrasta liczba klientów opłacających usługę, możemy zaplanować zakup kolejnych serwerów.
Kod, który odpytuje API, będzie się uruchamiał po każdym odświeżeniu strony (czyli po każdym zapisaniu pliku, gdy korzystamy z automatycznego odświeżania). Jeśli więc API w darmowym planie ogranicza zapytania np. do 50, to limit ten bardzo szybko się wyczerpie.
Możemy sobie z tym poradzić, tymczasowo tworząc „fake’owe API”, i dopiero na końcu, gdy nasz projekt jest gotowy, podmienić kod na właściwy. Są na to co najmniej dwa sposoby.
Twoja funkcja odpytująca API może wyglądać mniej więcej tak:
async function get() { try { const response = await fetch('https://catfact.ninja/fact'); if (response.ok) { const data = await response.json(); return data; } throw new Error(response.statusText); } catch (error) { console.error(error); } finally { console.log('Odpytywanie API zakończone!'); } } const response = await get(); console.log(response.fact);
Lub z wykorzystaniem metody then w ten sposób:
async function get() { const promise = fetch('https://catfact.ninja/fact'); return promise .then(resp => { if (resp.ok) { return resp.json(); } return Promise.reject(resp); }) .catch(err => console.error(err)) .finally(() => { console.log('Odpytywanie API zakończone!'); }); } get().then(response => { console.log(response.fact); });
Tymczasowo zakomentuj część kodu (koniecznie pamiętaj zakomentować też linię z funkcją fetch!) i zwróć własną obietnicę z obiektem, który odpowiada temu, co dostajesz z API.
Dla zapisu z async…await będzie to wyglądać tak:
async function get() { try { // const response = await fetch('https://catfact.ninja/fact'); // if (response.ok) { // const data = await response.json(); // return data; // } // throw new Error(response.statusText); return { fact: 'A cat cannot see directly under its nose.', length: 41 } } catch (error) { console.error(error); } finally { console.log('Odpytywanie API zakończone!'); } } const response = await get(); console.log(response.fact);
Natomiast w wersji z then tak:
async function get() { // const promise = fetch('https://catfact.ninja/fact'); // return promise // .then(resp => { // if (resp.ok) { // return resp.json(); // } // return Promise.reject(resp); // }) return new Promise(function (resolve) { resolve({ fact: 'A cat cannot see directly under its nose.', length: 41, }); }) .catch(err => console.error(err)) .finally(() => { console.log('Odpytywanie API zakończone!'); }); } get().then(resp => { console.log(resp.fact); });
Jeśli chcesz rozbudować tymczasowy obiekt z danymi symulującym zwrot z API i jednocześnie nie „zaciemniać” sobie obrazu funkcji pobierającej dane, możesz po prostu zapisać obiekt w osobnej zmiennej w ten sposób:
const data = { fact: 'A cat cannot see directly under its nose.', length: 41 } async function get() { try { // const response = await fetch('https://catfact.ninja/fact'); // if (response.ok) { // const data = await response.json(); // return data; // } // throw new Error(response.statusText); return data } catch (error) { console.error(error); } finally { console.log('Odpytywanie API zakończone!'); } } const response = await get(); console.log(response.fact);
Zwróć uwagę, że w obecnej sytuacji dalsza część kodu (z .catch i .finally) nigdy się nie wykona, ponieważ wyżej zawsze zwracamy rozwiązaną obietnicę. Zostawiamy go jednak, ponieważ docelowo będzie nam potrzebny, gdy już przywrócimy fetchowanie danych z prawdziwego API.
To wszystko. Możesz „odpytywać” swoje fake’owe API do woli bez obaw o limity!
Aby nie musieć cały czas zaglądać do kodu obsługującego API i zakomentowywać lub odkomentowywać danych fragmentów, możemy stworzyć klasę z „przełącznikiem”.
Zwróć uwagę, że poniżej przedstawiona metoda get spełnia tę samą rolę, co funkcja get we wcześniejszych przykładach. Jedyne, co zrobiliśmy to:
Przełączanie między fake’owym a prawdziwym API odbywa się dzięki właściwości fakeAPIActive. Domyślnie jest ona ustawiona na false – jeżeli więc nie zmienimy jej umyślnie, będziemy łączyć się z prawdziwym API.
class CatFactsProvider { constructor(url) { this.url = url; this.fakeAPIActive = false; } getFakeData() { return new Promise(function (resolve) { resolve({ fact: 'A cat cannot see directly under its nose.', length: 41, }); }); } get() { if (this.fakeAPIActive) { return this.getFakeData(); } else { return this.fetchData(); } } fetchData() { const promise = fetch(this.url); return promise .then(resp => { if (resp.ok) { return resp.json(); } return Promise.reject(resp); }) .catch(err => console.error(err)) .finally(() => { console.log('Odpytywanie API zakończone!'); }); } } export default CatFactsProvider;
Jeśli chcesz dowiedzieć się więcej o takim porządkowaniu kodu obsługującego API, zapraszam Cię do artykułu „CRUD w osobnym pliku – lepszy kod dla API”.
Importujemy teraz klasę CatFactsProvider do pliku index.js (bo tam chcemy skorzystać z API). Na jej podstawie tworzymy obiekt api (pamiętamy o przekazaniu adresu URL interesujących nas zasobów) oraz ustawiamy właściwość api.fakeAPIActive na true, ponieważ chcemy skorzystać z fake’owych danych.
import CatFactsProvider from './CatFactsProvider.js'; document.addEventListener('DOMContentLoaded', function () { const api = new CatFactsProvider('https://catfact.ninja/fact'); api.fakeAPIActive = true; //uruchamiam fake'owe API api.get().then(function (response) { console.log(response.fact); }); });
Gotowe. Od teraz nie musisz zakomentowywać i odkomentowywać kodu fake’owej obietnicy, wystarczy zmienić true na false i odwrotnie!
To rozwiązanie jest bardziej czasochłonne, więc póki zależy Ci tylko na pobieraniu danych z API, raczej bym się na nie nie decydował.
Warto jednak sięgnąć po JSON Server, gdy chcesz przećwiczyć pozostałe operacje: aktualizację, dodawanie i usuwanie danych przez API.
Wszystko, co jest potrzebne do rozpoczęcia pracy z tym serwerem lokalnym, znajdziesz w artykule „JSON Server – asynchroniczny JavaScript z lokalnym API”.
Od teraz nie musisz martwić się limitem zapytań. Pamiętaj tylko, aby przywrócić poprzedni kod realnie odpytujący API, gdy będziesz już wrzucać projekt do portfolio lub oddawać go do code review.
Udostępnij ten artykuł:
Potrzebujesz cotygodniowej dawki motywacji?
Zapisz się i zgarnij za darmo e-book o wartości 39 zł!
PS. Zazwyczaj rozsyłam 1-2 wiadomości na tydzień. Nikomu nie będę udostępniał Twojego adresu email.
Mam coś dla Ciebie!
W każdy piątek rozsyłam motywujący do nauki programowania newsletter!
Dodatkowo od razu otrzymasz ode mnie e-book o wartości 39 zł. To ponad 40 stron konkretów o nauce programowania i pracy w IT.
PS Zazwyczaj wysyłam 1-2 wiadomości na tydzień. Nikomu nie będę udostępniał Twojego adresu e-mail.