Getting Started
This guide walks you through installing the library, running your first FHIRPath evaluation, and adopting the patterns you will use in production code.
Prerequisites
- Go 1.23 or later.
- A Go module (
go.mod) in your project. If you do not have one yet, rungo mod init <your-module>.
Installation
Add the library to your project with go get:
go get github.com/gofhir/fhirpathThen import it in your Go source files:
import "github.com/gofhir/fhirpath"Your First Evaluation
The simplest way to evaluate a FHIRPath expression is the top-level Evaluate function. It accepts raw JSON bytes representing a FHIR® resource and a FHIRPath expression string, and returns a Collection of results.
package main
import (
"fmt"
"log"
"github.com/gofhir/fhirpath"
)
func main() {
// Define a FHIR Patient resource as JSON
patient := []byte(`{
"resourceType": "Patient",
"id": "123",
"name": [{"family": "Doe", "given": ["John"]}],
"birthDate": "1990-05-15"
}`)
// Evaluate a FHIRPath expression
result, err := fhirpath.Evaluate(patient, "Patient.name.family")
if err != nil {
log.Fatal(err)
}
fmt.Println(result) // [Doe]
}Evaluate compiles and evaluates the expression in a single call. It returns a types.Collection (an alias for []types.Value), which holds the evaluation result. Every FHIRPath expression produces a collection – even a single scalar value is wrapped in a one-element collection, and a missing path yields an empty collection.
Compiling Expressions
If you plan to evaluate the same expression against many resources, compile it once with Compile or MustCompile and then reuse the resulting *Expression:
// Compile once (returns an error if the expression is invalid)
expr, err := fhirpath.Compile("Patient.name.given")
if err != nil {
log.Fatal(err)
}
// Evaluate against multiple resources
result1, _ := expr.Evaluate(patient1JSON)
result2, _ := expr.Evaluate(patient2JSON)MustCompile is a convenience variant that panics instead of returning an error. It is useful for package-level variables where the expression is known at compile time:
var nameExpr = fhirpath.MustCompile("Patient.name.family")Expression Cache
For production workloads where expressions may arrive at runtime (for example, from configuration or user input), use EvaluateCached. It maintains a global, thread-safe LRU cache of compiled expressions so that repeated evaluations of the same expression string do not pay the compilation cost more than once:
result, err := fhirpath.EvaluateCached(patientJSON, "Patient.birthDate")The default cache holds up to 1,000 expressions. You can create a custom cache for finer control:
cache := fhirpath.NewExpressionCache(500) // custom size
expr, err := cache.Get("Patient.name.family")
if err != nil {
log.Fatal(err)
}
result, err := expr.Evaluate(patientJSON)You can inspect cache performance at any time:
stats := cache.Stats()
fmt.Printf("Size: %d, Hits: %d, Misses: %d, Hit Rate: %.1f%%\n",
stats.Size, stats.Hits, stats.Misses, cache.HitRate())Convenience Functions
The library provides several typed convenience functions that evaluate an expression and extract the result in a single call. All of these use the expression cache internally.
EvaluateToBoolean
Returns a Go bool. Useful for FHIRPath expressions that produce a single Boolean value, such as validation constraints:
active, err := fhirpath.EvaluateToBoolean(patientJSON, "Patient.active")
if err != nil {
log.Fatal(err)
}
fmt.Println(active) // true or falseEvaluateToString
Returns a single Go string:
family, err := fhirpath.EvaluateToString(patientJSON, "Patient.name.first().family")
if err != nil {
log.Fatal(err)
}
fmt.Println(family) // DoeEvaluateToStrings
Returns a []string containing the string representation of every value in the result collection:
givenNames, err := fhirpath.EvaluateToStrings(patientJSON, "Patient.name.given")
if err != nil {
log.Fatal(err)
}
fmt.Println(givenNames) // [John]Exists
Returns true if the expression produces a non-empty collection:
hasPhone, err := fhirpath.Exists(patientJSON, "Patient.telecom.where(system='phone')")
if err != nil {
log.Fatal(err)
}
fmt.Println(hasPhone) // true or falseCount
Returns the number of elements in the result collection:
nameCount, err := fhirpath.Count(patientJSON, "Patient.name")
if err != nil {
log.Fatal(err)
}
fmt.Println(nameCount) // 1Error Handling
The library reports two categories of errors:
Compilation errors – returned by
Compile(or raised byMustCompileas a panic) when the expression string contains invalid FHIRPath syntax.Evaluation errors – returned by
Evaluateand related functions when a runtime error occurs (for example, comparing incompatible types).
A typical error-handling pattern looks like this:
result, err := fhirpath.Evaluate(resource, expr)
if err != nil {
// Handle or log the error
return fmt.Errorf("fhirpath evaluation failed: %w", err)
}
if result.Empty() {
// The path resolved but produced no values
fmt.Println("No results found")
} else {
fmt.Println("Result:", result)
}Empty collections are not errors. In FHIRPath, navigating to a path that does not exist simply returns an empty collection ({}). Always check result.Empty() before extracting values.
Evaluation with Options
For advanced use cases, you can pass functional options to control timeouts, recursion limits, and custom variables:
expr := fhirpath.MustCompile("Patient.name.family")
result, err := expr.EvaluateWithOptions(patientJSON,
fhirpath.WithTimeout(2*time.Second),
fhirpath.WithMaxDepth(50),
fhirpath.WithVariable("name", types.Collection{types.NewString("test")}),
)See Environment Variables for more detail on custom variables.
Next Steps
- Type System – learn about the eight FHIRPath types and how they map to Go types.
- Collections – understand empty propagation, singleton evaluation, and collection operations.
- Operators – reference for arithmetic, comparison, Boolean, and collection operators.
- Environment Variables – use built-in and custom variables in your expressions.