Versioning breaking changes for custom operations

When you have defined a custom REST operation, what is the recommended approach when you have to make changes that break backward compatibility? Is it best to just define a whole new operation, for example if you have $my-operation then define a new one like $my-operation-2? Or is there a way to keep the same operation name and indicate which version should be used through some other means?

1 Like

I would suggest using the MIME Type parameter as outlined in the documentation for managing multiple FHIR Versions.

Basically, you should be able to route a request based on the value of that parameter to the correct code for either the old / new version of the call you want to support.

Ideally you should make the version required in the Accept header and reject requests without it (so that it is never ambiguous which FHIR version a client is requesting). If this is not possible, you should assume requests without a version are requesting the oldest version you currently support. This will keep existing clients that do not add a version from breaking when you start checking the Accept header.

If you support 3 and 4, assume requests without a version are 3. Clients that want R4 must send you an Accept header specifying so. At some point when you support R5, then clients will have to explicitly change from requesting R4 to R5 to get the new functionality.

Sorry the question wasn’t clear. I wasn’t referring to different FHIR versions. This is all within a single FHIR version. I’m just asking how to best handle breaking changes for a custom operation.

What level of control do you have over clients? Basically, I would handle this on a case-by-case basis depending on how critical your application is. If you aren’t discussing major fhir versions, then it becomes a generic software engineering question regarding breaking changes and versioning.

I generally attempt to follow Semantic versioning. So consumers will know if they are used to communicating with a server with version 2.2.1 that they should not expect it to go ok if they start talking to a server with version 3.1.0. In the case I am using the Accept header for fhir versions, I will require a custom header that serves the same purpose but for my own versioning scheme (perhaps Accept-Version?).

We need to be able to support third-party clients. I would prefer to work within the FHIR standard rather than introduce a proprietary HTTP header.

The meaning of a REST operation isn’t determined by the operation name in the URL, it’s determined by the canonical URL associated with the operation name in the server’s CapabilityStatement. Different servers can use the same name for completely different operations and that’s fine so long as the CapabilityStatement makes clear what OperationDefinition the name is associated with.

That said, you should probably verify that your clients are actually inspecting the CapabilityStatement they way they’re supposed to.

1 Like

The FHIR Standard doesn’t say anything about your underlying implementation of an endpoint changing, though. If you are talking about your API changing (What parameters you accept, what the return type of a specific endpoint it), then the CapabilityStatement is probably a good place to start. However, you can still change the behavior of and endpoint, and the CapabilityStatement to match it, AND break your third-party clients who probably can’t deal with those changes.

Also, if you are talking about the API remaining constant but the behavior of your underlying endpoint changing (for example, you used to accept contained Medication resources in MedicationRequest$create but now you expect a reference instead) - there is not going to be a way to communicate this change via the FHIR spec. Like I said earlier, this is a question that revolves around how you version your service (independent of the FHIR version). Your clients need to have a stable version to use that is not changing, or they need to agree with you on what changes are made and when. The easiest way to deal with this from my perspective has traditionally been (like I specified above) the Accept header including the version of the API a client expects. Unfortunately the FHIR spec uses this to determine major FHIR release version, so I suggested adding a custom one to take its place. Alternatively, yes, you could create new endpoints for every change in functionality - this may become unmanageable if you have a dozen clients asking for different things and a few calls that have multiple versions.