NCHS is working to modernize the national collection and exchange of mortality data by developing and deploying new Application Programming Interfaces (APIs) for data exchange, implementing modern standards health like HL7's Fast Healthcare Interoperability Resources (FHIR), and improving overall systems and processes. This repository provides a reference implementation and documentation describing the Reference Client, which supports the client side exchange of mortality data between vital records jurisdictions and NCHS. For the server side implementation, see NVSS API server.
The Reference Client is an example implementation for jurisdications that handles submitting VRDR FHIR Messages, reliable delivery (acknowledgements and retries), and retrieving message responses from the NVSS API server. The implementation follows the FHIR Messaging IG. The reference implementation is developed for .NET using C# and leverages the vrdr-dotnet library to create and parse FHIR Messages. This implementation uses the VRDR.Client library to make calls to the NVSS API server. If you are looking to build your own client, see the VRDR.Client library for basic library functions to authenticate, post records, and retrieve responses from the NVSS API server.
The client is responsible for interacting with the NVSS API server so jurisdictions do not need to duplicate work when building their own client. The client abstracts away the reliable message delivery and retrieval between the client and NVSS API server. It provides its own API interface to jurisdictions to simplify their workflow.
This client can be used to test and demo messaging between the client and the NVSS API Server using the FHIR Messaging IG format. It can also be used as a reference for jurisdictions building their own client implementation.
The client implementation runs a service that submits messages to the NVSS API and retrieves responses to make available to jurisdictions. The service handles reliable delivery, resends, and parsing so jurisdictions do not need to implement a custom service. The service sits between the jurisdiction's system and the NVSS API Server.
Example workflow
- The VRDR reporter sends a JSON vrdr record via
POST
to the client's/record/submission
,/record/update
, or/record/void
endpoint - Upon receipt, the client converts the json to a VRDR record, wraps it in a FHIR Message and inserts it in the
MessageItem
table - The client's
TimedService
pulls new messages from theMessageItem
table every X-configured seconds and POSTs the message to NVSS API Server - Next, the
TimedService
makes aGET
request for any new messages from the NVSS API Server - The
TimedService
parses the response messages and stores them in theResponseItems
table- If there was an acknowledgement or error, it updates the corresponding message in the
MessageItem
table with the new message status
- If there was an acknowledgement or error, it updates the corresponding message in the
- Finally, the
TimedService
checks for any messages that have not received an acknowledgement in Y-configured seconds and resubmits them- The TimedService runs these three steps in sequence every X-configured seconds
- The frequencies of X and Y are configurable
- The VRDR reporter sends a
GET
request to the/record/{deathYear}/{jurisdictionId}/{certNo}
endpoint at any time to get the status of the message with the provided business identifiers for death year, jurisdiction id, and certificate number
Note that if a submission message was sent, followed by an update message with the same business identifiers, the returned status will be for the latest message inserted into the database, in this case the update message status.
The client implementation has endpoints to submit VRDR records, update records, and void records. It also has an endpoint to retrieve the status and response of a given record.
POST /record/submission
- Parameters: The
POST /record/submission
endpoint accepts a VRDR record as json - Function: Wraps the record in a FHIR Submission message and queues the message to be sent to the NVSS API Server
- Response: A successful request returns
204 No Content
- Parameters: The
POST /record/submissions
- Parameters: The
POST /record/submissions
endpoint accepts a list of VRDR records as json - Function: Wraps each record in a FHIR Submission message and queues the message to be sent to the NVSS API Server
- Response: A successful request returns
204 No Content
- Parameters: The
POST /record/update
- Parameters: The
POST /record/update
endpoint accepts a VRDR record as json - Function: Wraps the record in a FHIR Update message and queues the message to be sent to the NVSS API Server
- Response: A successful request returns
204 No Content
- Parameters: The
POST /record/updates
- Parameters: The
POST /record/updates
endpoint accepts a list of VRDR records as json - Function: Wraps each record in a FHIR Update message and queues the message to be sent to the NVSS API Server
- Response: A successful request returns
204 No Content
- Parameters: The
POST /record/void
- Parameters: The
POST /record/void
endpoint accepts a VRDR record as json and ablock_count
parameter - Function: Wraps the record in a FHIR Void message, sets the block count, and queues the message to be sent to the NVSS API Server
- Response: A successful request returns
204 No Content
- Parameters: The
POST /record/voids
- Parameters: The
POST /record/voids
endpoint accepts a list of VRDR records and associatedblock_count
as json - Function: Wraps each record in a FHIR Void message, sets the block count, and queues the message to be sent to the NVSS API Server
- Response: A successful request returns
204 No Content
- Parameters: The
POST /record/alias
- Parameters: The
POST /record/alias
endpoint accepts a VRDR record as json as well as the following parametersalias_decedent_first_name
alias_decedent_last_name
alias_decedent_name_suffix
alias_father_surname
alias_social_security_number
- Function: Wraps the record in a FHIR Alias message, sets the alias values, and queues the message to be sent to the NVSS API Server
- Response: A successful request returns
204 No Content
- Parameters: The
POST /record/aliases
- Parameters: The
POST /record/aliases
endpoint accepts a list of VRDR records and associated parameters below as jsonalias_decedent_first_name
alias_decedent_last_name
alias_decedent_name_suffix
alias_father_surname
alias_social_security_number
- Function: Wraps each record in a FHIR Alias message, sets the alias parameters, and queues the message to be sent to the NVSS API Server
- Response: A successful request returns
204 No Content
- Parameters: The
GET /record/{deathYear}/{jurisdictionId}/{certNo}
- Parameters:
- deathYear: the year of death in the VRDR record
- jurisditionId: the jurisdiction Id in the VRDR record
- certNo: the 6 digit certificate number in the VRDR record
- Function: Retrieves the most recent MessageItem with business identifiers that match the provided parameters
- Response: A successful request returns
200 OK
and a JSON object with the MessageItem and it's Extraction Error or Coded Response if available
- Parameters:
The client implementation can run with a local development setup where all services are run locally, or an integrated development setup that connects to the development NVSS API Server.
This project uses dotnet and docker to run the local database.
- Set up the database docker containers
- Run
docker-compose up --build
to initialize the client db (postgres) - From the reference-client-api/nvssclient directory, run
dotnet ef database update
to intialize the client's db
- Run
- Configure the client implementation to connect to the development NVSS API Server
- Create an
appsettings.json
file from theappsettings.json.sample
file - In
appsettings.json
set"LocalTesting" : false
- In
appsettings.json
set"AuthServer": "https://apigw.cdc.gov/auth/oauth/v2/token"
- In
appsettings.json
set"AuthServer": "https://apigw.cdc.gov/OSELS/NCHS/NVSSFHIRAPI/<jurisidction-id>/Bundles"
- In
appsettings.json
fill out the"Authentication"
section to authenticate to the server via oauth, contact admin for your credentials - In
appsettings.json
set"ResendInterval"
to your desired interval. The recommended length in production is 4 hours, or 14400 seconds. If you are testing the resend implementation then you may want to set it to a shorter interval like 10 seconds to see results quickly. - In
appsettings.json
set"PollingInterval"
to the frequency you want to poll for new responses. In production, 1 hour may be a good interval to use, but for testing an interval like 30 seconds may be better for seeing results quickly.
- Create an
- Set up OAuth
- OAuth is required to authenticate to the NVSS API Server. Contact the NVSS server team to acquire your client id, client secret, username, and password.
- In
appsettings.json
set"ClientId": "<your-client-id>"
- In
appsettings.json
set"ClientSecret": "<your-client-secret>"
- In
appsettings.json
set"Username": "<your-username>"
- In
appsettings.json
set"Password": "<your-password>"
- Once the client db is running and the configuration is complete, go to the reference-client-api/nvssclient project directory and run
dotnet run
- Set up the database docker containers
- Run
docker-compose up --build
to initialize the client db (postgres) and the NVSS API Server db (mssql) - From the reference-client-api/nvssclient directory, run
dotnet ef database update
to intialize the client's db
- Run
- Download the reference-nchs-api code from https://gitlab.mitre.org/nightingale/reference-nchs-api
- make sure the db password in appsettings.json matches the one set in the docker compose file
- from the reference-nchs-api/messaging project directory run
dotnet ef database update
to initialize the NVSS API Server's db - from the reference-nchs-api directory run the NVSS API Server with
dotnet run --project messaging
- Configure the client implementation to connect to the local NVSS API Server
- Create an
appsettings.json
file from theappsettings.json.sample
file - In
appsettings.json
set"ClientDatabase"
to your database connection string - In
appsettings.json
set"LocalTesting" : true
- In
appsettings.json
set"LocalServer":"https://localhost:5001/bundles"
- In
appsettings.json
set"ResendInterval"
to your desired interval. The recommended length in production is 4 hours, or 14400 seconds. If you are testing the resend implementation then you may want to set it to a shorter interval like 10 seconds to see results quickly. - In
appsettings.json
set"PollingInterval"
to the frequency you want to poll for new responses. In production, 1 hour may be a good interval to use, but for testing an interval like 30 seconds may be better for seeing results quickly.
- Create an
- Now that the client db, NVSS API Server db, and the NVSS API Server are all up and running, go to the reference-client-api/nvssclient project directory and run
dotnet run
- Create a FHIR Record. The standard that specifies this format can be found here. There are also two public library implementations available to assist in the creation of FHIR Records, VRDR-dotnet and VRDR_javalib.
- Submit the record using a POST request to the
/record/submission
endpoint. The following example demonstrates how to make the request using curl:
curl --location --request POST 'http://localhost:4300/record/submission' \
--header 'Content-Type: application/json' \
--data "@path/to/file.json"
The API will return a 204 No Content HTTP response if the request was successful.
- After submitting a record, use the
GET /record/{deathYear}/{jurisdictionId}/{certNo}
endpoint to check the status of the message and the coded response if available. The json response will include the status of the most recent message with the provided business identifiersdeathYear
,jurisdictionId
, andcertNo
. The following example demonstrates how to make the request using curl:
curl http://localhost:4300/record/status/2018/MA/001
The API will return a json response if the request was successful. Example Response:
{
"message": {
"id": 4,
"uid": "bd734a03-8a5c-4ee7-a994-779f58f4433f",
"stateAuxiliaryIdentifier": "42",
"certificateNumber": 1,
"deathJurisdictionID": "MA",
"deathYear": 2018,
"message": "{\"resourceType\":\"Bundle\",\"id\":\"b443f717-96cc-4f72-8e62-06119d54111d\",\"type\":\"message\",\"timestamp\":\"2021-12-13T10:51:32.770941-05:00\",\"entry\":[{\"fullUrl\":\"urn:uuid:bd734a03-8a5c-4ee7-a994-779f58f4433f\",\"resource\":{\"resourceType\":\"MessageHeader\",\"id\":\"bd734a03-8a5c-4ee7-a994-779f58f4433f\",\"eventUri\":\"http://nchs.cdc.gov/vrdr_submission\",\"destination\":[{\"endpoint\":\"http://nchs.cdc.gov/vrdr_submission\"}],\"source\":{\"endpoint\":\"https://example.com/jurisdiction/message/endpoint\"},\"focus\":[{\"reference\":\"urn:uuid:27c1f73d-59a7-4c52-bb20-7073c1778535\"}]}},{\"fullUrl\":\"urn:uuid:d1911214-a3fc-452f-bb9d-91d4b38d96c6\",\"resource\":{\"resourceType\":\"Parameters\",\"id\":\"d1911214-a3fc-452f-bb9d-91d4b38d96c6\",\"parameter\":[{\"name\":\"cert_no\",\"valueUnsignedInt\":1},{\"name\":\"state_auxiliary_id\",\"valueString\":\"42\"},{\"name\":\"death_year\",\"valueUnsignedInt\":2018},{\"name\":\"jurisdiction_id\",\"valueString\":\"MA\"}]}},
...
,{\"value\":\"42\"}],\"status\":\"current\",\"type\":{\"coding\":[{\"system\":\"http://loinc.org\",\"code\":\"64297-5\",\"display\":\"Death certificate\"}]},\"date\":\"2020-08-07T16:41:57.698163-04:00\",\"author\":[{\"reference\":\"urn:uuid:7afbe14f-932f-4639-966a-84c4e9d2f7c6\"}],\"content\":[{\"attachment\":{\"url\":\"urn:uuid:cf96ffd9-fb38-479f-ba3c-5f3f36752fdb\"}}],\"context\":{\"related\":[{\"reference\":\"urn:uuid:cf96ffd9-fb38-479f-ba3c-5f3f36752fdb\"}]}}}]}}]}",
"retries": 0,
"status": "Acknowledged",
"expirationDate": "2021-12-13T10:52:09.439643",
"createdDate": "2021-12-13T10:51:32.781559",
"updatedDate": "2021-12-13T10:51:49.376313"
},
"response": null
}
This section provides useful information to developers working on the client implementation or referring to it as a reference for their own custom implementation.
- When applying a new migration, update the Models to reflect the desired changes.
- Stop and remove the container running the current client db
- Run
docker-compose up --build
to recreate the db- You may need to comment out this block in Program.cs
// .ConfigureServices((hostContext, services) => // { // services.AddHostedService<TimedHostedService>(); // }); ```
- Run
dotnet ef migrations add <Your-Migration-Name>
to create the new migration - Run
dotnet ef database update
to update the db schema
- Tests are located in
nvssclient.tests
cd
into the nvssclient.tests folder- Run
dotnet test
- To persist data and make it available to the Jurisidction upon request, the full response message is stored in the ResponseItems table, rather than just the ID in a Message Log. The ResponseItem table serves as the Message Log, see FHIR Messaging IG
- The
POST /record
end point does not return data because the submission and coding process takes too long to provide a synchronous response. The user can request the message status via theGET /record
endpoint.
Down the road tasks and questions
- Existing issue when applying new migrations requires temporarily commenting out the TimedService
- Need to consider the state jurisdictions will keep track of, ex if using a native format, there may be two versions of the same record
- Consider a potential adapter for handling different formats
- Request feedback on the status endpoint, should messages be cleared out over time