One of the major goals of the 3.0 release was to consolidate as much code as possible so that adding support for additional FHIR versions would be more trivial. The 3.0 release is still only supporting STU3 and R4, the plan is to include R4B, R5, and R6 in the 3.1 release. Also worth mentioning that as of this posting R6 is in its 4th ballot phase and is expected to be released later this year.
Support for .NET target netstandard2.0 has been dropped which means .NET Framework 4.6.1 and later is no longer supported. This enabled us to remove a bunch of code which we did not have a good testing harness for.
Currently bug fixes and some minor/useful features are backported to version 2.x. You can find a list at the end of the post describing which parts has been backported.
An experimental background indexing mode has been added as an opt-in. There is uncertainty to its usefulness so it might be removed in the future, if this is something that you find useful we will need feedback on it. Documentation can be found here
You can now join our Discord server: https://discord.gg/M9kp2zGGYF.
When migrating the first thing you will notice is that AddFhir(...), UseFhir(...),
and AddFhirFacade(...) has been renamed to AddFhirWithMvc(...), UseFhirWithMvc(...),
and AddFhirFacadeWithMvc(...).
For further details on migrating from v2 to v3 look up the guide. Also make sure that you read through the What's New in 3.0 section below.
What's New in 3.0
1. Unified Spark.Engine and Spark.Store.MongoDB Shared Libraries
PRs: #1171, #1182, #1183, #1184, #1185, #1176
A new NuGet package Spark.Engine has been introduced, containing all
non-version-specific engine code. Spark.Engine.R4 and Spark.Engine.STU3 are
now thin satellite assemblies that sit on top of it. Consumers should continue
referencing the version-specific package, the shared package is an implementation
detail that makes implementing future versions of FHIR much easier.
As part of this split, Spark.Mongo.R4 and Spark.Mongo.STU3 has been consolidated and renamed to
Spark.Store.MongoDB, making the MongoDB store equally version-agnostic.
2. R4 and STU3 in One Branch
The master branch is now the single source of truth for both R4 and STU3, consolidating
CI, Docker images, and integration tests.
3. New Solution Structure
PRs: #1187, #1188, #1189, #1190
The repository has been reorganised into:
Libraries/ - Spark.Engine, Spark.Engine.R4, Spark.Store.MongoDB
Applications/ - Spark.Web.R4, Spark.Web.STU3
Tests/ - unit tests + IntegrationTests/
4. Modernised .NET Target Framework Support
PRs: #842, #843, #898, #917, #1036
The targets netstandard2.0 and net472 have been dropped. Spark 3.0 targets
net8.0, net9.0, and net10.0. This allows the library to take advantage of
modern C# language features and runtime improvements throughout.
5. Upgraded to Hl7.Fhir SDK 6.1.1
PR: #1177
All projects have been upgraded to Firely SDK 6.1.1, including full API migration to the new SDK surface.
6. Experimental Background Indexing
A background indexing pipeline has been introduced as an opt-in feature. By setting
SparkSettings.Experimental.IndexingMode = Background, write operations enqueue
index work onto an IIndexQueue (backed by a MongoDB indexqueue collection)
instead of indexing synchronously in the HTTP request path.
New types:
IIndexQueue- queue interface (EnqueueAsync,ClaimNextAsync,AcknowledgeAsync,NackAsync)IndexWorker-BackgroundServicethat drains the queue viaIIndexServiceMongoIndexQueue- MongoDB implementationIndexQueueSettings- controlsLeaseTimeout,MaxAttempts,PollIntervalExperimentalSettings/IndexingModeenum onSparkSettings
7. FHIR Facade Decoupled from MVC
PR: #1262
The FHIR service layer can now be registered independently of MVC:
AddFhirFacadeCore(...)- registers the FHIR core services only (useful for minimal APIs or non-MVC hosts)AddFhirFacadeWithMvc(...)- full MVC pipeline (renamed fromAddFhirFacade)AddFhirWithMvc(...)- renamed fromAddFhirUseFhirWithMvc(...)- renamed fromUseFhir
8. Generic FhirResponse<T>
PR: #850
FhirResponse<T> wraps FhirResponse with a typed resource parameter, enabling
compile-time safety when working with specific FHIR resource types. Matching generic
overloads have been added to IFhirService and IFhirResponseFactory.
9. Enriched IFhirModel
PRs: #1033, #1046, #1048, #1171
Several members have been added to IFhirModel to reduce the surface area where
version-specific code needs to leak into shared code:
SupportedResources-IReadOnlyList<string>of all resource type namesFhirRelease- FHIR version string (e.g."4.0.1"), moved here fromSparkSettingsGetModelInspector()- returns theModelInspectorfor class-mapping lookupsGetTypeForFhirType(string)- maps FHIR type name → C# typeGetFhirTypeNameForType(Type)- maps C# type → FHIR type nameSearchParametersnow returnsList<Spark.Engine.Model.SearchParameter>
10. CapabilityStatement Builder Overhaul
The CapabilityStatement builder family has had an overhaul and are living in the
the shared Spark.Engine library. The builder API has been updated to use fluent
Func<TBuilder, TBuilder> delegates, and CapabilityStatementService now accepts
ServerVersion and FHIRVersion in its constructor. The static legacy builder in
Service.FhirServiceExtensions.CapabilityStatementBuilder has been removed.
11. ServerVersion Record
Commit: #44687ed3
SparkSettings.Version has changed from string to a new ServerVersion record
(Major, Minor, Patch, optional PreRelease). An implicit operator string
ensures backward-compatible use anywhere a string was expected.
12. Custom Search Parameters via SparkSettings
Commit: #5c755407
Custom search parameters must now be configured through
SparkSettings.CustomSearchParameters. The old AddCustomSearchParameters()
extension method has been removed.
13. New React + Vite Admin Frontend
PR: #1096
The ASP.NET MVC views, Entity Framework Core infrastructure, and ASP.NET Identity have been removed and replaced with a React + TypeScript + Vite single-page application. Authentication is now handled via GitHub OAuth, removing the need to manage a local user database.
The frontend is built automatically during dotnet build (MSBuild runs npm run build
inside app/), so no separate build step is required.
14. UUID v7 Identity Generation
PR: #1137
GuidIdentityGenerator now generates UUID v7 identifiers instead of random UUID v4.
UUID v7 is time-ordered, which improves MongoDB index locality and query performance
for sequential inserts.
15. Non-FHIR API Support
PR: #945
The engine can now coexist with custom non-FHIR endpoints on the same host. Previously, the FHIR routing and middleware could interfere with unrelated controllers. 3.0 provides a clean separation so that a mixed application hosting both FHIR resources and custom REST endpoints works correctly out of the box.
16. Improved Search Error Handling
- Malformed search parameters now produce an
OperationOutcomeentry in the response bundle instead of throwing an exception. - Unknown query parameters result in an
OperationOutcomeentry in the bundle (instead of silently ignoring them or failing).
17. _count=0 Treated as _summary=count
Sending _count=0 on a search request now correctly returns a count-only result
bundle, consistent with the FHIR specification's intent for _summary=count.
18. Weak ETag Support
PR: #1091
The server can now parse the weak ETag format (e.g. W/"123") in addition to the
strong ETag format.
19. Version-Guarded Conditional Search Index Writes
PR: #1108
When an out-of-order write is detected (e.g. a delayed re-index of an older resource version), the index store now guards against overwriting a newer index entry with an older one. This prevents stale data from corrupting search results during concurrent or background indexing scenarios.
20. Content-Type Header Handling Improvement
PR: #906
The engine now correctly handles Content-Type headers that include additional
parameters (e.g. application/fhir+json; charset=utf-8). Previously, extra
parameters could cause the content type to go unrecognised and result in a 415
Unsupported Media Type response.
21. Crash Fix: Contained Resources Without an Id
PR: #923
A crash that occurred when a resource contained more than one contained sub-resource
lacking an id element has been fixed. The server now handles this gracefully and
continues processing instead of throwing an unhandled exception.
22. Other Notable Fixes & Improvements
IfMatchVersionIdis now correctly propagated to theEntry.VersionConstraint(#1263)- Zero-length response body is guarded against
- Exceptions are always logged in the error handler (no silent swallowing)
Breaking Changes (Summary)
Consult the migrate from v2 to v3 guide for all breaking changes.
The headline breaking changes are:
- Removed legacy .NET targets (
netstandard2.0,net472) AddFhirFacade→AddFhirFacadeWithMvcAddFhir→AddFhirWithMvcUseFhir→UseFhirWithMvcSpark.Mongo.R4package →Spark.Mongo- Many
IFhirModelmethod signatures changed to useSpark.Engine.Model.SearchParameterinstead ofHl7.Fhir.Model.SearchParameter - Namespace consolidation - many types moved to
Spark.Engine.*namespaces SparkSettings.FhirReleaseremoved - useIFhirModel.FhirRelease- Legacy formatter infrastructure removed (
BinaryFormatter,FhirMediaTypeFormatter, etc.) Snapshotsealed - many previously public members are nowprivate
Features Backported to v2 (v2-r4/master and v2-stu3/master)
The following improvements were made available to the v2 line without requiring
an upgrade to v3. Both v2-r4/master and v2-stu3/master.
| Feature | Released as |
|---|---|
| Non-FHIR API support alongside the FHIR server | v2.3.5 |
Crash fix: contained resources without an id element |
v2.3.5 |
| SignalR Maintenance Hub requires authorization | v2.3.5 |
| Weak ETag parsing | v2.4.0 |
| React + Vite frontend, GitHub OAuth, removal of legacy MVC/Identity/EF Core | v2.4.0 |
| Target .NET 10.0 (alongside net8 / net9) | v2.4.0 |
UUID v7 in GuidIdentityGenerator |
v2.4.0 |
_count=0 treated as _summary=count |
v2.4.0 |
| Optimised Docker Dockerfile layer caching | v2.4.0 |
OperationOutcome entry in bundle for unknown query parameters |
v2.4.1 |
IfMatchVersionId propagated to Entry.VersionConstraint |
v2.5.0 |
ParseVersionFromETag() extraction |
v2.5.0 |
Malformed search parameters → OperationOutcome entry instead of exception |
v2.5.0 |