Skip to content

Implementing a Server

Codegen gave you a server interface with one method per rpc. Implementing a server is always the same three steps:

  1. Implement the handler — write the body of GetUser: take the request, return the response (or an error).
  2. Register your implementation on a gRPC server object.
  3. Serve — bind to a port and start accepting HTTP/2 connections.
flowchart LR
  client["client stub"] -->|GetUserRequest| server["gRPC server
(listening on :50051)"]
  server -->|dispatch by method| handler["your GetUser handler"]
  handler -->|User| server
  server -->|response| client
How a request reaches your handler

The gRPC server handles all the HTTP/2, protobuf decoding, and method dispatch. Your handler only ever sees a typed request and returns a typed response.

Here is the whole thing — handler, registration, and serving — for UserService.GetUser:

type server struct {
userv1.UnimplementedUserServiceServer // forward-compat embedding
}
// GetUser is the handler: request in, response out.
func (s *server) GetUser(ctx context.Context, req *userv1.GetUserRequest) (*userv1.User, error) {
// (normally you'd look this up in a database)
return &userv1.User{
Id: req.Id,
Name: "Ada Lovelace",
}, nil
}
func main() {
lis, _ := net.Listen("tcp", ":50051")
s := grpc.NewServer()
userv1.RegisterUserServiceServer(s, &server{}) // register
log.Println("listening on :50051")
s.Serve(lis) // serve
}

When something goes wrong, you do not throw a random exception — you return a gRPC status. A missing user is NOT_FOUND, bad input is INVALID_ARGUMENT. The error model has its own lesson in the Service Definition module; the key habit to start now is: map failures to status codes deliberately, because that status is what the client’s err will carry.

Because the server rides on HTTP/2 multiplexing, many calls hit your handler concurrently over the same connections. In Go each call runs in its own goroutine; in Python a thread pool serves them; in Node the event loop interleaves them. That means your handler must be safe to run concurrently — no unsynchronized shared mutable state.

What are the three steps to implement a gRPC server?
What does your handler receive and return?
How should a handler signal that a user was not found?
Why must a gRPC handler be concurrency-safe?