SQL on FHIR WG Meetings

Gino Canessa on how to describe an operation, and where that approach quietly stops working — Mar 25, 2025

Gino Canessa
Gino Canessa
Principal Software Engineer at Microsoft
Nikolai Ryzhikov
Nikolai Ryzhikov
CTO at Health Samurai
Arjun Sanyal
Arjun Sanyal
Principal Antidote Solutions
John Grimes
John Grimes
Principal Research Consultant CSIRO
Mar 25, 2025

Describing the export operation — profile the Parameters resource

Gino Canessa
Gino Canessa
Principal Software Engineer at Microsoft

Profile the Parameters resource. The information mirrors OperationDefinition almost exactly, but you can't validate against an OperationDefinition and developers get no example to copy, so they ask for a profile anyway. And only the profile can carry an invariant like "you can't supply both a URL and a canonical".

The R6 build has added inputProfile and outputProfile for exactly this; older versions need an extension.

Gino Canessa
Gino Canessa
Principal Software Engineer at Microsoft

If you could go back in time, OperationDefinition's parameter block would just be a Parameters you could profile. But it went normative, so all we could add was a slot for one.

Returning CSV or Parquet — nothing in FHIR describes that cleanly

Gino's route is to declare the return type as Binary and let content negotiation do the work.

Nikolai said the spec is a little bit lying about GraphQL — everyone returns plain GraphQL JSON rather than a Binary wrapper, because otherwise GraphQL clients break — and Gino agreed nobody is expected to ever actually hand back a Binary resource.

John came round after reading the Binary page, which has a whole content-negotiation section, and made the point that settled it: you never have to implement Binary storage, ids or lifecycle, you're only using the type to describe a response.

They agreed to file a ticket for a better way to say it.

Logical model for the response — floated, pushed back

Gino floated defining a logical model for the response instead — a view run response with the shape you actually want, rather than a Parameters full of names and parts — which fits R6's push to treat logical models as first class.

Nikolai Ryzhikov
Nikolai Ryzhikov
CTO at Health Samurai

Parameters is trivial to write helpers against, needs no generated types, and stays open when an implementation wants to add a field. A logical model sounds like overkill.

Gino's compromise: from an OperationDefinition, generating either a profile or a logical model should be a trivial transform, and he made himself a note because he doesn't know why the tool doesn't already exist.

Async export — a Parameters body that grows with the job

Nikolai proposed that an async export return a Parameters body alongside the 202 and the Location header — the export id, an echo of what was requested, maybe an estimate — and that the same shape simply grow as the job runs: status, progress, partial outputs, and finally the outputs themselves.

John pointed out this is the FHIR async pattern, which in Pathling he implemented once generically for every operation via the respond-async header.

On progress he was sceptical: today it's an unstructured string and it's hard to make it more, because progress is implementation-specific — Spark counts jobs it knows about against jobs finished, another engine has nothing to report — and a string is only good for displaying.

Nikolai's answer: as Parameters it can be a percentage, an estimated finish, the views already done, the rows processed. Gino added that all of it stays optional, so a client copes with a server that says nothing at all.

Advertising ViewDefinition in a capability statement

Gino's advice: accept being on the bleeding edge of what R6 is still defining. Pick an approach, try it, and keep track of what works and what doesn't so it can steer the spec.

When Nikolai said they'd be backward compatible forever so they wouldn't need a version on it, Gino's reply was: sure, but everyone's not using Clojure.