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 |
|---|---|---|---|
|
Anyone |
The FHIR server (CapabilityStatement) |
|
|
Staff |
Generated sample operation |
|
|
Staff; veterans for their own record |
Mobile MVI Service |
|
|
Staff; veterans for their own record |
Mobile MVI Service |
|
|
Staff; veterans for their own record |
Mobile MVI Service |
|
|
Staff; veterans for their own record |
Oracle Health (cerner-mock) |
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
PatientandEncounter, which are exchanged as JSON (media typeapplication/fhir+json). Every resource on a server has a logical id, for examplePatient/10115V737384. Resources also carry business identifiers, which pair a system with a value. VA patients are identified by their ICN, in the systemurn: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 aBundleof typesearchsetthat 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 aParametersresource. - Errors and capabilities
-
Errors are returned as an
OperationOutcomeresource that lists the issues. Every FHIR server describes what it supports in aCapabilityStatement, returned byGET [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 anOperationOutcome. The Mobile Framework FHIR starters run the HAPI server inside Spring Boot, register your providers, apply the JWT security annotations, and provideRestClientManagerandFhirClientManagerfor 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 |
|
HAPI resource providers with |
API contract |
An OpenAPI document you write |
The CapabilityStatement that HAPI generates from your providers ( |
Errors |
|
HAPI exceptions, returned as an HTTP status and an |
Calling other services |
|
|
Security |
|
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:
-
New Hire Checklist, including building location-service
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.xmlmatches the Maven settings, including thebitbucketserver -
The
DTR_URL,DTR_USER, andDTR_PWDenvironment variables are set (see Token and Environment Configuration) -
You are connected to the VPN
-
You have
jqinstalled (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 |
|---|---|
|
3.0.4 |
Mobile Framework ( |
3.0.6 |
HAPI FHIR |
8.12.1 |
|
1.53 |
WireMock component ( |
|
cerner-mock component ( |
|
jwtgen |
3.1.0 (build it from |
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:
-
FHIR Starter Project (this page): what you will build, and how to get started (about 1 hour)
-
2. Create the FHIR Service: generate the service from the archetype, build it, and call it with test JWTs (about 2.5 hours)
-
3. Implement the Patient Resource Provider: read and search patients from the Mobile MVI Service, stubbed with WireMock (about half a day)
-
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. Add FHIR Exception Handling: report errors as an
OperationOutcome, and translate downstream failures (about 2 hours) -
6. Add an Operation and an Interceptor: add a FHIR operation and a server interceptor (about 2.5 hours)
-
7. Connect to Oracle Health: deploy cerner-mock, and configure the Oracle Health FHIR client, its settings, and its secrets (about 2 hours)
-
8. Search Encounters in Oracle Health: authenticate with an access token, and search encounters in Oracle Health (about half a day)
-
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_referencebranch 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/srcYou 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 |
|---|---|
The MVI client, the Oracle Health FHIR client and its authentication, and veteran self-access checks |
|
|
|
The error message catalog, interceptors, and an OAuth2 FHIR client |
|
Loading test data into cerner-mock |
|
The MVI client and the WireMock setup for local deployments |
|
A larger service with many providers, operations, and downstream clients |
|
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.