Builder API
Every resource type in gofhir/models provides a fluent builder, generated automatically for all resources across R4, R4B, and R5.
Fluent Builder Pattern
The builder pattern provides a chainable API for constructing resources step by step.
Structure
For each resource type <Resource>, the library generates:
| Export | Type | Description |
|---|---|---|
<Resource>Builder | struct | The builder struct holding the resource under construction |
New<Resource>Builder() | *<Resource>Builder | Constructor that creates a new builder with a zero-valued resource |
Set<Field>(v T) | *<Resource>Builder | Sets a singular field (pointer or scalar) |
Add<Field>(v T) | *<Resource>Builder | Appends to a repeated (slice) field |
Build() | *<Resource> | Returns the constructed resource |
Naming Convention
Setis used for singular fields – fields with a maximum cardinality of 1 (e.g.,Id,Gender,BirthDate,Status).Addis used for repeated fields – fields with a maximum cardinality greater than 1 (e.g.,Name,Identifier,Telecom,Extension).
Patient Example
import "github.com/gofhir/models/r4/v2"
patient := r4.NewPatientBuilder().
SetId("patient-123").
SetActive(true).
SetGender(r4.AdministrativeGenderMale).
SetBirthDate("1990-05-15").
AddName(r4.HumanName{
Family: ptrTo("Doe"),
Given: r4.PtrSlice("John", "Michael"),
}).
AddIdentifier(r4.Identifier{
System: ptrTo("http://hospital.example.org/mrn"),
Value: ptrTo("MRN-12345"),
}).
AddTelecom(r4.ContactPoint{
System: ptrTo(r4.ContactPointSystemPhone),
Value: ptrTo("+1-555-0100"),
Use: ptrTo(r4.ContactPointUseHome),
}).
AddAddress(r4.Address{
Use: ptrTo(r4.AddressUseHome),
Line: r4.PtrSlice("123 Main St"),
City: ptrTo("Springfield"),
State: ptrTo("IL"),
PostalCode: ptrTo("62701"),
Country: ptrTo("US"),
}).
Build()
// Helper function for creating string pointers
func ptrTo[T any](v T) *T {
return &v
}Observation Example
import "github.com/gofhir/models/r4/v2"
status := r4.ObservationStatusFinal
observation := r4.NewObservationBuilder().
SetId("obs-001").
SetStatus(status).
SetCode(r4.CodeableConcept{
Coding: []r4.Coding{{
System: ptrTo("http://loinc.org"),
Code: ptrTo("29463-7"),
Display: ptrTo("Body weight"),
}},
Text: ptrTo("Body Weight"),
}).
SetSubject(r4.Reference{
Reference: ptrTo("Patient/patient-123"),
}).
SetEffectiveDateTime("2024-01-15T10:30:00Z").
SetValueQuantity(r4.Quantity{
Value: r4.NewDecimalFromFloat64(72.5),
Unit: ptrTo("kg"),
System: ptrTo("http://unitsofmeasure.org"),
Code: ptrTo("kg"),
}).
AddCategory(r4.CodeableConcept{
Coding: []r4.Coding{{
System: ptrTo("http://terminology.hl7.org/CodeSystem/observation-category"),
Code: ptrTo("vital-signs"),
Display: ptrTo("Vital Signs"),
}},
}).
Build()Choosing Between Patterns
| Criterion | Fluent Builder | Struct Literal |
|---|---|---|
| Method chaining | Yes | No |
| Incremental construction | Natural fit | Not possible |
| Conditional fields | Add after builder creation | Needs an if before the literal |
| Pointer wrapping | Handled by Set*/Add* | Use Ptr |
| Everything known up front | Works | Shortest |
When to Use the Builder
The builder is well suited when you need to construct a resource incrementally, especially when some fields depend on conditions:
builder := r4.NewPatientBuilder().
SetId(id).
SetActive(true)
if hasName {
builder.AddName(name)
}
if hasAddress {
builder.AddAddress(address)
}
patient := builder.Build()When to Use Struct Literals
For a resource you have entirely in hand, a struct literal is shorter than either and needs no builder:
patient := &r4.Patient{
Id: r4.Ptr(id),
Active: r4.Ptr(true),
Name: []r4.HumanName{name},
}Both styles produce identical output — the builder just sets fields on the same struct — so pick whichever reads better where you are.
Functional options were the third style, and are gone as of v2. Every With<Resource><Field> had a builder method with an identical signature and identical behaviour, so v1.6.0 marked all 11,952 of them Deprecated naming the replacement, and v2 removed them: Set* for single fields, Add* for repeating ones.
If you are coming from v1, staticcheck on your v1.6.0 or later build lists every call site with its replacement. See the v1-to-v2 migration guide.
Struct Literals
You can always construct resources directly using Go struct literals. This provides the most control and is the most familiar Go pattern:
patient := &r4.Patient{
Id: ptrTo("patient-789"),
Active: ptrTo(true),
Gender: ptrTo(r4.AdministrativeGenderMale),
Name: []r4.HumanName{{
Family: ptrTo("Johnson"),
Given: r4.PtrSlice("Robert"),
}},
}resourceType needs no attention in either style: it is a zero-size marker on the struct, so a plain r4.Patient{} already serializes with the right value. Read it back with GetResourceType().