Většina webových aplikací pracuje s daty, která jsou uložena na nějakém vzdáleném serveru. Pokud chceme s daty na nějakém serveru pracovat, server musí poskytovat takzvané API (Application Programming Interface). Většina API poskytuje data ve formátu JSON. Nejprve si tedy povíme o tomto formátu a pak se naučíme si data stahovat pomocí JavaScriptu.

Formát JSON

Podle většiny moderních doporučení je lepší v JavaScriptu používat v řetězcích jednoduché uvozovky. V počátcích JavaScriptu však bylo běžné používat spíše dvojité. Pokud v našich objektech schválně uzavřeme všechny klíče a řetězce do dvojitých uvozovek i tam, kde by to jinak nebylo potřeba, dostaneme reprezentaci zapsanou takto.

const row = {
  "name": {
    "first": "Petr",
    "last": "Bílek"
  },
  "product": {
    "name": "Prací prášek",
    "amount": 1.5,
    "unit": "kg"
  },
  "price": 240
};

Toto je z hlediska JavaSriptu naprosto korektní zápis. Vznikne tak zcela stejný objekt, jak ten, který by vznikl bez použití uvozovek kolem klíčů. Tento způsob zápisu má své speciální jméno - JavaScript Object Notation, nebo-li JSON. Za dobu existence JavaScriptu se tento zápis rozšířil po celém internetu a dnes jde o nejpoužívanější formát pro výměnu dat. Pokud si tedy budeme chtít stáhnout data z nějakého API, dostaneme je většinou právě ve formátu JSON.

Volání API

K tomu, abychom mohli pracovat s API je potřeba si detailněji popsat, jak funguje komunikace s webovým serverem. Pokud po webovém serveru něco chceme, například HTML stránku nebo nějaká data, musíme odeslat takzvaný HTTP požadavek HTTP request . Každý požadavek musí být zacílený na nějakou URL adresu. Adresy URL mají následující formát.

protokol://adresa_serveru/cesta?parametr1=hodnota1&parametr2=hodnota2

Protokol je většinou http nebo https. Adresa serveru může být například www.google.com. Za adresou serveru následuje cesta, která specifikuje kam v rámci serveru se má požadavek odeslat. Za otazníkem se píšou takzvané parametry, které dále upřesňují požadavek.

Takto například vypadá adresa požadavku na server www.google.com, který spustí vyhledávání.

https://www.google.com/search?q=praha&sourceid=chrome

Cesta v požadavku je tedy /search, parametr q udává, že se má vyhledat slovo praha a parametr sourceid říká, že se ptáme z prohlížeče chrome. Tuto adresu můžete normálně zadat do prohlížeče a uvidíte výsledky vyhledávání.

Dotazy na API

Dotazy na API fungují naprosto stejně jako jsme popsali výše. Jedno z nejjednodušších API, které si můžete sami vyzkoušet, poskytuje informace o tom, kdo má který den v jaké zemi svátek. Pokud chcete vědět, kdo má zrovna dneska svátek v České republice, vložte do prohlížeče následující adresu.

https://api.abalin.net/today?country=cz

Zkuste si pro sebe určit, co je zde adresa serveru, cesta a parametry. Podle toho, jaký je zrovna den, dostanete odpověď podobnou této.

{"data":{"dates":{"day":9,"month":5},"namedays":{"cz":"Ctibor"}}}

Server se snaží posílat data co nejúsporněji a vynechává v JSONu pro počítače zbytečné mezery. Naštěstí dat není mnoho, takže jsou čitelná i v této formě.

Většina API poskytuje více různých cest. Pro vyhledání jména pro dnešní den jsme použili cestu /today. Můžeme však položit i obrácený dotaz a zeptat se, kdy má v Americe svátek Suzanne. K to mu použijeme cestu /getdate s parametry name a country.

https://api.abalin.net/getdate?name=suzanne&country=us

Pokud server poskytuje API, cestám v rámci jednoho serveru se říká endpointy. Z našeho API pro získání jsme zatím viděli dva různé endpointy. Toto API jich má však ještě více. Vrátíme se k nim v příkladech na procvičení.

Volání API z JavaScriptu

Většina API funguje velmi přímočaře. Odešlete správně zformulovaný požadavek na nějaký endpont a server vám pošle odpověď s kýženými daty nejčastěji ve formátu JSON.

Pokud chceme tento proces provést v JavaScriptu, máme k dispozici velmi šikovnou metodu fetch. Můžeme si tedy založit nový program a rovnou napsat toto.

const promise = fetch('https://api.abalin.net/today?country=cz');

