Build on HTTP POST request, making use of its body
<aside> 💡
Idempotent
An operation that produces the same result no matter how many times it is applied
</aside>
Reducing Over-fetching
Reducing Under-fetching (N+1 Requests Problem)
Drawbacks
Endpoint Operations
SpaceX APIs Example: https://www.postman.com/devrel/graphql-examples/collection/94xo3pv/free-graphql-apis
Query (Read)
{
launchesPast(limit: 10) {
mission_name
launch_date_local
launch_site {
site_name_long
}
links {
article_link
video_link
}
rocket {
rocket_name
}
}
}
It use the following HTTP POST to send the content.
curl -X POST "<https://your-graphql-endpoint.com/graphql>" \\
-H "Content-Type: application/json" \\
-d '{"query": "{ launchesPast(limit: 10) { mission_name launch_date_local launch_site { site_name_long } links { article_link video_link } rocket { rocket_name } } }"}'
-d '{"query": "..."}' → Sends the GraphQL query as JSON in the request body.
Mutation (Update)
mutation {
addUser(name: "John Doe", email: "johndoe@example.com") {
id
name
email
}
}
It use the following HTTP POST to send the content.
curl -X POST "<https://your-graphql-endpoint.com/graphql>" \\
-H "Content-Type: application/json" \\
-d '{"query": "mutation { addUser(name: \\"John Doe\\", email: \\"johndoe@example.com\\") { id name email } }"}'
-d '{"query": "..."}' → The mutation is wrapped inside a JSON object under the "query" key.
Build on HTTP/2, leveraging its two-way communication feature like WebSockets.
It can not be used by browser by default, we need used a proxy (e.g. gRPC-Web) for browsers. Because gPRC needs grand control over HTTP/2.
It is more common between server to server.
Faster than REST API, instead of sending JSON (which can be big), gPRC sends Protocol Buffers (in binary format) which provides schema.
The file type for Protocol Buffers is .proto file.
syntax = "proto3";
// The greeter service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
rpc SayHelloAgain (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
In Go:
Generate Go code from the .proto file
Use protoc with the protoc-gen-go and protoc-gen-go-grpc plugins.
protoc --go_out=. --go-grpc_out=. greeter.proto
This generates two files:
greeter.pb.go (Contains Go struct definitions)
// Code generated by protoc-gen-go. DO NOT EDIT.
// Source: greeter.proto
package greeterpb
import (
"reflect"
"sync"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/runtime/protoimpl"
)
// HelloRequest represents the request message.
type HelloRequest struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
}
// HelloReply represents the response message.
type HelloReply struct {
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
}
greeter_grpc.pb.go (Contains gRPC service interfaces)
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
package greeterpb
import (
"context"
"google.golang.org/grpc"
)
// GreeterClient defines the client API for the Greeter service.
type GreeterClient interface {
SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error)
SayHelloAgain(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error)
}
// GreeterServer defines the server API.
type GreeterServer interface {
SayHello(context.Context, *HelloRequest) (*HelloReply, error)
SayHelloAgain(context.Context, *HelloRequest) (*HelloReply, error)
}
// UnimplementedGreeterServer must be embedded for forward compatibility.
type UnimplementedGreeterServer struct{}
func (*UnimplementedGreeterServer) SayHello(context.Context, *HelloRequest) (*HelloReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented")
}
func (*UnimplementedGreeterServer) SayHelloAgain(context.Context, *HelloRequest) (*HelloReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method SayHelloAgain not implemented")
}
// RegisterGreeterServer registers a GreeterServer implementation with a gRPC server.
func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) {
s.RegisterService(&_Greeter_serviceDesc, srv)
}
Implement the gRPC Server
package main
import (
"context"
"fmt"
"log"
"net"
pb "path/to/generated/greeterpb"
"google.golang.org/grpc"
)
// GreeterServer implements the Greeter service.
type GreeterServer struct {
pb.UnimplementedGreeterServer
}
// SayHello implements the SayHello RPC.
func (s *GreeterServer) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("Received: %s", req.Name)
return &pb.HelloReply{Message: fmt.Sprintf("Hello, %s!", req.Name)}, nil
}
// SayHelloAgain implements the SayHelloAgain RPC.
func (s *GreeterServer) SayHelloAgain(ctx context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("Received again: %s", req.Name)
return &pb.HelloReply{Message: fmt.Sprintf("Hello again, %s!", req.Name)}, nil
}
func main() {
listener, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("Failed to listen: %v", err)
}
grpcServer := grpc.NewServer()
pb.RegisterGreeterServer(grpcServer, &GreeterServer{})
log.Println("gRPC server is running on port 50051...")
if err := grpcServer.Serve(listener); err != nil {
log.Fatalf("Failed to serve: %v", err)
}
}