Skip to content

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:

ExportTypeDescription
<Resource>BuilderstructThe builder struct holding the resource under construction
New<Resource>Builder()*<Resource>BuilderConstructor that creates a new builder with a zero-valued resource
Set<Field>(v T)*<Resource>BuilderSets a singular field (pointer or scalar)
Add<Field>(v T)*<Resource>BuilderAppends to a repeated (slice) field
Build()*<Resource>Returns the constructed resource

Naming Convention

  • Set is used for singular fields – fields with a maximum cardinality of 1 (e.g., Id, Gender, BirthDate, Status).
  • Add is 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

CriterionFluent BuilderStruct Literal
Method chainingYesNo
Incremental constructionNatural fitNot possible
Conditional fieldsAdd after builder creationNeeds an if before the literal
Pointer wrappingHandled by Set*/Add*Use Ptr
Everything known up frontWorksShortest

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().

Last updated on