Je zde však drobný zádrhel. Servery jsou různě rychlé podle toho, jak jsou vytížené nebo jak jsou od nás geograficky daleko. Dat ke stažené také může být poměrně hodně. Všechny tyto faktory přispívají k tomu, že stahování dat může trvat nějakou chvíli, a my dopředu nevím, jak dlouhá tato chvíle bude. Dokonce se může stát, že server data nakonec nepošle vůbec, protože je přetížený požadavky jiných klientů, nebo dokonce úplně spadnul a už není dostupný.

Kdybychom tedy v našem programu na prvním řádku čekali, až funkce fetch skončí, mohli bychom si taky počkat notnou chvíli. Mezi tím by náš program zcela zamrznul a uživatel by neměl radost. Funkce fetch proto nevrací data, nýbrž takzvaný promise. Promise je speciální konstrukce podobná časovači, u kterého však dopředu nevím, kdy skončí. Promise v podstatě říká “až to bude, tak to bude”. Na promise můžeme pověsit funkci, která se má zavolat ve chvíli, kdy dorazí odpověď od serveru. To se provede pomocí metody then.

const promise = fetch('https://api.abalin.net/today?country=cz');
promise.then((resp) => {
  console.log(resp);
});

V parametru resp máme uloženu odpověd ze serveru. Pokud z této odpovědi chceme získat JSON, stačí zavolat metodu json. Čeká nás však podraz. Tado metoda opět nevrací samotný JSON nýbrž pouze promise. Musíme tedy znova použít metodu then a vznikne nám trochu zamotaný kód.

const respPromise = fetch('https://api.abalin.net/today?country=cz');
const jsonPromise = respPromise.then((resp) => resp.json());
jsonPromise = jsonPromise.then((json) => console.log(json));

Po spuštění tohoto programu už se nám v konzoli objeví naše data. Kǒd výše však začiná být nepřehledný. Naštěstí se však můžeme snadno vyhnout ukládání promisů do proměnných, které jsou v tomto případě stejně zbytečné. Výsledný kód se pak o kus zpřehlední.

fetch('https://api.abalin.net/today?country=cz')
  .then((resp) => resp.json())
  .then((json) => console.log(json));

Tento zápis na první pohled může pořád vypadat složitě. Jeho použití je však pořád stejné. Stačí si jej tedy prostě zapamatovat a zvyknout si na to, že “takto se to prostě dělá.” Promisy jsou relativně komplikované a hluboké téma. Nebudeme proto do nich zabíhat hluběji než je v tuto chvíli nezbytně nutné.

Zpracování dat

Naše funkce pro zpracování získaných dat je zatím pouze vypisovala do konzole. Můžeme si však napsat funkci, která dělá cokoliv chceme. Takto například můžeme zobrazím jméno mající dnes svátek v elementu s třídou name.

const displayName = (json) => {
  const nameElm = document.querySelector('.name');
  nameElm.textContent = json.data.namedays.cz;
};

fetch('https://api.abalin.net/today?country=cz')
  .then((resp) => resp.json())
  .then(displayName);

Funkce fetch tedy funguje podobně jak událost. Naše funkce displayName se zavolá až ve chvíli, kdy skutečně dorazí data ze serveru.

Cvičení - volání API

1

Svátek zítra

to dáš

Zatím jsme z API pro získávání svátků viděli endpointy /today a /getdate. Existují však ještě další.

Napište aplikaci, která uživateli zobrazí, kdo má svátek zítra. K tomu použíjte endpoint /tomorrow, který funguje podobně jako endpoint /today.

2

Svátek v den

to dáš

Dalším užitečným endpointem API pro svátky je /namedays. Ten nám umožňuje zjistit, kdo má svátek v přesně určený den. Tento endpoint má tři povinné parametry: country, month a day. Takto například zjistíme, kdo má v Česku svátek 13. února.

https://api.abalin.net/namedays?country=cz&month=2&day=13

Vytvořte aplikaci, které bude obsahovat dvě textová políčka a tlačítko. Uživatel do políček zadá číslo dne a číslo měsíce a po stisknutí tlačítka se mu na stránce zobrazí, kdo má tento den svátek.

Google Sheets API

Většina webových aplikací potřebuje kromě frontendu také backend. Moderní aplikace často fungují tak, že backend poskytuje API, skrze které může frontend přistupovat k datům aplikace. Backendové API se dá napsat přímo v JavaScriptu. Bohužel je to však obsáhlé téma, které se nám už do tohoto kurzu nevejde. Vlastní API si tedy v JavaScriptu bohužel nenapíšeme. Jednoduchá data pro naši aplikaci si však můžeme uložit například do Google Sheets. Ty poskytují vlastní API, pomocí kterého můžete číst i zapisovat obsah tabulek a vytvořit si tak jednoduchý datový zdroj pro váš frontend.

