Skip to content

Day 124 — Reflection & grpcurl

Month 5 · Week 2 · ⬅ Day 123 · Day 125 ➡ · Journal index

🎯 Learning Objective

Enable server reflection and use grpcurl to discover and call gRPC methods without a hand-written client or the .proto files.

📚 Topics

  • reflection.Register(server) and the reflection service
  • grpcurl list / describe / calling methods with JSON
  • health checking; when to disable reflection

📖 Reading / Sources

📝 Notes

  • Server reflection lets a client ask the server "what services and methods do you expose, and what do their messages look like?" at runtime. Enable it with one line: reflection.Register(grpcServer) → [[reflection]].
  • With reflection on, grpcurl is curl for gRPC: no .proto needed because it pulls the schema from the server. It's the fastest way to smoke-test a service → [[grpcurl]].
  • Core commands: grpcurl -plaintext localhost:50051 list (services), ... list pkg.Service (methods), ... describe pkg.Message (fields), and call: grpcurl -plaintext -d '{"id":"42"}' localhost:50051 pkg.Users/GetUser → [[tooling]].
  • -plaintext skips TLS for local dev; against a TLS server use -cacert/-cert/-key. Pass metadata with -H 'authorization: Bearer tok' to exercise the auth interceptor from Day 121 → [[transport-security]].
  • The health service (grpc.health.v1.Health) is a standard probe: grpc_health_probe or grpcurl ... grpc.health.v1.Health/Check returns SERVING. Load balancers and k8s readiness probes use it → [[health-check]].
  • Security note: reflection exposes your full API surface. Many teams enable it in dev/staging and disable it in production, or gate it behind auth, to avoid handing attackers a map.
  • Without reflection you can still call a server by pointing grpcurl at the .proto/descriptor: grpcurl -import-path . -proto user.proto ....

💻 Code Examples

import (
    "google.golang.org/grpc"
    "google.golang.org/grpc/reflection"
    "google.golang.org/grpc/health"
    healthpb "google.golang.org/grpc/health/grpc_health_v1"
)

srv := grpc.NewServer()
userpb.RegisterUsersServer(srv, &usersImpl{})

// Register the health service and reflection (dev/staging).
hs := health.NewServer()
hs.SetServingStatus("", healthpb.HealthCheckResponse_SERVING)
healthpb.RegisterHealthServer(srv, hs)
reflection.Register(srv) // <- grpcurl can now discover everything
# Discover and call, no .proto files needed:
grpcurl -plaintext localhost:50051 list
grpcurl -plaintext localhost:50051 describe user.Users
grpcurl -plaintext -H 'authorization: Bearer tok-abc' \
        -d '{"id":"42"}' localhost:50051 user.Users/GetUser

🏋️ Exercises / Practice

Exercise Status Link
(tooling) grpcurl listdescribe → call a method day note snippet
(tooling) Probe grpc.health.v1.Health/Check day note snippet

🐛 Mistakes Made

  • Ran grpcurl against a TLS server with -plaintext and got a cryptic connection error — the flag must match the server's transport. Dropped -plaintext and added -cacert.
  • Left reflection registered in a "prod-like" build before realizing it advertises the whole API; noted to gate it by environment.

❓ Open Questions

  • Can reflection be scoped to expose only a subset of services, or is it all-or-nothing?

🧠 Active Recall (answer without looking)

  1. Q: Why can grpcurl call a service without its .proto files?
ABecause server reflection (registered via `reflection.Register`) lets the server return its own service/message descriptors at runtime; grpcurl fetches the schema from the server.
  1. Q: Why might you disable reflection in production?
AIt exposes the full API surface (every service, method, and message) to anyone who can reach the port, which aids attackers. Teams often keep it on in dev/staging only or gate it behind auth.

🪶 Feynman Reflection

Reflection is the server publishing its own table of contents. Normally a client needs the .proto to know what to call; with reflection on, it just asks the server "what have you got?" and the server hands back the menu. grpcurl is the diner that reads that menu and orders — no cookbook required.

🕳️ Knowledge Gaps

  • Whether descriptor caching in grpcurl can go stale after a redeploy.

✅ Summary

I can enable reflection and the health service, and drive a running gRPC server end-to-end with grpcurl (list, describe, call, with auth metadata), plus reason about when reflection is unsafe.

⏭️ Next Steps / Prep for Tomorrow

  • Day 125: writing tests for gRPC services with bufconn.

Time spent Difficulty Confidence
90 min 🟦⬜⬜⬜⬜ 🟦🟦🟦🟦⬜

Suggested commit: docs(journal): server reflection & grpcurl workflow (day 124)