FHIR Starter Project

Introduction

The FHIR Starter Project is a step-by-step guide for developers joining one of our FHIR teams (for example CES, HSME, or VAOS). If you are joining a FHIR team, complete this project instead of the Starter Project. Like the Starter Project, it makes sure your development environment is fully set up and introduces you to our development workflow, but it does so with the kind of service you will work on every day: a FHIR service built on HAPI FHIR and the Mobile Framework.

The tutorial service you will create covers concepts such as:

  • How the Mobile Framework runs a HAPI FHIR server inside a Spring Boot service

  • Resource providers, and the read, search, and operation interactions

  • Role-based JWT security and veteran self-access checks

  • Calling a REST service and a downstream FHIR server

  • Stubbing dependencies with WireMock and cerner-mock in a local cluster

  • FHIR error handling with OperationOutcome

  • Integration testing with Skaffold

  • Health checks and service readiness

  • Service configuration and documentation

This tutorial is intended to give you a broad overview of how we build FHIR services, and as such it is quite involved. If you have questions along the way, review the developer guides available on the Apothesource Documentation Site, as well as our Slack and StackOverflow sites. If you cannot find the information you need or still need help, post a message in the greenfield-dev Slack channel, and someone will assist you.

Follow the instructions on the following pages closely, and feel free to reach out to the Onboarding team for guidance.

What You Will Build

You will build fhir-starter-service, a FHIR R4 service that returns patient demographics from the Mobile MVI Service and encounters from an Oracle Health (Cerner) FHIR server. In your local cluster, WireMock stands in for the Mobile MVI Service and cerner-mock stands in for Oracle Health.

                       x-vamf-jwt (staff | veteran)
 curl / Postman / tests ────────────────────────────► fhir-starter-service      [base] = /fhirstarter/v1/fhir
                                                        │
 GET [base]/metadata, [base]/$validate-icn (generated)  │
 GET [base]/Patient/{icn}                               ├── REST client "mvi" ──────► WireMock (stubs of the Mobile MVI Service)
 GET [base]/Patient?identifier=<ICN system>|{icn}       │
 GET [base]/Patient/{icn}/$identifiers                  │
                                                        │
 GET [base]/Encounter?patient={icn}[&status=finished]   └── FHIR client "cerner" ────► cerner-mock (Oracle Health mock)
                                                              + OAuth client-credentials token

Throughout this guide, [base] means the FHIR base URL of your local service, http://localhost:8080/fhirstarter/v1/fhir. By the end, your service supports these interactions:

Interaction Who may call it Where the data comes from Page

GET [base]/metadata

Anyone

The FHIR server (CapabilityStatement)

2

GET [base]/$validate-icn?icn={icn}

Staff

Generated sample operation

2

GET [base]/Patient/{icn}

Staff; veterans for their own record

Mobile MVI Service

3

GET [base]/Patient?identifier=urn:oid:2.16.840.1.113883.4.349|{icn}

Staff; veterans for their own record

Mobile MVI Service

3

GET [base]/Patient/{icn}/$identifiers

Staff; veterans for their own record

Mobile MVI Service

6

GET [base]/Encounter?patient={icn}&status={status}

Staff; veterans for their own record

Oracle Health (cerner-mock)

8

FHIR in a Nutshell

You don’t need to be a FHIR expert to complete this project, but these basics will help. The FHIR R4 specification and the HAPI FHIR plain server documentation go into more depth.

Resources

FHIR models healthcare data as resources, such as Patient and Encounter, which are exchanged as JSON (media type application/fhir+json). Every resource on a server has a logical id, for example Patient/10115V737384. Resources also carry business identifiers, which pair a system with a value. VA patients are identified by their ICN, in the system urn:oid:2.16.840.1.113883.4.349. Resources point at each other with references, for example "subject": {"reference": "Patient/10115V737384"}.

Interactions

