API je rozhraní, přes které tvůj backend nabízí svoje funkce okolnímu světu, frontendu, mobilní aplikaci, jiné službě. Je to smlouva: „když mi pošleš tenhle požadavek, udělám tohle a vrátím ti tamto." A jakmile tu smlouvu někdo začne používat, nemůžeš ji jen tak změnit, aniž bys mu rozbil aplikaci. Dobrý návrh API je hlavně o ohleduplnosti k tomu, kdo ho bude volat.
Pár pojmů na úvod
- API (Application Programming Interface) = sada „vstupních bodů" tvého backendu, které může někdo zavolat. Ne pro člověka jako web, ale pro jiný program.
- Endpoint = jeden konkrétní vstupní bod = kombinace cesty a metody, např.
GET /coursesneboPOST /orders. - Zdroj (resource) = věc, se kterou API pracuje (kurz, objednávka, uživatel).
- Payload / tělo (body) = data, která v requestu posíláš nebo v response dostáváš (obvykle JSON).
- Klient (konzument API) = ten, kdo tvoje API volá. Může to být tvůj vlastní frontend i cizí appka.
Vycházíme z Foundations. HTTP metody a status kódy už znáš, teď je použijeme k návrhu rozumného API.
Styly API, tři přístupy
Existují tři hlavní způsoby, jak API postavit. Nejsou to soupeři, každý se hodí jinam:
| Styl | Jak se na to dívat | Kdy ho zvolit |
|---|---|---|
| REST | Pracuješ se zdroji přes HTTP metody | Default. Veřejná API, běžné CRUD |
| gRPC | Voláš funkci na vzdálené službě | Komunikace mezi tvými vlastními službami, kde jde o rychlost |
| GraphQL | Klient si řekne přesně o data, která chce | Když klient (často mobil) potřebuje pokaždé něco jiného |
Jako junior začni s REST, je nejrozšířenější a všechno ostatní pochopíš snáz, až ho budeš mít v ruce. gRPC a GraphQL si necháš, až narazíš na konkrétní potřebu.
REST pořádně
Hlavní myšlenka REST: zdroj je podstatné jméno, akci určuje HTTP metoda. Do URL tedy nepatří
slovesa, co s daným zdrojem děláš, říká už metoda (GET, POST…).
✅ GET /courses vrať seznam kurzů
✅ GET /courses/42 vrať detail kurzu 42
✅ POST /courses vytvoř kurz
✅ PUT /courses/42 nahraď celý kurz 42
✅ PATCH /courses/42 uprav část kurzu 42
✅ DELETE /courses/42 smaž kurz 42
✅ GET /courses/42/lessons lekce patřící ke kurzu 42
❌ GET /getCourses (sloveso v URL: to dělá GET)
❌ POST /createCourse (sloveso v URL: to dělá POST)
❌ POST /courses/42/delete (na mazání je DELETE)
Připomenutí sémantiky metod (z Foundations): GET jen čte (nic nemění, jde cachovat), PUT a DELETE jsou idempotentní (zopakování nic nezkazí), POST idempotentní není a právě proto existují idempotency keys ↓.
Idempotency keys, jak bezpečně opakovat POST
Připomeň si pojem idempotentní z Foundations: operace, kterou můžeš provést
vícekrát se stejným výsledkem. Problém je, že POST (vytvoř něco) idempotentní není.
Představ situaci: klient pošle POST /payments, ale spojení vyprší (timeout) dřív, než dorazí
odpověď. Klient neví, jestli platba proběhla. Co udělá? Zkusí to znovu (retry) a když ta první
ve skutečnosti prošla, právě jsi zákazníkovi naúčtoval dvakrát.
Řešení: klient ke každému takovému requestu přiloží unikátní klíč (Idempotency-Key). Server si
podle něj pozná, že už ten požadavek viděl:
Server si páruje klíč → výsledek (třeba v Redisu nebo databázi, na omezenou dobu). Podrobně, včetně záludností, v kapitole Patterny → Idempotency. Tohle je naprostý základ, který by měl umět každý backend engineer.
Verzování, jak měnit API a nerozbít staré klienty
Tvoje API se bude vyvíjet, ale aplikace, které ho už používají, nesmí ze dne na den přestat fungovat.
- Přes URL:
/v1/courses,/v2/courses, nejjasnější a nejčastější způsob. - Přes hlavičku: verzi pošleš v hlavičce requestu, čistší URL, ale hůř vidět.
- Pravidlo: přidat nové (volitelné) pole je neškodné. Odebrat nebo přejmenovat pole, nebo změnit jeho typ, je breaking change (rozbije klienty) → dělej to v nové verzi.
⚠️ Nová major verze není zadarmo, znamená udržovat dvě implementace souběžně. Proto se s ní šetří: většina změn jde udělat zpětně kompatibilně (aditivně), bez nové verze. A když starou verzi rušíš, udělej to řízeně: ohlas to dopředu (hlavičky
Deprecation/Sunset) a dej klientům čas přejít. Neverzuj při každé změně.
Pagination, nikdy nevracej „všechno"
Kdyby GET /orders vrátilo všech 5 milionů objednávek, zabiješ tím databázi i klienta. Proto se
výsledky vrací po stránkách (pagination). Dva způsoby:
| Typ | Jak se volá | Výhody / nevýhody |
|---|---|---|
| Offset | ?limit=20&offset=40 (přeskoč 40, dej 20) | Jednoduchý, umí skočit na stránku N / pomalý na velkých datech a nestabilní (když mezitím někdo přidá řádek, stránky se posunou) |
| Cursor | ?limit=20&cursor=abc (dej 20 za bodem abc) | Stabilní a rychlý i na velkých datech / neumí skočit rovnou na stránku N |
Pro nekonečný scroll a velké tabulky používej cursor. Offset si nech na malé, neměnné seznamy.
Error contract, jednotný tvar chyb
Klient musí umět chybu zpracovat strojově, ne jen přečíst. Proto vracej chyby pořád ve stejném tvaru napříč celým API:
{
"success": false,
"error": {
"code": "VALIDATION_FAILED",
"message": "Vstup neprošel validací",
"details": [{ "field": "email", "issue": "invalid_format" }]
}
}
codeje stabilní řetězec pro stroj (kód na něj může reagovat),messageje text pro člověka. Klientovi nikdy neposílej syrový technický výpis chyby (stack trace), je to únik informací a bezpečnostní riziko.- Vracej k tomu správný HTTP status (4xx = chyba klienta, 5xx = chyba serveru).
- Pole
detailspatří k validačním chybám (422, které konkrétní pole je špatně). Business konflikt (409, např. „kurz je plný") nemá konkrétní vadné pole, vrať jencodeamessage(třebacode: "COURSE_FULL"), žádnédetailsnecpi.
Další věci, co dělají API dobrým
- Konzistentní pojmenování: vyber si
snake_casenebocamelCasea drž ho všude stejně. - Filtrování a řazení přes query parametry:
?status=active&sort=-createdAt. - Rate limiting: omez počet requestů na klienta, ať ti někdo API nezahltí; přes kód
429(Security). - Dokumentace: popiš API standardem OpenAPI/Swagger, ideálně generovaným z kódu, ať nezestárne.
- Bezpečnost: každou operaci, co mění data, ověř (kdo a jestli smí), vstup validuj na serveru a nikdy nevěř tomu, co přijde od klienta (viz Security).
Bulk operace, dlouhé operace a API gateway
Tři situace, na které REST „základ" nestačí a je dobré vědět, jak na ně:
- Bulk operace (hromadné). Potřebuješ vytvořit/upravit 100 položek? Posílat 100 requestů je pomalé. Nabídni endpoint, který vezme pole položek najednou. Pak ale řeš částečný úspěch, co když projde 98 a 2 selžou? Vrať výsledek pro každou položku zvlášť, ne jen „OK / chyba".
- Dlouhé operace (async API). Když operace trvá dlouho (vygenerovat velký report), nenech klienta
čekat na jednu odpověď. Vrať hned
202 Accepteds odkazem „stav úlohy je tady", úlohu zpracuj na pozadí a klient si stav buď polluje (občas se zeptá), nebo mu po dokončení pošleš webhook. - API gateway. Když máš víc služeb, postavíš před ně jednu bránu, která řeší společné věci na jednom místě: ověření, rate limiting, směrování requestů na správnou službu. Klient mluví s bránou, ne přímo se službami. (Varianta BFF = Backend For Frontend: brána ušitá na míru konkrétnímu klientovi, třeba mobilu.)
Failure modes, kde se to nejčastěji pokazí
- Chybějící idempotency → duplicitní platby/objednávky, když klient po timeoutu zopakuje request.
- Breaking change bez nové verze → rozbiješ produkční klienty bez varování.
- Offset pagination na milionech řádků → pomalé a přeskakující/duplikující záznamy.
- Únik interních detailů v chybách → posíláš klientovi stack trace nebo SQL = bezpečnostní díra.
🛠️ Cvičení
- Navrhni REST API. Pro „kurzy a jejich lekce" napiš endpointy: výpis a detail kurzu, vytvoření a úprava kurzu, výpis lekcí kurzu a zápis studenta. Bez sloves v URL, se správnými metodami a status kódy.
- Idempotentní POST. Naskicuj v pseudokódu handler
POST /paymentssIdempotency-Key: kde klíč ukládáš, jak poznáš opakovaný request a jak vrátíš uloženou odpověď. - Cursor vs offset. Pro nekonečný scroll feedu napiš, jaké query parametry použiješ, a vysvětli, proč offset na milionu řádků začne přeskakovat nebo duplikovat záznamy.
- Error contract. Navrhni jednotný tvar chyby a přiřaď k němu 4 situace i s HTTP kódy: neprošla validace, nepřihlášen, nemá právo, konflikt (např. plný kurz).
- Breaking change? U každé změny rozhodni, jestli je breaking (a tedy potřebuje novou verzi): přidání volitelného pole, přejmenování pole, zúžení typu, nový povinný parametr, změna formátu data.
Náčrt řešení: rozbal, až si cvičení zkusíš sám
- Navrhni REST API,
GET /courses,GET /courses/42,POST /courses(201),PUT/PATCH /courses/42,GET /courses/42/lessons, zápis studenta jakoPOST /courses/42/enrollments. Pointa: žádná slovesa v URL (zdroj = podstatné jméno, akci určuje metoda); past je dělatPOST /courses/42/enrollmísto zápisu nového zdroje. - Idempotentní POST, uložíš dvojici
klíč → výsledek(Redis/DB), na začátku ověříš, jestli klíč už existuje: pokud ano, vrátíš uloženou odpověď a neprovedeš znovu. Na co pozor: klíč musíš uložit atomicky se samotnou operací, jinak vznikne okno, kdy platba projde, ale klíč ne → duplicita. A pozor i na souběžný retry (klient pošle podruhé, než první doběhne): naivní „zkontroluj, pak založ" má sám race condition, proto dej na klíčUNIQUEa zakládej ho atomickým insertem, ať druhý request narazí (celý vzor v Patterny). - Cursor vs offset, pro feed
?limit=20&cursor=abc(ukazatel na poslední položku). Pointa: offset na milionu řádků přeskakuje/duplikuje, protože když mezi requesty někdo přidá řádek, celé okno se posune; cursor se váže na konkrétní bod, takže je stabilní (jen neumí skočit na stránku N). - Error contract, jednotný tvar
{ success, error: { code, message, details? } }: neprošlá validace422(+detailspo polích), nepřihlášen401, nemá právo403, plný kurz409(jencode, např.COURSE_FULL, bezdetails). Pozor:detailspatří jen k validaci; do business konfliktu je necpi a klientovi nikdy neposílej stack trace. - Breaking change?, breaking jsou: přejmenování pole, zúžení typu, nový povinný parametr a změna formátu data (rozbijí klienty → nová verze); přidání volitelného pole je bezpečné. Pointa: aditivní změny dělej zpětně kompatibilně, novou major verzi si nech jen na ty breaking.
🧠 Otázky & odpovědi
Proč POST není idempotentní a jak to vyřešíš pro platby?
Zopakování POSTu vytvoří nový záznam nebo akci, retry po timeoutu tak může znamenat dvě platby. Řešení je idempotency key: klient pošle s requestem unikátní klíč a server si páruje klíč → výsledek. Když dorazí stejný klíč podruhé, operaci neprovede znovu, jen vrátí uloženou odpověď. Klíč se musí uložit atomicky se samotnou operací, jinak vznikne okno, kdy operace proběhne, ale klíč ne → duplicita.
Kdy použít cursor a kdy offset pagination?
Offset (limit/offset) je fajn na malé, neměnné seznamy a když chceš umět skočit na konkrétní
stránku, ale na velkých datech je pomalý a nestabilní (když mezi requesty někdo přidá řádek, okno se
posune a dostaneš duplikáty nebo přeskočíš). Cursor (ukazatel na poslední položku) je stabilní a
rychlý i na velkých a měnících se datech a hodí se na nekonečný scroll; neumí jen skočit na stránku N.
Co je breaking change a jak se mu bránit?
Změna, která rozbije existující klienty: odebrání nebo přejmenování pole, zúžení typu, nový povinný
parametr, změna významu. Přidat volitelné pole je naopak bezpečné. Breaking změnu zaveď v nové
verzi (/v2) a starou nech běžet, dokud klienti nepřejdou. Zásada: buď tolerantní k tomu, co
přijímáš, a opatrný v tom, co měníš ve výstupu.
Jak má vypadat dobrá chyba z API?
Vždy stejný tvar napříč API: strojově čitelný stabilní code (např. INSUFFICIENT_STOCK),
lidsky čitelný message a volitelně details pro chyby po jednotlivých polích. K tomu správný HTTP
status (4xx chyba klienta, 5xx chyba serveru). Klientovi nikdy neposílej stack trace ani SQL, je to
únik informací, který pomáhá útočníkovi.
Proč začít s REST a kdy teprve gRPC nebo GraphQL?
REST je univerzální, snadno se cachuje a rozumí mu každý → ideální default i pro učení. gRPC (rychlý, typovaný) se hodí na komunikaci mezi tvými vlastními službami, kde jde o nízkou latenci. GraphQL zvol, když klient (typicky mobil) potřebuje pokaždé jinou sadu dat a chceš se vyhnout posílání zbytečností, za cenu složitějšího serveru a obtížnějšího cachování.
Jak navrhnout API pro operaci, která trvá dlouho?
K čemu je API gateway?
Je to jedna brána před tvými službami, která na jednom místě řeší společné věci: ověření, rate limiting a směrování requestu na správnou službu. Klient mluví s bránou, ne přímo se službami. Varianta BFF (Backend For Frontend) je brána ušitá na míru konkrétnímu klientovi (např. mobilu), aby dostával přesně ta data v takovém tvaru, jaký potřebuje.
