API · Grpc

gRPC Migration from REST — A Field Guide

gRPC Migration from REST — A Field Guide is the work that makes the systems talk. The API is the contract between the producer and the consumer; the contract is what determines.

John Kihiu12 min read

Migrating a REST service to gRPC is rarely justified as a wholesale rewrite — the realistic path is incremental, service by service, starting with the internal links where gRPC's advantages (typed contracts, HTTP/2 multiplexing, smaller payloads) actually matter, and leaving public-facing REST endpoints alone until there's a real reason to touch them. Trying to migrate everything at once, including the public API and every client that calls it, is how migrations stall for a year and get abandoned halfway.

The highest-value, lowest-risk place to introduce gRPC is a link between two services you control end to end — no browser clients, no third-party integrators, no backward-compatibility promises to the outside world. If service A calls service B over REST/JSON today and both are internal, converting that one link is a contained change: write the .proto, generate stubs for both sides, swap the client and server implementations, and ship it behind a flag if you want a safety net. This is where the migration should start, and for many teams it's also where it should end — the public API can stay REST indefinitely if there's no forcing function to change it.

PROTOBUF · MIGRATED ENDPOINT
// Before: GET /api/orders/{id} -> JSON
// After:
service OrderService {
  rpc GetOrder (GetOrderRequest) returns (Order);
}
message GetOrderRequest { string order_id = 1; }
message Order {
  string id = 1;
  string status = 2;
  int64 total_cents = 3;
}

Keep a REST facade at the edge during transition

If browser clients or external partners depend on the existing REST API, you don't have to migrate them to gRPC directly — tools like grpc-gateway or Envoy's gRPC-JSON transcoding let you implement the service once in gRPC and auto-generate a REST/JSON facade in front of it from the same .proto annotations. External consumers keep calling familiar REST endpoints; internally, everything is gRPC. This is usually the right end state for public APIs rather than forcing external clients onto gRPC, which most browsers and many third-party integrators can't consume directly anyway.

Run both side by side, verify, then cut over

A hard cutover — flip the switch, REST endpoint gone, gRPC endpoint live — is how migrations produce incidents. The safer sequence is to stand up the gRPC service alongside the existing REST one, shadow a portion of production traffic to it (or route a small percentage of real traffic and compare responses), and only decommission the REST path once the gRPC path has proven itself under real load with real edge cases. This costs more calendar time than a hard cutover but converts "did we miss an edge case" from an incident into a diff in a comparison log.

Contract-test the gRPC service against the REST behavior it replaces

Before cutting traffic over, write tests that assert the new gRPC service produces equivalent results to the REST endpoint it's replacing for the same inputs — including error cases, not just the happy path. This catches subtle semantic drift (different null handling, different pagination behavior) that a manual code review tends to miss.

Client migration is usually the long pole, not the server

Writing the gRPC service is often the fast part; migrating every client that calls it is the slow part, especially if clients are owned by other teams, other companies, or ship inside mobile apps that can't be force-updated. Budget the migration timeline around client adoption, not server readiness, and keep the REST facade running until client migration is functionally complete — a gRPC service with no REST fallback and 40% of clients still on the old contract is a self-inflicted outage waiting for a deploy.

What not to migrate

Public, browser-facing, or partner-facing endpoints where broad compatibility matters more than raw performance are usually not worth migrating to native gRPC — REST's ubiquity is the point there, and a REST facade over an internal gRPC service captures most of the internal benefit without breaking external compatibility. Reserve the migration effort for the internal links where the volume, latency sensitivity, or type-safety benefits actually justify the work.

Wrapping up

Migrate internal service-to-service links first, keep a REST facade at the edge for external and browser clients, run old and new side by side long enough to prove equivalence, and expect client adoption — not server implementation — to set the real timeline. A migration scoped this way ships incrementally and never requires an all-or-nothing cutover weekend.

John Kihiu
Acumatica ERP Developer · Laravel Engineer

Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.