gRPC, OpenAPI, REST verstehen und wissen, wann man sie im API-Design einsetzt (2020)
(cloud.google.com)- Ausgangspunkt des API-Designs ist der Unterschied zwischen RPC und REST. Alle drei Ansätze können mit HTTP verbunden sein, unterscheiden sich aber im Modell dafür, wie Aufrufe erstellt und Clients aufgebaut werden.
- REST ist ein Hypertext-Modell, bei dem Clients den vom Server bereitgestellten URLs folgen. Es unterscheidet sich damit von OpenAPI-artigen APIs, bei denen Clients URL-Formate zusammensetzen.
- OpenAPI definiert Operationen über URL-Pfad-Templates und HTTP-Methoden. Das ist praktisch und weit verbreitet, ähnelt aber eher einem auf HTTP gemappten RPC-Modell als REST.
- gRPC definiert RPC-APIs direkt über IDL, Codegenerierung, binäre Payloads und HTTP/2-Verbindungsmanagement und verbirgt HTTP-Details.
- gRPC erfordert auf beiden Seiten spezielle Software und hat Einschränkungen bei Proxy-Erweiterungen, beim Schutz vor gleichzeitigen Updates und bei Teilaktualisierungen. Es eignet sich daher besonders für interne APIs oder wenn eine Übersetzungsschicht wie Cloud Endpoints genutzt werden kann.
Drei Modelle für HTTP-basierte APIs
- Die wichtigsten Ansätze, HTTP als API-Transportschicht zu nutzen, lassen sich in REST, OpenAPI und gRPC einteilen.
- Einer der Gründe, warum viele öffentliche APIs und private verteilte APIs HTTP verwenden, ist, dass Organisationen mit Sicherheitsfragen rund um HTTP-Traffic auf den Ports 80 und 443 vertraut sind.
- Alle drei Ansätze können mit HTTP verbunden sein, unterscheiden sich aber darin, was adressiert wird und wie der Client Aufrufe erstellt.
REST: URLs nicht zusammensetzen, sondern ihnen folgen
- Bei REST verwendet der Client die vom Server übergebene URL unverändert und versteht oder kombiniert das URL-Format nicht als Teil der API-Spezifikation.
- Ein Browser folgt der aktuellen Seite, Lesezeichen oder einer vom Benutzer eingegebenen URL und tut höchstens so viel, wie HTTP-Request-Informationen aus der URL zu extrahieren oder relative URLs in absolute URLs umzuwandeln.
- Kern einer REST-API ist die Nutzung von Hypertext/Hypermedia, bei der Verweise zwischen Ressourcen als URLs anderer Ressourcen ausgedrückt werden.
- Im REST-Ansatz werden alle Identifikatoren als URLs ausgetauscht.
POST /accounts→account_URLPOST /subscriptionsmitaccount_URL→subscription_URLGET {account_URL}→ gibt den Datenbaum des Kontos zurück
- Die Vorteile von REST liegen nahe an Stabilität, Konsistenz und Universalität des Webs selbst; das entitätsorientierte Modell von HTTP/REST kann APIs einfacher und regelmäßiger machen.
OpenAPI: näher an direkt auf HTTP gemapptem RPC
- OpenAPI definiert unter
pathsURL-Pfad-Templates und bezeichnet die Kombination aus Pfad und HTTP-Methode als Operation. - Ein Pfad wie
/pets/{petId}verlangt vom Client, den Wert für{petId}zu kennen und in das URL-Template einzusetzen, um einen HTTP-Request zu erstellen. - In diesem Ansatz muss der Client das URL-Format im Detail kennen; das steht im Gegensatz zum Hypertext-Modell von REST.
- Warum OpenAPI so verbreitet ist, ist klar:
- Es ähnelt dem traditionellen RPC-Modell und ist Programmierern vertraut.
- RPC-Konzepte lassen sich direkt auf HTTP-Requests abbilden.
- Bei öffentlichen APIs ist der Vorteil groß, dass sie mit Standard-HTTP-Techniken aus nahezu jeder Sprache und Umgebung zugänglich sind.
- Dafür müssen URL-Pfade, HTTP-Methoden und Parameter-Mapping entworfen werden, wodurch sowohl API-Anbieter als auch Konsumenten mehr Details lernen müssen.
gRPC: RPC-Implementierungstechnik, die HTTP/2 verbirgt
- gRPC nutzt HTTP/2 als Transportschicht, legt API-Designern sowie Client- und Servercode aber keine HTTP-Details offen.
- Der Aufrufablauf eines gRPC-Clients ist einfach:
- Die aufzurufende Prozedur bestimmen
- Die zu verwendenden Parameterwerte berechnen
- Die Parameter an den codegenerierten Stub übergeben und den Aufruf ausführen
- gRPC definiert Remote Procedures auf Basis einer interface description language, sodass URL-Pfade, Parameter und HTTP-Methoden-Mappings nicht wie bei OpenAPI gemeinsam ausgedrückt werden müssen.
- Codegenerierung, Frameworks und Bibliotheken können es erleichtern, Client-Bibliotheken und Serverimplementierungen zu erstellen.
- Durch binäre Payloads und HTTP/2-Verbindungsmanagement bietet gRPC Performance-Vorteile. Dieselben Techniken kann man auch ohne gRPC direkt nutzen, dafür ist jedoch mehr technisches Wissen erforderlich.
Wann gRPC statt OpenAPI in Betracht kommt
- Beim Entwurf einer API mit OpenAPI müssen Operationen und Parameter als Kombination aus URL-Pfad und HTTP-Methode ausgedrückt werden; die vielen Optionen können anspruchsvoll sein.
- Wenn man weiterhin ein RPC-artiges Modell verwendet, reduziert gRPC den Aufwand, ein benutzerdefiniertes Mapping auf HTTP selbst zu entwerfen.
- gRPC und OpenAPI haben ein ähnliches grundlegendes API-Modell, unterscheiden sich aber darin, wie HTTP offengelegt wird.
- OpenAPI legt dem Client Details des HTTP-Transports offen, und der Designer kontrolliert das Mapping.
- gRPC verbirgt HTTP-Details durch vordefinierte Mappings und generierten Code.
- Der große Vorteil von OpenAPI ist, dass Clients Standard-HTTP-Tools und -Techniken unverändert verwenden können. Für viele API-Designer rechtfertigt dieser Vorteil den zusätzlichen Designaufwand.
Ein entitätsorientiertes Modell mit RPC kombinieren
- Auch bei der Nutzung von gRPC oder OpenAPI kann man einige Vorteile von REST gewinnen, wenn man RPC-Methoden auf Entitäten ausrichtet und beschränkt.
- Der Ansatz beginnt nicht mit der Definition von Prozeduren, sondern definiert zuerst Ressourcentypen und ordnet jedem Typ standardisierte Entitätsoperationen zu.
- Die Basisoperationen sind Create, Retrieve, Update, Delete und List; man kann sie häufig als CRUD plus List betrachten.
- Bei Bedarf können zusätzliche Operationen vorgesehen werden, aber wenn entitätsorientierte und prozedurorientierte Konzepte vermischt werden, können einige Vorteile abgeschwächt werden.
- Prozeduren nach Entitätstypen zu gruppieren, ist auch eine der zentralen Ideen objektorientierter Sprachen.
Einschränkungen und Vorsichtsmaßnahmen bei gRPC
- gRPC benötigt sowohl auf Client- als auch auf Serverseite spezielle Software, und generierter Code muss in die Build-Prozesse beider Seiten integriert werden.
- Für Nutzer dynamischer Sprachen wie JavaScript oder Python, bei denen es in der Entwicklungsumgebung möglicherweise kaum einen Build-Prozess gibt, kann diese Anforderung belastend sein.
- Google Cloud Endpoints macht gRPC-APIs über HTTP und JSON zugänglich und stellt damit Client-Optionen wieder her; allerdings kann nicht jeder dies nutzen oder eine gleichwertige Funktionalität aufbauen.
- Bei REST-APIs lässt sich auch ohne Metadaten leicht ein Bot erstellen, der die gesamte API crawlt. RPC-artige APIs wie gRPC oder OpenAPI benötigen dagegen für jeden Entitätstyp unterschiedliche APIs sowie Metadaten oder maßgeschneiderte Software.
- HTTP-APIs werden häufig über API-Management-Tools und Proxys wie Apigee Edge um Sicherheitsfunktionen, Eingabevalidierung, Datenformat-Mapping sowie Änderungen an Headern und Body erweitert. Bei gRPC können solche Proxy-Erweiterungen deutlich schwieriger sein.
- gRPC definiert keinen Standardmechanismus, um Datenverluste durch gleichzeitige Updates zu verhindern.
- HTTP bietet dafür die Header
EtagundIf-Match.
- HTTP bietet dafür die Header
- gRPC definiert auch keinen Mechanismus für Teilaktualisierungen.
- In HTTP gibt es
PATCH; als Standards für JSON existieren JSON merge patch und JSON patch. - JSON merge patch ist einfacher, kann aber nicht alle Fälle wie Array-Updates behandeln.
- JSON patch kann mehr Fälle abdecken, ist aber komplexer in der Nutzung.
- In HTTP gibt es
Auswahlkriterien
- Wenn man das REST-Hypertext-Modell bereits entwerfen kann oder bereit ist, es zu lernen, kann es eine gute Wahl sein, wenn Stabilität, Konsistenz und Universalität das Ziel sind.
- Mit OpenAPI lassen sich APIs bauen, die allein mit Standard-HTTP-Techniken zugänglich sind; allerdings nehmen die Designoptionen für das Mapping von RPC-Konzepten auf HTTP zu, was Design, Implementierung und Lernen erschweren kann.
- Wenn eine API für OpenAPI in Betracht kommt, lohnt es sich, auch gRPC zu prüfen. Die grundlegenden API-Modelle beider Ansätze sind vergleichbar, und gRPC reduziert die Notwendigkeit, HTTP-Mappings selbst zu erstellen.
- gRPC ist unter folgenden Bedingungen besonders attraktiv:
- Wenn Clients dank Produkten wie Cloud Endpoints nicht zwingend gRPC-Technologie übernehmen müssen
- Wenn es sich um eine interne API handelt, bei der man die Technologieentscheidungen des Servers und aller Clients kontrollieren kann
- Bei der Entscheidung für gRPC statt OpenAPI oder REST sollte man berücksichtigen, dass die Möglichkeiten, API-Verhalten über API-Management-Tool-basierte Proxys wie Apigee Edge zu erweitern oder zu korrigieren, deutlich eingeschränkter sein können.
1 Kommentare
Meinungen auf Hacker News
Wenn ich die Zeit zurückdrehen könnte, würde ich mich am liebsten von Anfang an daran hindern, gRPC zu lernen.
Anfangs war ich von der Vision begeistert, aber nach ein paar Jahren stellte sich heraus, dass es viel zu viele Probleme bereitet. Auch die Behauptung, es verberge die Interna, ist eher ein Witz: Um die Ursache für Requests zu finden, die in 1 von 10 Fällen fehlschlagen, kippt man Debug-Logs aus und dreht an 10 bis 20 vage benannten Timeout-/Retry-Einstellungen.
Maven-Plugins, seltsame
deadline exceeded-Fehler, Load Balancer, die HTTP/2 nicht mögen, Situationen, in denen man wegen Firewalls am Ende doch Standard-APIs verwenden muss, dürftige Dokumentation und der Versuch, brauchbare Fehlermeldungen für Observability zu bekommen – all das frisst Zeit.Die Developer Experience des generierten Codes ist schlecht, Client-Stubs sind konkrete
final-Klassen und daher in Tests schwer zu mocken, und auch Server-Implementierungen müssen von konkreten Klassen erben statt Interfaces zu implementieren.Server-Methoden haben asynchrone Signaturen, wodurch AOP-basierte Mechanismen wie
@Transactionalkaputtgehen; Exception-Unterstützung gibt es ebenfalls nicht. Immutable Value Classes sind zwar gut, müssen aber alle über Builder erzeugt werden.Wenn man gRPC in einer SOA einsetzen will, muss man am Ende viel Boilerplate schreiben, um den gRPC-Lärm zu verstecken und testbaren, sauberen Code zu bekommen. Der RPC-Compiler von Thrift hat ähnliche Probleme plus noch weitere.
Mit aktuellem .NET und C# ist die gRPC-Erfahrung ziemlich gut; Microsoft hat deshalb auch bestehende RPC-Technologien wie WCF eingestellt und sich auf gRPC konzentriert.
protocgenerierten Binding-Ausgaben zu validieren, war umständlicher und fehleranfälliger als direkte Serialisierung.Das Wire-Protokoll ist nicht typsicher; es gibt zwar Type-Tags, aber dieselben Tags werden für mehrere Datentypen wiederverwendet. Auch das Zig-Zag-Integer-Encoding ist langsam.
Als RPC-Bibliothek ist es miserabel; das Einzige, das ich selbst erlebt habe und das noch schlechter war, waren FlatBuffers.
Ich frage mich, ob dieser Unterschied an Java liegt oder an der gRPC-Technologie selbst.
Ich frage mich, ob das ein Java+gRPC-spezifisches Problem ist.
Ich entwickle seit Langem APIs, habe sowohl gRPC als auch HTTP/REST verwendet und außerdem die Bibliothek https://github.com/oapi-codegen/oapi-codegen veröffentlicht, die aus OpenAPI-Spezifikationen Go-Clients und -Server generiert.
Der Art, wie der Artikel OpenAPI und REST unterscheidet, kann ich nur schwer zustimmen. OpenAPI ist eine Methode, das Verhalten einer HTTP-API zu dokumentieren, und kann sowohl RESTful APIs als auch völlig beliebige APIs beschreiben. In dem Sinne, dass es eine von Tools interpretierbare Schemasprache zur Beschreibung einer API ist, ähnelt es konzeptionell den Protocol-Buffer-Dateien, die das gRPC-Protokoll festlegen.
gRPC ist ein RPC-Mechanismus zum Austausch von Protos. Als Google protobuf veröffentlichte, wurde die interne RPC-Schicht Stubby nicht freigegeben, und gRPC ist nicht so gut wie Stubby. Trotzdem ist es bei der Übertragung effizient und relativ leicht zu erweitern.
Allerdings hat gRPC kein so robustes Ökosystem wie die gängigen HTTP-Bibliotheken, sodass man Middleware wie Logging oder Authentifizierung oft selbst implementieren muss; besonders gilt das bei RPC zwischen Services, die in unterschiedlichen Sprachen implementiert sind.
Das eigentliche Problem bei gRPC sehe ich jedoch in den Proto-Dateien. Alle Clients müssen mit einer zum Server kompatiblen
.proto-Datei gebaut werden, daher ist es kein discoverable Protokoll. Eine HTTP-API kann man auch ohne OpenAPI-Beschreibung mitcurloder selbst geschriebenem Code aufrufen; die Kopplung ist lockerer, und deshalb sind Arbeit und Debugging einfacher.REST, wie es in Roy Fieldings Dissertation aus dem Jahr 2000 definiert wurde, bedeutete, dass ein
GETauf die Root-URL in einer200 OK-Antwort Links enthält und man durch das Folgen dieser Links alle von der API bereitgestellten Ressourcen erkunden können muss. Hierarchien waren erlaubt, aber alles musste irgendwo im Link-Baum erreichbar sein; die Absicht war, Discoverability zu schaffen.In fast allen Umgebungen, in denen ich in den vergangenen 20 Jahren gearbeitet habe, wurde tatsächlich
POST resource_name/resource_id/sub_resource/sub_resource_id/mutation_typeoder, je nach Idempotenz-Handling des Unternehmens,PUT resource_name/resource_id/sub_resource/sub_resource_idverwendet, wobei Clients auf Basis von Strukturwissen wie Swagger/OpenAPI magische URLs zusammensetzten.Deshalb nennen strenge Leute die Praxis oft nicht „REST“, sondern „RESTful“, um auszudrücken, dass sie Fieldings REST-Definition nicht implementiert.
Aus der Perspektive von jemandem, der ziemlich große APIs mit internen und externen Clients betreut, ist auch der Ablauf schwer nachvollziehbar, Code aus OpenAPI-Spezifikationen zu generieren. Man füllt die generierten Stubs aus, verbessert dann die API-Spezifikation iterativ, woraufhin das Tool wieder neue Stubs erzeugt; manuelles Mergen wird nötig, und je größer die API wird, desto schwieriger wird es, relevante Änderungen zu finden.
Deshalb habe ich mithilfe von
go/astund ähnlichem ein Monster gebaut, das OpenAPI-Spezifikationen aus dem Code generiert. Es ist nicht perfekt, aber eine 95%-Lösung, die sowohl mit Echo als auch mit Gin funktioniert. Wenn ein neuer Endpoint benötigt wird, kann man Request-/Response-Structs und einen leeren Handler erstellen, die Dokumentation generieren und sie an die Frontend-Entwickler schicken. So geht es schnell voran.Die meisten Entwickler müssen sich nicht damit beschäftigen, wie sie eine API in OpenAPI ausdrücken, und die Dokumentation passt immer zum Code.
Apipe.new(GitHun) |> from("search/repositories") |> eq(:language, "elixir") |> order_by(:updated) |> limit(1) |> execute()Man muss weder die verfügbaren gRPC-Funktionen noch die RESTful-Eigenheiten von Third-Party-APIs kennen und behält dennoch eingebaute Dokumentation und typisierten Zugriff.
https://github.com/cpursley/apipe
Ich erwäge außerdem eine TypeScript-Adapter-Schicht, damit man es ähnlich wie Supabase in JS/TS-Projekte einbinden kann.
const { data, error } = await apipe.from('search/repositories').eq('language', 'elixir').order_by('updated').limit(1)Dieser Aufruf kann über einen Elixir-Proxy laufen, der schwere Aufgaben wie asynchrone Verarbeitung oder Rate-Limiting übernimmt.
Auf Basis der JSON-Serialisierung von Protobuf kann man OpenAPI-Beschreibungen erstellen und per Swagger bereitstellen; gRPC selbst bietet außerdem eingebaute reflection sowie das darauf aufbauende Utility
grpcurl.Aus der Perspektive von jemandem, der bei einigen FAANG-Unternehmen gearbeitet hat, ist Thrift/gRPC für internes Service-Routing wirklich nützlich.
Ein erheblicher Teil dieser Komplexität wird allerdings von den Teams gemanagt, die Bibliotheken, Service-Discovery-Schichten, Routing usw. bauen. Mit RPC-Protokollen lassen sich solche Dinge in einem Umfang und mit einer Geschwindigkeit umsetzen, die mit gewöhnlichen JSON/REST-Services schwer zu erreichen sind.
Ich habe auch noch nie gesehen, dass eine REST-API keine Verben durchsickern lässt, und wenn ich ein Backend-Service-Mesh bauen oder zwei lokale Services über einen Netzwerk-Stream verbinden müsste, würde ich immer gRPC wählen.
Für kunden- oder webseitig exponierte Zwecke würde ich gRPC aber niemals einsetzen. RPC ist mächtig, weil es viele Entscheidungen festlegt und „einen einzigen Weg“ erzwingt. Wenn dagegen verschiedenste Clients aus unterschiedlichen Tech-Stacks einen Service nutzen müssen, ist REST deutlich besser.
POST /api/doThingyschicke.Das ist ein einfaches RPC, bei dem jeder mitmachen kann, solange nur ein ganz grundlegender HTTP-Client vorhanden ist, und es funktioniert gut auf allen Betriebssystemen und in allen Browsern. Man muss sich auch nicht damit herumschlagen, ob etwas in den URL-Pfad, in Query-Parameter oder in den Body gehört.
Wenn man Server-Ökosysteme wie Buf oder Connect nutzt, die das Ganze weniger unbequem machen wollen, akzeptiert auch gRPC bereitwillig JSON über HTTP.
Ich denke an Fälle wie Online-Spiele oder MMOs, die deutlich mehr Echtzeitkommunikation brauchen als REST, bin mir aber nicht sicher, ob man heutzutage einfach irgendetwas auf eine Socket-Verbindung setzt.
Ich wüsste auch gern, was sonst noch ausprobiert wurde.
Wenn man kein bidirektionales Streaming macht, halte ich gRPC im Großen und Ganzen für Zeitverschwendung.
Es gibt transitive Dependency-Hölle zur Laufzeit, Toolchain-Hölle, und selbst die Teams innerhalb von Google, die die jeweiligen Implementierungen betreuen, scheinen sich philosophisch nicht einig zu sein, wie Grundfunktionen funktionieren sollten.
Wenn man eine gRPC-API Teams bereitstellt, die nicht die eigene Sprache verwenden — besonders wenn es nicht Go/Python/Java ist oder selbst davon nur alte Versionen —, sie mit kommerziellen Standardprodukten integriert oder sie im Browser exponiert, braucht man überall Zwischenschichten.
gRPC lief größtenteils reibungslos.
REST für die Kommunikation zwischen Backend-Services zu verwenden ergibt bei Performance-Anforderungen ebenfalls wenig Sinn, und außer bei Aufrufen, die gelegentlich kleine Datenmengen abrufen, gibt es wenig Grund, ein menschenlesbares Protokoll/API zu verwenden.
Wenn ein
oneof-Subtyp zurückgegeben wurde, dessen aktueller Typ keine Felder hatte, sah das Ergebnis der automatischen Konvertierung etwa so aus:{ "id": "id", "sub_type_two": { } }Funktional funktioniert es, und selbst wenn später Felder hinzukommen, funktioniert dieser Code weiter. Aber in der Web-Welt ist es seltsam, den Antworttyp durch ein leeres Objekt darzustellen, und beim Schreiben von protobuf fällt einem dieses Problem womöglich nicht gut auf.
Im Vergleich zu fast jedem denkbaren Binärprotokoll ist es eher Anti-Streaming.
Meine einzige Erfahrung mit gRPC im Unternehmen war ein Projekt, das ein anderer Senior Developer mit der Begründung durchgedrückt hatte, man brauche „Performance“.
Am Ende mussten wir trotzdem eine JSON-API bauen, weil das Frontend sie konsumieren können musste, und außer diesem Entwickler hatte niemand Erfahrung mit gRPC. Der Entwickler selbst ging dann auch nicht über den gRPC-Python-Quickstart hinaus und half nicht beim Beheben von Bugs.
Das Projekt war aus unzähligen Gründen ein Chaos und erreichte nie auch nur annähernd eine Größenordnung, die gRPC gerechtfertigt hätte.
Trotzdem gefiel mir gRPC bei der kleinen persönlichen Nutzung, die ich damit hatte, und ich habe das Gefühl, dass es sehr viel mehr Arbeit und Nachdenken braucht. Das kann auch daran liegen, dass ich mit JSON-APIs sehr viel mehr Erfahrung habe.
Ich nutze ConnectRPC https://connectrpc.com/ mit Freude.
Es behebt viele der problematischen Teile von gRPC, und ich hoffe, dass ConnectRPC besseres Streaming entwickeln kann, sobald Safari WebTransport akzeptiert.
Anfangs hielt ich https://buf.build für überdimensioniert, aber die Möglichkeit, Proto-Dateien von Drittanbietern einzubinden, ohne sie einzeln herunterzuladen, war ausschlaggebend.
deps:- buf.build/landeed/protopatch- buf.build/googleapis/googleapisAuch die automatische SDK-Generierung ist ein großer Punkt. Früher hätte ich lobend erwähnt, dass SDKs für ungefähr 9 Sprachen automatisch erzeugt werden; in den letzten ein, zwei Tagen gab es aber ein Update, und jetzt sehe ich 16 Sprachen plus OpenAPI und weitere neue Funktionen.
Auch ich habe mich vom falschen Versprechen des gRPC-Streamings verleiten lassen, und dieses Dokument deckte sich genau mit meiner Erfahrung: https://connectrpc.com/docs/go/streaming/
Dadurch kann man bidirektionales Streaming auch im Browser nutzen.
Es wirkt, als hätte Google die ganze Branche in eine Art psychologische Operation verwickelt, damit alle für die interne Service-Kommunikation gRPC verwenden.
Die Developer Experience von gRPC ist deutlich schlechter als bei REST.
Man kann jemandem nicht einfach einen simplen Befehl geben, um einen Endpoint aufzurufen; es braucht zusätzliche, nicht standardisierte Tools. Außerdem ist der generierte Client-seitige Code in praktisch jeder Sprache ein ungewöhnlich hässlicher Klotz.
Durch eine einzige Protokolländerung kann man statisch erkennen, welche nachgelagerten Consumer aktualisiert und neu deployed werden müssen; aus einer Aufgabe von mehreren Wochen kann so eine Änderung von einer Stunde werden.
Man weiß außerdem, dass eingehende und ausgehende Nachrichten sofort validiert werden, und kann sie günstig speichern, um sie später wiederherzustellen.
Mit proto erhält man sehr gut lesbare API-Dokumentation, die nicht durch Code oder Business-Logik verwässert wird. Versionsverwaltung und Semantik für Deprecation sind ebenfalls eingebaut, und abgesehen von Maps werden auch reichhaltigere Datenstrukturen unterstützt.
Im Vergleich wirkt JSON im Backend aufgebläht und angestaubt.
Wenn man Datentypen und Funktionssignaturen aufschreibt, bekommt man etwas, das sich wie eine echte Funktion aufrufen lässt, und kann sich statt auf Serialisierungs-/Deserialisierungs-Boilerplate auf die Business-Logik konzentrieren.
Thrift ist aus demselben Grund viel besser, als alles selbst zu bauen, und GraphQL halte ich für noch besser.
Selbst in Go ist es lästig, Regenerierung und Versionierung geteilter protos in den Griff zu bekommen, und mit jeder zusätzlichen Sprache wird es schlimmer.
Trotzdem scheint jedes Startup zu glauben, es brauche 100 Microservices und gRPC.
Der Satz „Wenn eine API eine REST API ist, muss der Client das URL-Format nicht verstehen, und dieses Format ist kein Teil der API-Spezifikation, die man dem Client gibt“ steht in engem Zusammenhang mit Roy Fieldings Definition von REST.
Fielding schrieb, dass eine REST API ohne Vorwissen betreten werden sollte, außer einer anfänglichen URI und einer Menge standardisierter Medientypen; danach sollten alle Übergänge des Anwendungszustands dadurch erfolgen, dass der Client aus den vom Server angebotenen Optionen wählt.
https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypert...
Das wurde zwar schon oft diskutiert, aber es ist immer noch interessant, dass in einem wirklich RESTful System die „API-Spezifikation“, die man dem Client gibt, nur aus der anfänglichen Entry-Point-URI/URL bestehen sollte.
Persönlich halte ich sie für aufgebläht und nicht für eine Lösung realer Probleme.
https://en.m.wikipedia.org/wiki/HATEOAS
Insbesondere verstehe ich nicht, wie der Client die manipulierbaren Ressourcen oder die Request-/Response-Modelle entdecken soll.
Die Konfiguration mag so sein, aber die API-Spezifikation muss viel mehr als die URL enthalten und die vom System verwendeten Medientypen detailliert beschreiben. Das heißt, der Großteil der Beschreibung betrifft die HTTP-Request-/Response-Bodys.
Im Link steht ebenfalls, dass „eine REST API fast ihren gesamten Beschreibungsaufwand auf die Definition der Medientypen verwenden sollte, mit denen Ressourcen dargestellt und der Anwendungszustand gesteuert wird“.
Am Ende gibt man also nicht einfach
application/jsonzurück, sondern etwas Spezifisches wie+json, und meistens enthält es keine generische JSON-Struktur, sondern Business-Daten, die die Anwendung verstehen muss.In der öffentlichen Diskussion liegt der Fokus nur auf der anfänglichen URL, und der größte Teil von Fieldings „beschreibt die Medientypen“ fällt unter den Tisch. Daher ist es völlig nachvollziehbar, wenn jemand, der „eine URL reicht“ hört, fragt: „Wo ist der Rest der Spezifikation?“
Ich habe den Artikel auch angeklickt, weil ich dachte, es sei wieder ein Blogger, der REST nicht verstanden hat, aber der Autor scheint zumindest die Grundkonzepte zu kennen.
Ich mag gRPC ebenfalls, und für kommerzielle Projekte ist es ziemlich attraktiv, aber für persönliche oder idealistische Projekte halte ich REST für besser.
Ich mag es nicht, gRPC innerhalb des Rechenzentrums zu verwenden.
Man wählt es aus Performance-Gründen, aber gRPC ist schwerlich als High Performance zu bezeichnen, und die Qualität der öffentlichen Clients ist besonders außerhalb der Kernimplementierungen in C++/Java sehr niedrig. Die Node.js-Implementierung ist ein Beispiel dafür.
Ich habe nichts dagegen, protobuf als API-Spezifikation zu verwenden, aber man sollte es zusammen mit einem Framing-Protokoll über TCP einsetzen können. Für diese Art von RPC gibt es allerdings keine eindeutig dominierende Wahl.
Bei webbasierten APIs bevorzuge ich lesbare Payloads, aber meist verwendet man JSON und verliert dabei Typspezifität, was zu Interoperabilitätsproblemen zwischen Backend-Sprachen führt. Besonders in Node.js wird
JSON.parsequasi wie eine Implementierung für Schema-Mapping verwendet.Wenn man das sauber machen will, muss man Encoder und Decoder explizit aus dem Schema generieren, und damit geht ein Teil des Vorteils von JSON im JS-Kontext verloren.
Ich beobachte auch Microsofts TypeSpec-Projekt: typespec.io
Bei einem Single-Language-Tech-Stack nimmt seine Bedeutung ab. Und wenn man außerhalb der wichtigsten Google-Sprachen arbeitet, ist die Experience wahrscheinlich entsprechend weniger gut.
gRPC wirkte für alle außerhalb von Google unnötig schwer zugänglich.
Der gRPC-JS-Client ist unnötig schwergewichtig und ziemlich undurchsichtig. Die Idee ist gut, aber im Vergleich zu Leuten, die an die „Einfachheit“ von REST gewöhnt sind, ist die Umsetzung enttäuschend.
RPC ist semantisch leichter wartbar, weil man die Kardinalität oder Beziehungen des Datenmodells nicht in ein einzelnes normatives Pattern zwängen muss. In einer Welt, in der sich APIs schnell ändern, ist es schwierig, schöne RESTful Entities zu treffen; bei großen Teams und wechselnden Anforderungen/Ownership ist ein servicezentriertes Design besser.
Die Frontend-Seite wartet keine Backend-Systeme. Sie will eine leicht verständliche API und Entities, die sich mit REST abstrahieren lassen. Sie ist der letztliche Nutznießer eines solchen Designs.
Der Aufwand für REST ergibt Sinn bei Unternehmen, die APIs verkaufen und bei denen Drittentwickler die wichtigsten Kunden sind.
Für Backend-Entwicklung sind protobuf und binäre Wire-Encoding einfacher. Man kann APIs definieren und statisch typisiert zwischen Services teilen, und auch die Zeit für Message-Encoding/-Decoding sinkt. JSON ist weder semantisch noch typisiert und hat zudem großen Overhead.
Umgekehrt arbeitet das Frontend nativ mit Text und JSON. Es will keine protobuf-Definitionen herunterladen oder Binärdaten wie Bürger zweiter Klasse behandeln, und es passt auch nicht sauber zu den Tools.
gRPC bringt Routing, Retries, Side Channels, Streaming und Semantik für Protokoll-Deprecation gut mit, aber im Frontend ist davon kaum etwas sichtbar. Das alles ist für Backend-Consumer gedacht.
Am Ende ist es zu 100 % eine Tooling-Kluft zwischen Frontend und Backend und ein Missverhältnis bei Interface und Usability.
Im Grunde kann man den Quelltext ansehen, er ist menschenlesbar und leicht zu inspizieren.
gRPC ist dafür gedacht, dass Maschinen effizient miteinander sprechen; sobald Menschen eingreifen, sei es beim Coding oder beim Inspizieren von Requests/Responses, wird es etwas unbequem.
Weil Kontext und Ziele unterschiedlich waren, ist dieser Unterschied in der Usability nachvollziehbar.
Meiner Ansicht nach ist die Implementierung von buf.build gut.
https://buf.build/blog/protobuf-es-the-protocol-buffers-type...
Viele scheinen es gewählt zu haben, weil sie ein Protokoll wie binäres RPC mit Vertrag brauchen; je weiter man sich aber von GoLang entfernt, desto schlechter wird es.
Für einfache CRUD-Services reicht REST völlig aus.