Reflection
Reflection is a first-class Protobuf-ES API, not hidden runtime internals. Use it when you need to inspect generated schemas, build registries, read custom options, or manipulate messages without hardcoding their concrete type.
import { getOption, type DescMessage, type Message } from "@bufbuild/protobuf";import { reflect } from "@bufbuild/protobuf/reflect";import { sensitive } from "./gen/example_options_pb";
export function redact(schema: DescMessage, message: Message) { const r = reflect(schema, message); for (const field of r.fields) { if (getOption(field, sensitive)) { r.clear(field); } }}Generated schemas are descriptors with extra type information. That means the same schema object can drive serialization, field lookup, registries, custom option access, and dynamic message manipulation.
Core pieces
Section titled “Core pieces”- Descriptors: Schema objects such as
DescFile,DescMessage,DescField,DescEnum, andDescService. - Registries: Collections of descriptors keyed by fully qualified name.
- Custom options: Extension-backed annotations on schema elements.
- Dynamic messages:
reflect(),ReflectMessage,ReflectList, andReflectMap.
Typed lookup
Section titled “Typed lookup”Generated schemas expose common lookups directly:
import { PhoneType, PhoneTypeSchema, UserSchema, UserService } from "./gen/example_pb";
UserSchema.field.firstName.name; // "first_name"PhoneTypeSchema.value[PhoneType.MOBILE].name; // "PHONE_TYPE_MOBILE"UserService.method.createUser.name; // "CreateUser"Keeping schema and message aligned
Section titled “Keeping schema and message aligned”When a function accepts both a schema and a message, use MessageShape<Desc> to keep them paired at the type level.
import type { DescMessage, MessageShape } from "@bufbuild/protobuf";
export function redactTyped<Desc extends DescMessage>(schema: Desc, message: MessageShape<Desc>) { const r = reflect(schema, message); for (const field of r.fields) { if (getOption(field, sensitive)) { r.clear(field); } }}