Server Stream

A complete example showing server streaming in action: the server sends links one at a time with a simulated delay, and the client prints them as they arrive in real-time.

Server — Stream with Delay

Add this method to your linkServer in main.go:

func (s *linkServer) ListLinks(req *pb.ListLinksRequest, stream pb.LinkService_ListLinksServer) error {
	s.mu.RLock()
	defer s.mu.RUnlock()

	for _, link := range s.links {
		// Simulate processing time per item
		time.Sleep(500 * time.Millisecond)

		if err := stream.Send(link); err != nil {
			return err
		}
		log.Printf("sent: %s", link.GetShortCode())
	}

	log.Printf("stream complete, sent %d links", len(s.links))
	return nil
}

Client — Real-Time Stream Reader

cmd/client/main.go:

package main

import (
	"context"
	"fmt"
	"io"
	"log"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"

	"shortener/pb"
)

func main() {
	conn, err := grpc.NewClient("localhost:50051",
		grpc.WithTransportCredentials(insecure.NewCredentials()),
	)
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()

	client := pb.NewLinkServiceClient(conn)
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	stream, err := client.ListLinks(ctx, &pb.ListLinksRequest{})
	if err != nil {
		log.Fatalf("ListLinks failed: %v", err)
	}

	fmt.Println("Waiting for links...")
	count := 0
	for {
		link, err := stream.Recv()
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Fatalf("stream error: %v", err)
		}
		count++
		fmt.Printf("  [%d] %s -> %s (clicks: %d)\n",
			count, link.GetShortCode(), link.GetUrl(), link.GetClicks())
	}
	fmt.Printf("Done. Received %d links.\n", count)
}

Expected Output

Server terminal:

2026/05/09 12:00:00 sent: k7m2px
2026/05/09 12:00:00 sent: a3f9wq
2026/05/09 12:00:01 sent: zt04bn
2026/05/09 12:00:01 stream complete, sent 3 links

Client terminal (messages appear one by one with 500ms gaps):

Waiting for links...
  [1] k7m2px -> https://go.dev/doc/effective_go (clicks: 0)
  [2] a3f9wq -> https://protobuf.dev/programming-guides/proto3/ (clicks: 0)
  [3] zt04bn -> https://grpc.io/docs/languages/go/quickstart/ (clicks: 0)
Done. Received 3 links.

The key thing to notice: the client prints each link as it arrives, not all at once. That's the difference between streaming and a regular list response.

💡 Remove the time.Sleep in production. It's here to make the streaming behavior visible.

💻 Run locally

Copy the code above and run it on your machine

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