Clients use REST interactions on a FHIR server. A read returns one resource by its logical id (GET [base]/Patient/10115V737384). A search returns a Bundle of type searchset that contains the matching resources (GET [base]/Patient?identifier=…​). An operation is a named function whose name starts with $. Operations can be invoked on the whole server ([base]/$validate-icn), on a resource type ([base]/Patient/$name), or on one resource ([base]/Patient/10115V737384/$name). Operations take and return a Parameters resource.

Errors and capabilities

Errors are returned as an OperationOutcome resource that lists the issues. Every FHIR server describes what it supports in a CapabilityStatement, returned by GET [base]/metadata.

HAPI FHIR and the Mobile Framework

HAPI FHIR is the Java implementation of FHIR that our services use. In a HAPI plain server, you write resource providers: classes with annotated methods (@Read, @Search, @Operation) that HAPI calls for each request. HAPI exceptions become HTTP status codes and an OperationOutcome. The Mobile Framework FHIR starters run the HAPI server inside Spring Boot, register your providers, apply the JWT security annotations, and provide RestClientManager and FhirClientManager for calling other services.

If you have built Spring REST services before, this table shows how the pieces you know map to a FHIR service:

Spring REST service Mobile Framework FHIR service

Endpoints

@RestController with @GetMapping methods

HAPI resource providers with @Read, @Search, and @Operation methods

API contract

An OpenAPI document you write

The CapabilityStatement that HAPI generates from your providers (/metadata), plus a generated OpenAPI document and Swagger UI

Errors

@ControllerAdvice and ResponseStatusException

HAPI exceptions, returned as an HTTP status and an OperationOutcome

Calling other services

RestClientManager

RestClientManager for REST services, and FhirClientManager for FHIR servers

Security

@JwtRestricted annotations

The same annotations, plus a check in code that veterans only access their own records

Prerequisites

It is assumed before beginning that you have completed:

Also make sure that:

  • OrbStack (or Docker Desktop) is running with Kubernetes enabled, with enough memory for your service, WireMock, and cerner-mock (see the resource settings in the Development Environment Setup)

  • Your ~/.m2/settings.xml matches the Maven settings, including the bitbucket server

  • The DTR_URL, DTR_USER, and DTR_PWD environment variables are set (see Token and Environment Configuration)

  • You are connected to the VPN

  • You have jq installed (brew install jq). The curl examples pipe their responses through it, so the JSON is easy to read

Versions Used in This Guide

This guide was last verified in October 2026 with the versions below. If a newer version is available when you start, use the versions shown here unless the Onboarding team tells you otherwise.

Component Version

fhir-module-archetype

3.0.4

Mobile Framework (mobile-service-parent)

3.0.6

HAPI FHIR

8.12.1

mobile-mvi-client

1.53

WireMock component (dhsss/wiremock)

v3.13.2

cerner-mock component (ces/cerner-mock)

Release/1.16

jwtgen

3.1.0 (build it from main)

Getting Started

Clone the Starter Project

Your FHIR service lives in a branch of the same repository as the Starter Project. Clone it from https://coderepo.mobilehealth.va.gov/scm/ckm/starter-project.git:

git clone https://coderepo.mobilehealth.va.gov/scm/ckm/starter-project.git
cd starter-project

Create a New Branch

Before you begin, create a new branch off of the main branch. Name it fhir_starter_ plus your first and last name, for example fhir_starter_jane_doe:

git checkout -b fhir_starter_<firstname>_<lastname>

After the command has run, make sure you are on the new branch by running git branch.

You will push this branch to the shared repository as you work through the guide. Never push to main.

How This Guide Is Organized

The guide has nine pages. Each one adds to your service, and lists what you should have done in a "You’re Done When" checklist. Most pages end with a full build and a commit; page 3 ends with a commit only, because page 4 updates the tests that page 3 breaks.

