APIs are the backbone of modern applications. Whether it’s powering a mobile app, enabling microservices, or integrating third-party services, choosing the right API protocol is crucial. Traditionally, REST APIs have dominated the web, but in recent years gRPC, powered by Protocol Buffers (Protobuf), has gained traction for high-performance, real-time systems.
In this article, we’ll explore the differences between REST and gRPC, why gRPC is gaining popularity, and how you can build both using Go.
1. REST vs gRPC: What’s the Difference?
REST
- Based on HTTP/1.1 and JSON payloads.
- Human-readable, widely supported.
- Great for public APIs and simple client-server communication.
gRPC
- Based on HTTP/2 with Protobuf for serialization.
- Strongly-typed, compact, and schema-first.
- Supports streaming (client, server, and bidirectional).
- Ideal for microservices and performance-critical systems.
Key takeaway: REST is easy to start with and widely compatible, while gRPC provides better performance and strong contracts.
2. Building a REST API in Go
Go’s net/http
makes it easy to create REST APIs:
package main
import (
"encoding/json"
"net/http"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
func getUser(w http.ResponseWriter, r *http.Request) {
user := User{ID: 1, Name: "Alice"}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(user)
}
func main() {
http.HandleFunc("/user", getUser)
http.ListenAndServe(":8080", nil)
}
Pros: Quick setup, easy debugging.
Cons: JSON is verbose, slower for large-scale systems.
3. Building a gRPC API in Go
Step 1: Define a Protobuf schema (user.proto
)
syntax = "proto3";
package user;
service UserService {
rpc GetUser(UserRequest) returns (UserResponse);
}
message UserRequest {
int32 id = 1;
}
message UserResponse {
int32 id = 1;
string name = 2;
}
Step 2: Generate Go code
protoc --go_out=. --go-grpc_out=. user.proto
Step 3: Implement the server in Go
package main
import (
"context"
"log"
"net"
pb "example.com/user"
"google.golang.org/grpc"
)
type server struct {
pb.UnimplementedUserServiceServer
}
func (s *server) GetUser(ctx context.Context, req *pb.UserRequest) (*pb.UserResponse, error) {
return &pb.UserResponse{Id: req.Id, Name: "Alice"}, nil
}
func main() {
lis, _ := net.Listen("tcp", ":50051")
grpcServer := grpc.NewServer()
pb.RegisterUserServiceServer(grpcServer, &server{})
log.Println("gRPC server running on port 50051")
grpcServer.Serve(lis)
}
Pros: High performance, strong typing, streaming support.
Cons: More setup, less human-readable (requires clients to use gRPC).
4. Performance Comparison
- Serialization: Protobuf (gRPC) is smaller and faster than JSON.
- Protocol: HTTP/2 supports multiplexing, reducing latency compared to REST’s HTTP/1.1.
- Streaming: gRPC enables real-time communication, REST does not.
In benchmarks, gRPC can outperform REST APIs by 2–10x depending on workload.
5. When to Use REST vs gRPC
Choose REST when:
- You’re building public APIs consumed by browsers or third-party developers.
- Human readability and compatibility are more important than raw performance.
Choose gRPC when:
- You’re building microservices that need efficient communication.
- You require real-time streaming (chat apps, IoT telemetry, trading platforms).
- You want strong contracts and type safety across teams.
Conclusion
Both REST and gRPC are powerful tools, and the right choice depends on your project’s requirements. With Go, you can implement both with ease: REST for simple, external-facing APIs and gRPC for high-performance internal services.
As systems scale, many teams even adopt a hybrid approach, using REST for public-facing endpoints and gRPC for inter-service communication.