Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ A collection of examples on top of Aidbox FHIR platform
- [BedaEMR integration](aidbox-integrations/BedaEmr/)
- [FHIR Analytics](aidbox-integrations/fhir-analytics/)
- [Real-Time Analytics with Aidbox, ClickHouse, and Superset](aidbox-integrations/clickhouse-superset/)
- [GraphQL Federation](aidbox-integrations/apollo-graphql-federation/)

## Aidbox Features

Expand Down
145 changes: 145 additions & 0 deletions aidbox-integrations/apollo-graphql-federation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
---
features: [Graphql, Federation, Apollo]
languages: [YAML, JavaScript]
---
# Apollo Federation with Aidbox

Access Aidbox's FHIR GraphQL API through an Apollo Gateway via [Apollo Federation v2](https://www.apollographql.com/docs/federation/).

## Overview

Apollo Federation allows you to compose multiple GraphQL services (subgraphs) into a single unified API (supergraph).
With Aidbox's federation support enabled, Aidbox exposes its FHIR GraphQL schema as a federation-compatible subgraph, which can be consumed by Apollo Gateway.

## Prerequisites

1. Docker
2. Cloned repository: [Github: Aidbox/examples](https://github.com/Aidbox/examples/tree/main)
3. Working directory: `apollo-graphql-federation`
4. Aidbox license key

```
git clone https://github.com/Aidbox/examples.git
cd examples/aidbox-integrations/apollo-graphql-federation
```

## Run Aidbox and Apollo gateway

```bash
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets make it in a modern way, for example like here

2. **Initialize Aidbox instance**

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried this, but it can't automatically start Apollo with this approach. Apollo starts, tries to access Aidbox, but since it's not initialized, it fails. So you'll have to restart Apollo manually, instead of just doing docker compose up.

AIDBOX_LICENSE="your aidbox license key" docker compose up -d
```

## How Federation Works

When federation is enabled (`BOX_MODULE_GRAPHQL_FEDERATION_SUPPORT=true`), Aidbox:

1. **Adds Federation 2 directives** to the GraphQL schema:
- `@link` - Declares this as a Federation 2 schema
- `@key(fields: "id")` - Marks FHIR resource types as entities
- `@shareable` - Allows types to be resolved by multiple subgraphs

2. **Exposes `_service` query** that returns the GraphQL SDL with federation directives

3. **Implements `_entities` resolver** for resolving entity references

## Usage

### Creating Test Data

Create a few patients in Aidbox using the REST Console:

{% code title="Request" %}
```http
PUT /fhir/Patient/pt-1
content-type: text/yaml
accept: text/yaml

id: pt-1
name:
- family: Smith
given: [John]
birthDate: '1990-01-15'
```
{% endcode %}

{% code title="Request" %}
```http
PUT /fhir/Patient/pt-2
content-type: text/yaml
accept: text/yaml

id: pt-2
name:
- family: Johnson
given: [Jane]
birthDate: '1985-06-20'
```
{% endcode %}

### Querying via Apollo Gateway

You can query Aidbox through the Apollo Gateway at `http://localhost:4000`.
You can also open this URL in a browser to access Apollo Sandbox - an interactive GraphQL IDE for exploring the schema and running queries.

#### Query a Single Patient

{% code title="Request" %}
```bash
curl -s -X POST http://localhost:4000 \
-H "Content-Type: application/json" \
-u "root:secret" \
-d '{"query": "{ Patient(id: \"pt-1\") { id name { family given } birthDate } }"}'
```
{% edncode %}

{% code title="Response" %}
``` json
{
"data": {
"Patient": {
"id": "pt-1",
"name": [{
"family": "Smith",
"given": ["John"]
}],
"birthDate": "1990-01-15"
}
}
}
```
{% endcode %}

#### Query All Patients

{% code title="Request" %}
```bash
curl -s -X POST http://localhost:4000 \
-H "Content-Type: application/json" \
-u "root:secret" \
-d '{"query": "{ PatientList { id name { family given } birthDate } }"}'
```
{% endcode %}

{% code title="Response" %}
```json
{
"data": {
"PatientList": [{
"id": "pt-1",
"name": [{
"family": "Smith",
"given": ["John"]
}],
"birthDate": "1990-01-15"
}, {
"id": "pt-2",
"name": [{
"family": "Johnson",
"given": ["Jane"]
}],
"birthDate": "1985-06-20"
}]
}
}
```
{% endcode %}
75 changes: 75 additions & 0 deletions aidbox-integrations/apollo-graphql-federation/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
volumes:
postgres_data: {}
services:
postgres:
image: docker.io/library/postgres:18
volumes:
- postgres_data:/var/lib/postgresql/18/docker:delegated
command:
- postgres
- -c
- shared_preload_libraries=pg_stat_statements
environment:
POSTGRES_USER: aidbox
POSTGRES_PORT: '5432'
POSTGRES_DB: aidbox
POSTGRES_PASSWORD: postgres

aidbox:
image: docker.io/healthsamurai/aidboxone:edge
pull_policy: always
depends_on:
- postgres
ports:
- "8080:8080"
environment:
BOX_MODULE_GRAPHQL_FEDERATION_SUPPORT: "true"
AIDBOX_LICENSE: "$AIDBOX_LICENSE"
BOX_ADMIN_PASSWORD: password
BOX_BOOTSTRAP_FHIR_PACKAGES: hl7.fhir.r4.core#4.0.1
BOX_COMPATIBILITY_VALIDATION_JSON__SCHEMA_REGEX: '#{:fhir-datetime}'
BOX_DB_DATABASE: aidbox
BOX_DB_HOST: postgres
BOX_DB_PASSWORD: postgres
BOX_DB_PORT: '5432'
BOX_DB_USER: aidbox
BOX_FHIR_BUNDLE_EXECUTION_VALIDATION_MODE: limited
BOX_FHIR_COMPLIANT_MODE: 'true'
BOX_FHIR_CORRECT_AIDBOX_FORMAT: 'true'
BOX_FHIR_CREATEDAT_URL: https://aidbox.app/ex/createdAt
BOX_FHIR_SCHEMA_VALIDATION: 'true'
BOX_FHIR_SEARCH_AUTHORIZE_INLINE_REQUESTS: 'true'
BOX_FHIR_SEARCH_CHAIN_SUBSELECT: 'true'
BOX_FHIR_SEARCH_COMPARISONS: 'true'
BOX_FHIR_TERMINOLOGY_ENGINE: hybrid
BOX_FHIR_TERMINOLOGY_ENGINE_HYBRID_EXTERNAL_TX_SERVER: https://tx.health-samurai.io/fhir
BOX_FHIR_TERMINOLOGY_SERVICE_BASE_URL: https://tx.health-samurai.io/fhir
BOX_MODULE_SDC_STRICT_ACCESS_CONTROL: 'true'
BOX_ROOT_CLIENT_SECRET: secret
BOX_SEARCH_INCLUDE_CONFORMANT: 'true'
BOX_SECURITY_AUDIT_LOG_ENABLED: 'true'
BOX_SECURITY_DEV_MODE: 'true'
BOX_SETTINGS_MODE: read-write
BOX_WEB_BASE_URL: http://localhost:8080
BOX_WEB_PORT: 8080
healthcheck:
test: curl -f http://localhost:8080/health || exit 1
interval: 5s
timeout: 5s
retries: 90
start_period: 30s

apollo-gateway:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use a Dockerfile here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we use custom index.js to set everything up, so I'm not sure

image: node:20-alpine
ports:
- "4000:4000"
working_dir: /app
volumes:
- ./gateway:/app
command: sh -c "npm install && node index.js"
environment:
AIDBOX_URL: http://aidbox:8080/$$graphql
AIDBOX_ROOT_SECRET: secret
depends_on:
aidbox:
condition: service_healthy
28 changes: 28 additions & 0 deletions aidbox-integrations/apollo-graphql-federation/gateway/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import { ApolloGateway, IntrospectAndCompose, RemoteGraphQLDataSource } from '@apollo/gateway';

const authHeader = 'Basic ' + Buffer.from(`root:${process.env.AIDBOX_ROOT_SECRET}`).toString('base64');

class AuthenticatedDataSource extends RemoteGraphQLDataSource {
willSendRequest({ request }) {
request.http.headers.set('Authorization', authHeader);
}
}

const gateway = new ApolloGateway({
supergraphSdl: new IntrospectAndCompose({
subgraphs: [{ name: 'aidbox', url: process.env.AIDBOX_URL }],
}),
buildService({ url }) {
return new AuthenticatedDataSource({ url });
},
});

const server = new ApolloServer({ gateway });

const { url } = await startStandaloneServer(server, {
listen: { port: 4000, host: '0.0.0.0' },
});

console.log(`Apollo Gateway ready at ${url}`);
10 changes: 10 additions & 0 deletions aidbox-integrations/apollo-graphql-federation/gateway/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "apollo-gateway-aidbox",
"version": "1.0.0",
"type": "module",
"dependencies": {
"@apollo/gateway": "^2.9.3",
"@apollo/server": "^4.11.3",
"graphql": "^16.9.0"
}
}