Metadata Roundtrip

Client sends metadata, server reads it and sends response metadata back.

Client — cmd/client/main.go

import (
	"context"
	"fmt"

	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"

	"shortener/pb"
)

func getWithMetadata(client pb.LinkServiceClient) {
	ctx := metadata.AppendToOutgoingContext(context.Background(),
		"authorization", "Bearer my-token",
		"x-request-id", "req-12345",
	)

	var header, trailer metadata.MD
	resp, err := client.GetLink(ctx, &pb.GetLinkRequest{ShortCode: "abc123"},
		grpc.Header(&header),
		grpc.Trailer(&trailer),
	)
	if err != nil {
		fmt.Printf("error: %v\n", err)
		return
	}

	fmt.Printf("link: %s\n", resp.GetLink().GetUrl())
	fmt.Println("served by:", header.Get("x-served-by"))
	fmt.Println("timing:", trailer.Get("x-duration"))
}

Server — main.go

import (
	"log"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/status"
)

func (s *linkServer) GetLink(ctx context.Context, req *pb.GetLinkRequest) (*pb.GetLinkResponse, error) {
	md, _ := metadata.FromIncomingContext(ctx)
	reqID := ""
	if vals := md.Get("x-request-id"); len(vals) > 0 {
		reqID = vals[0]
	}
	log.Printf("request_id=%s", reqID)

	grpc.SendHeader(ctx, metadata.Pairs("x-served-by", "node-1"))

	start := time.Now()

	s.mu.RLock()
	link, ok := s.links[req.GetShortCode()]
	s.mu.RUnlock()
	if !ok {
		return nil, status.Errorf(codes.NotFound, "link not found: %s", req.GetShortCode())
	}

	grpc.SetTrailer(ctx, metadata.Pairs("x-duration", time.Since(start).String()))

	return &pb.GetLinkResponse{Link: link}, nil
}

💻 Run locally

Copy the code above and run it on your machine

© 2026 ByteLearn.dev. Free courses for developers. · Privacy