Skip to main content

gRPC vs REST vs GraphQL — Pick the Right Protocol

REST = resource-based HTTP. gRPC = contract-first binary RPC. GraphQL = client-driven query API.

When to use

  • REST: public APIs, browser clients, simple CRUD
  • gRPC: internal service-to-service (performance, typed contracts, streaming)
  • GraphQL: flexible client needs, multiple frontends needing different shapes

Tradeoffs

  • gRPC requires protobuf tooling and isn't browser-native
  • GraphQL N+1 query problem requires dataloaders; REST suffers over/under-fetching
FeatureRESTgRPCGraphQL
ProtocolHTTP/1.1 or HTTP/2HTTP/2HTTP/1.1 or HTTP/2
SchemaOpenAPI (optional)Protobuf (required)SDL (required)
Browser supportNativeRequires grpc-web proxyNative
StreamingSSE / WebSocketBidirectional nativeSubscriptions
Best forPublic APIs, CRUDInternal services, performanceFlexible client queries
// REST handler
func GetUserREST(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "id")
user := userRepo.Find(id)
json.NewEncoder(w).Encode(user)
}

// gRPC handler (proto: rpc GetUser(UserRequest) returns (UserResponse))
func (s *Server) GetUser(ctx context.Context, req *pb.UserRequest) (*pb.UserResponse, error) {
user := s.repo.Find(req.Id)
return &pb.UserResponse{Id: user.ID, Name: user.Name}, nil
}

// GraphQL resolver
func (r *Resolver) User(ctx context.Context, args struct{ ID string }) (*UserResolver, error) {
user := r.repo.Find(args.ID)
return &UserResolver{user}, nil
}

Gotcha: gRPC streaming beats WebSockets for bidirectional service-to-service. But it's opaque to standard HTTP proxies and load balancers unless you configure L7 support.