Plan on about three days for the whole guide; developers who have used Spring, Mockito, and Kubernetes before often finish in two. The times below are estimates for reading and doing, and include the full builds, which take a few minutes each:

  1. FHIR Starter Project (this page): what you will build, and how to get started (about 1 hour)

  2. 2. Create the FHIR Service: generate the service from the archetype, build it, and call it with test JWTs (about 2.5 hours)

  3. 3. Implement the Patient Resource Provider: read and search patients from the Mobile MVI Service, stubbed with WireMock (about half a day)

  4. 4. Test the Patient Provider: learn the test libraries, and test the Patient read and search with unit and integration tests (about half a day)

  5. 5. Add FHIR Exception Handling: report errors as an OperationOutcome, and translate downstream failures (about 2 hours)

  6. 6. Add an Operation and an Interceptor: add a FHIR operation and a server interceptor (about 2.5 hours)

  7. 7. Connect to Oracle Health: deploy cerner-mock, and configure the Oracle Health FHIR client, its settings, and its secrets (about 2 hours)

  8. 8. Search Encounters in Oracle Health: authenticate with an access token, and search encounters in Oracle Health (about half a day)

  9. 9. Configure, Document, and Review: describe and document the service, complete its tests, and present it (about 3 hours, or 2 without the optional sections)

Sections marked (Optional) can be skipped; nothing later depends on them. The appendix explains Kubernetes and Kustomize, and has reference material that the pages link to.

When You Get Stuck

  • Page 2 has a section, When Something Goes Wrong, for the most common problems: a deployment that doesn’t start, a failing service-lint check, and missing JWTs.

  • The fhir_starter_reference branch of the starter-project repository is a completed version of this guide, with one commit for each page from page 3 on, named "FHIR starter page 3: …​", "FHIR starter page 4: …​", and so on. The pages tell you which file to compare when a step is long. Try each page yourself first; you learn more from a build error you fix than from code you copy.

    To compare, find the commit of the page, then look at one file, or compare your whole service with it. For page 4, for example:

    git fetch origin fhir_starter_reference
    git log --oneline --grep "page 4:" origin/fhir_starter_reference
    git show <commit>:fhir-starter-service/src/test/java/gov/va/mobile/fhirstarter/v1/resource/PatientProviderTest.java
    git diff <commit> -- fhir-starter-service/src

    You can also browse the branch in coderepo: commits of fhir_starter_reference.

    The reference differs from your work in small ways, such as commit IDs, generated timestamps, and your own wording in the documentation. Its classes and tests should match yours closely.

  • If you’re still stuck, ask in the greenfield-dev Slack channel; include the command you ran and the error.

Further Reading

FHIR Services You Will Work With

The services below are built the same way as your tutorial service. Each page of this guide ends with pointers into them, so you can see how a real service handles the same task. You don’t need them to get started.

Service Good for studying

cerner-fhir-adapter

The MVI client, the Oracle Health FHIR client and its authentication, and veteran self-access checks

clinical-encounter-service

FhirConfig, system operations, and WireMock stubs of the Mobile MVI Service

chapi-proxy-service

The error message catalog, interceptors, and an OAuth2 FHIR client

fehr-fhir-service

Loading test data into cerner-mock

vista-fhir-note-api

The MVI client and the WireMock setup for local deployments

vista-fhir-adapter

A larger service with many providers, operations, and downstream clients

vista-scheduling-provider

Another larger service, built on the same framework

Framework Repositories

When you want to know how the framework or the build works, clone and examine these repositories:

Mobile Framework

git clone https://coderepo.mobilehealth.va.gov/scm/ckm/mobile-framework.git

NGSS-Maven-Tiles

git clone https://coderepo.mobilehealth.va.gov/scm/ckm/ngss-maven-tiles.git

These comprise two major parts of most of our services: the dependency management and the Maven configuration and tooling used to build our services. Many questions can be answered from an examination of these two projects.

For HAPI FHIR itself, bookmark the HAPI FHIR documentation, in particular the sections about the plain server and the generic client.

Continue to 2. Create the FHIR Service to generate your service.