Kdy co použít
- REST a JSON pro veřejné API, prohlížeč, integrace s partnery, webhooky a všude, kde se API čte nástroji bez generovaného kódu. Kontrakt popisuje OpenAPI.
- gRPC pro komunikaci vlastních služeb, kde ovládáš klienta i server, chceš generovaného klienta pro více jazyků, streamování a termíny volání (deadline).
- XML tam, kde ho určuje standard nebo protistrana: SOAP, e-faktury, bankovní zprávy ISO 20022, úřední podání. Tam ho binární protokol nenahradí.
Rozhodnutí není buď–anebo. Častá architektura má gRPC uvnitř a JSON na hranici systému, často ze stejného souboru .proto (viz JSON jako druhá vrstva).
Kontrakt: soubor .proto
Zprávy a služby se popisují v jazyce Protocol Buffers. Každé pole má typ a číslo;
binární zpráva obsahuje čísla polí, ne jejich jména. Z jednoho souboru generuje protoc
nebo buf třídy a klienta pro Go, Javu, C#, Python, TypeScript a další jazyky.
syntax = "proto3";
package eshop.v1;
import "google/protobuf/timestamp.proto";
message Polozka {
string kod = 1;
int32 pocet = 2;
int64 cena_haleru = 3;
}
message Objednavka {
string cislo = 1;
repeated Polozka polozky = 2;
google.protobuf.Timestamp vytvoreno = 3;
bool expres = 4;
}
message DetailRequest {
string cislo = 1;
}
service Objednavky {
rpc Detail(DetailRequest) returns (Objednavka); // jeden požadavek, jedna odpověď
rpc Sledovat(DetailRequest) returns (stream Objednavka); // server posílá změny průběžně
}
gRPC zná čtyři druhy metod: jeden požadavek a jedna odpověď, stream od serveru, stream od klienta
a obousměrný stream. Klient může volání nastavit termín a server po jeho vypršení vrátí
DEADLINE_EXCEEDED. Místo syntax = "proto3" nové soubory mohou používat
edice (edition = "2024"), které proto2 a proto3 nahrazují; binární formát
zůstává stejný.
Stejná zpráva jako JSON a jako Protobuf
import { create, createFileRegistry, fromBinary, toBinary, toJson } from "@bufbuild/protobuf";
import { FileDescriptorSetSchema, timestampFromDate } from "@bufbuild/protobuf/wkt";
import { readFileSync } from "node:fs";
// set.binpb vytvořil příkaz: npx buf build -o set.binpb
const registry = createFileRegistry(fromBinary(FileDescriptorSetSchema, readFileSync("set.binpb")));
const Objednavka = registry.getMessage("eshop.v1.Objednavka");
const zprava = create(Objednavka, {
cislo: "2026-117",
polozky: [
{ kod: "HR-01", pocet: 2, cenaHaleru: 14990n },
{ kod: "TA-07", pocet: 1, cenaHaleru: 21900n },
],
vytvoreno: timestampFromDate(new Date("2026-10-04T10:00:00Z")),
expres: false,
});
const bin = toBinary(Objednavka, zprava);
const json = JSON.stringify(toJson(Objednavka, zprava)); ProtoJSON: {"cislo":"2026-117","polozky":[{"kod":"HR-01","pocet":2,"cenaHaleru":"14990"},
{"kod":"TA-07","pocet":1,"cenaHaleru":"21900"}],"vytvoreno":"2026-10-04T10:00:00Z"}
JSON bajtů: 161 Protobuf bajtů: 47
hex: 0a 08 32 30 32 36 2d 31 31 37 12 0c 0a 05 48 52 2d 30 31 10 02 18 8e 75 12 0d
0a 05 54 41 2d 30 37 10 01 18 8c ab 01 1a 06 08 a0 c7 88 d6 06
Binární zpráva má 47 bajtů, JSON 161. Úspora vzniká hlavně tím, že se nepřenáší názvy klíčů
a čísla jsou zakódovaná jako varint (8e 75 je 14990). Po kompresi gzip se rozdíl
u větších odpovědí zmenšuje; porovnávej na svých datech, ne na jedné zprávě. Hex výpis bez
souboru .proto nic neřekne: 0a 08 znamená „pole 1, řetězec délky 8“, jméno
cislo v něm není.
Při ladění proto vývojáři čtou zprávy jako JSON. Nástroje typu grpcurl nebo buf curl
si schéma vezmou z reflexe serveru nebo ze souboru .proto a výsledek vypíšou jako ProtoJSON.
ProtoJSON: kanonický JSON pro Protobuf
Protobuf definuje vlastní mapování na JSON, nazvané ProtoJSON. Je to obyčejný JSON text, ale s pravidly, která se liší od ručně psaného API:
- Názvy polí se převádí na lowerCamelCase (
cena_haleru→cenaHaleru). Parser přijme obě podoby. int64auint64se zapisují jako řetězce. Důvod je stejný jako u čísel v JSON: JavaScript a další parsery čtou čísla jako double a nad 253 ztrácejí přesnost.bytesjsou řetězec v Base64,Timestampřetězec RFC 3339 v UTC, enum jméno hodnoty.- Pole s výchozí hodnotou se nezapisují, proto v ukázce chybí
"expres": false.
| Past | Co se děje |
|---|---|
| int64 jako řetězec | ProtoJSON zapíše int64 a uint64 jako "14990". Klient, který čeká číslo, selže nebo porovnává řetězce. |
| Chybějící výchozí hodnoty | Pole s výchozí hodnotou (false, 0, "") bez sledování přítomnosti se nezapíše; expres v ukázce chybí. Rozlišit „nezadáno“ pomůže optional. |
| Neznámá pole | Binární parser je zachová a předá dál, JSON parser je podle specifikace odmítne, pokud ho výslovně nenastavíš jinak. |
| Přejmenování pole | Binárně je bezpečné, rozhoduje číslo pole. JSON klíč se ale změní (cenaHaleru) a JSON klienti přestanou fungovat. |
| Znovupoužité číslo pole | Starý klient přečte nová data jako starý význam. Smazaná čísla a jména označ reserved. |
Srovnání
| REST + JSON | gRPC + Protobuf | |
|---|---|---|
| Přenos | HTTP/1.1, HTTP/2 i HTTP/3 | HTTP/2; v prohlížeči jen přes gRPC-Web nebo transcoding |
| Kontrakt | volitelný (OpenAPI, JSON Schema) | povinný soubor .proto, z něj se generuje kód |
| Data | text, čitelný bez nástrojů | binární, ke čtení potřebuješ .proto |
| Model volání | zdroje a metody HTTP (GET, POST…) | volání metod služby, 4 druhy včetně obousměrného streamu |
| Chyby | stavový kód HTTP a tělo odpovědi | HTTP 200 a grpc-status v trailerech |
| Cache a proxy | běžná HTTP cache, CDN, logy | každé volání je POST, HTTP cache se nepoužije; proxy musí umět HTTP/2 a trailery |
| Typy | jen to, co má JSON (číslo, řetězec…) | int32/int64, bytes, enum, Timestamp… |
| Vývoj schématu | dohodou a verzí API | čísla polí; přejmenování je binárně kompatibilní |
gRPC vrací HTTP 200 i při chybě aplikace a skutečný výsledek posílá v trailerech
grpc-status a grpc-message. Monitoring, který sleduje jen stavové
kódy HTTP, proto chyby gRPC nevidí. Každá zpráva má navíc pětibajtovou hlavičku (příznak
komprese a délka), takže tělo odpovědi není samotná zpráva Protobuf.
JSON jako druhá vrstva nad gRPC
Prohlížeč neumí přímo pracovat s rámci HTTP/2 ani s trailery, takže klasického gRPC klienta nespustí. Existují dvě cesty:
- gRPC-Web je upravený protokol (
application/grpc-web), který prohlížeč posílá přes fetch. Mezi prohlížečem a službou obvykle stojí proxy, která ho překládá. Klient potřebuje generovaný kód a obousměrný stream nepodporuje. - JSON transcoding zpřístupní metody gRPC jako REST s JSON podle anotace
google.api.http. Umí to grpc-gateway (samostatný proxy server), Envoy nebo ASP.NET Core přímo v aplikaci. Klient nepotřebuje nic o gRPC vědět.
service Objednavky {
rpc Detail(DetailRequest) returns (Objednavka) {
option (google.api.http) = { get: "/v1/objednavky/{cislo}" };
}
}
// stejná metoda je pak dostupná i jako
// GET /v1/objednavky/2026-117 → 200, application/json (ProtoJSON)
JSON odpověď z transcodingu se řídí pravidly ProtoJSON, včetně řetězců u int64
a vynechaných výchozích hodnot; konkrétní implementace to umí nastavit. Pokud ji publikuješ jako veřejné API, dokumentuj tato pravidla. Hodí se
i OpenAPI popis vygenerovaný ze stejného souboru .proto.
Časté chyby
- gRPC kvůli výkonu bez měření. U API, kde rozhoduje databáze nebo síťová latence, velikost zprávy často nehraje roli. Změř to.
- Veřejné API jen v gRPC. Partneři potřebují generátor, proxy pro HTTP/2 a znalost .proto. Pro integrace bývá jednodušší REST.
- Ruční JSON místo ProtoJSON. Pokud JSON vyrábíš z protobuf zpráv, použij serializátor knihovny. Ruční převod se rozejde v int64, výchozích hodnotách a názvech polí.
- Změna čísla nebo typu pole. Zpětnou kompatibilitu kontroluje
buf breaking; spouštěj ho v CI proti poslední vydané verzi.
Zdroje
gRPC: Core concepts, gRPC over HTTP2, gRPC-Web protocol, ProtoJSON Format, Protobuf Editions a gRPC JSON transcoding v ASP.NET Core. Další binární alternativy k JSON (CBOR, MessagePack, BSON) popisuje kapitola binární formáty.