K nastavení Google Sheets API následujte kroky níže.

  1. Pokud nemáte účet na Googlu, založte si jej na této stránce;
  2. Otevřete konfiguraci pro Google Sheets API. V části Step 1 klikněte na tlačítko Enable the Google Sheets API. Pokud nejste přihlášení na svůj účet, stránka vás vyzve k přihlášení. Pokud vše proběhlo v pořádku, objeví se dialog s údaji Client ID a Client Secret. Pro tuto chvíli můžeme dialog zavřít.
  3. V části Step 1 dále klikněte na tlačítko Create API Key. Objeví se dlouhatánský řetězec písmenek a čísel, pomocí kterého budete moci komunikovat s Google Sheets. Tento řetězec si uložte, abyste jej měli po ruce, až budete psát váš první program.

Čtení z tabulky

Připomeňme si naši tabulku výdajů z první lekce.

JménoVěcČástka
PetrPrací prášek240 kč
OndraSavo80 kč
PavlaToaleťák65 kč
ZuzkaMýdlo50 kč
PavlaZávěs do koupelny350 kč
LiborPivka na kolaudačku124 kč
PetrPytle na odpadky75 kč
MíšaUtěrky na nádobí130 kč
OndraToaleťák120 kč
MíšaPečící papír30 kč
ZuzkaSavo80 kč
PetrTapeta na záchod315 kč
OndraToaleťák64 kč
  1. Zamiřte na adresu http://sheets.google.com a vytvořte novou Google Sheets tabulku. Dajte jí název Výdaje a zkopírujte do ní tabulku výdajů z této stránky. Stačí vybrat všechny řádky (kromě hlavičky) zde na stránce a pak je vložit do Google tabulky pomocí Ctrl+Shift+V.
  2. Aby byla tabulka přístupná pro váš program, musíte ji nastavit jako veřejnou. V pravém horním rohu klikněte na tlačíko Sdílet, vyberte Získat odkaz a z nabídky vyberte Všichni uživatelé, kteří mají odkaz. Nyní si můžete odkaz zkopírovat do schránky a kliknout na Hotovo.
  3. V levé dolní části uvidíte název listu tabulky, který pravděpodobně zní List 1. Šipkou vedle názvu listu rozbalte menu a vyberte Přejmenovat. Jméno nastavte na List1. Mezery v názvech listů jsou nešikovné a je lepší se jich zbavit.

Nyní můžeme zkusit načíst naši tabulku z JavaScriptového programu. Nejdříve musíme znát identifikátor tabulky. Ten najdete v odkazu pro sdílení, který jste si zkopírovali do schránky. Odkaz bude vypadat nějak takto.

https://docs.google.com/spreadsheets/d/1_GQOBk3o7il-ZHam-yj1gFCZvoZdMe2kv8guk061tK8/edit?usp=sharing

Identifikátor ja pak tento řetězec.

1_GQOBk3o7il-ZHam-yj1gFCZvoZdMe2kv8guk061tK8

Dále potřebujete znát váš API klíč. Z těchto informací už můžeme sestavit adresu našeho API endpointu pro funkci fetch.

const API_KEY = 'ALzaSyBfd-ebCiXVE-iwss6S4ZBj15WN651ynQU';
const spreadsheetId = '1_GQOBk3o7il-ZHam-yj1gFCZvoZdMe2kv8guk061tK8';
const url = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/List1?key=${API_KEY}`;

fetch(url)
  .then((resp) => resp.json())
  .then((json) => console.log(json));

Pokud v tomto programu nahradíte první dvě proměnné za vaše údaje a pokud jste tabulku vytvořili správně, uvidíte v konzoli takovouto odpověď.

{
  "range": "List1!A1:Z1000",
  "majorDimension": "ROWS",
  "values": [
    ["Petr", "Prací prášek", "240 kč"],
    ["Ondra", "Savo", "80 kč"],
    ["Pavla", "Toaleťák", "65 kč"],
    ["Zuzka", "Mýdlo", "50 kč"],
    ["Pavla", "Závěs do koupelny", "350 kč"],
    ["Libor", "Pivka na kolaudačku", "124 kč"],
    ["Petr", "Pytle na odpadky", "75 kč"],
    ["Míša", "Utěrky na nádobí", "130 kč"],
    ["Ondra", "Toaleťák", "120 kč"],
    ["Míša", "Pečící papír", "30 kč"],
    ["Zuzka", "Savo", "80 kč"],
    ["Petr", "Tapeta na záchod", "315 kč"],
    ["Ondra", "Toaleťák", "64 kč"]
  ]
}

Tímto způsobem můžete snadno vytvořit zdroj dat pro vaši frontendovou aplikaci.

Zápis do tabulky

Z Google Sheets lze pomocí API data nejen číst ale i zapisovat. Zde náš však čeká zklamání. Google Sheets pro zápis do tabulek vyžadují vyšší úroveň zabezpečení. Práce s takto zabezpečenou tabulkou však znamená provést mnoho technických kroků navíc, na což v této lekci nemáme prostor.

Napsat aplikaci, která někam posílá data se nám tedy pomocí Google Sheets napsat nepodaří. K tomu bychom už potřebovali skutečné vlastní API. Pokud však naše aplikace data pouze zobrazuje, jsou Google Sheets velmi šikovná volba.

Zápis pomocí API

Google Sheets API je sice pro zapisování příliš náročné, avšak používat nějaké API k zapisování dat je běžná věc. Zatím jsme na všechna naše API posílali požadavek, aby nám server nějaká dat poslal. Těmto požadavkům se ve webové hantýrce říká GET, tedy něco jako “získat”. Pokud chce naopak něco my odeslat na server, použijeme požadavek POST.

Cvičení - zápis pomocí API

3

Chat

zapni hlavu
  1. Vytvořte si repozitář ze šablony stejně jako to děláme s úkoly. Odkaz na přijetí zadání najdete na classroom.github.com/a/dKpD9cWy.
  2. Prohlédněte si soubor index.html. Stránka je nastylovaná pomocí Bootstrapu. Nejsou potřeba žádné další CSS. Všimněte si formulářových inputů #name-input a #message-input, elementu #messages, se kterými budete později pracovat. Pro splnění zadání stačí upravovat pouze soubor index.js.
  3. Upravte soubor index.js tak, aby stránka zobrazovala nejnovější zprávy z api.
    1. Doplňte tělo funkce renderMessage. Jejím úkolem bude vracet HTML jedné zprávy podle předlohy, kterou najdete v index.html. Správné chování můžete vyzkoušet například výpisem do konzole pomocí console.log(renderMessage('Pavel', 'Ahoj 👋', '11. 5. 2020 17:30:00')).
    2. Dopište funkci renderMessages, ať pomocí for smyčky zavolá pro každou zprávu renderMessage a přidá ji do elementu s id messages. Nezapomeňte obsah #messages nejdříve vyčistit, jinak se vám zprávy budou časem opakovat.
    3. Vyzkoušejte, že volání přidává zprávy do stránky.
      renderMessages([
        { name: 'Pavel', message: 'Ahoj 👋', date: '11. 5. 2020 17:30:00' },
        { name: 'Martina', message: 'Ja se máte?', date: '11. 5. 2020 17:29:54' },
        { name: 'Michal', message: 'Nazdar', date: '12. 5. 2020 12:17:21' },
        { name: 'Ivana', message: 'Ahoj', date: '12. 5. 2020 11:02:15' },
      ]);
      
    4. Pomocí zabudované funkce fetch stáhněte uvnitř updateMessages zprávy z api. Ukázkový kód najdete v dokumentaci. Zprávy přes renderMessages(data.messages) zobrazte na stránce. Měly by se vám ukázat minimálně dvě.
  4. Upravte soubor index.js tak, aby formulář pomocí api odesílal nové zprávy na server.
    1. Doplňte funkci onSubmit, která při uložení formuláře odešle jméno a text z inputů.
    2. Pozdravte ostatní v chatu. Vyplňte na stránce políčko pro vaše jméno a zprávu textem „Ahoj“. Odešlete.

Dobrovolné úložky na doma

4

Lepší chat

zapni hlavu
  1. Vycházejte z předchozí úlohy.
  2. Vymažte políčko na zadávání textu zprávy po jeho odeslání.
  3. Zabraňte dvojímu odeslání formuláře, pokud uživatel omylem dvakrát za sebou rychle klikne na Odeslat.
  4. Přidejte do stránky CSS, které problikne žlutě všemi zprávami při každém renderu. Vyžaduje úpravu index.html.
    <style>
        @keyframes new-message {
            0% {
                background-color: #ffffd3;
            }
        }
        .card {
            animation: new-message 1s;
        }
    </style>
    
  5. Při přijímání zpráv sledujte hodnotu lastUpdate, kterou posílá server společně s messages. Volejte renderMessages jen při změně lastUpdate. Server mění tuto hodnotu pouze při přijetí nové zprávy.