>Welcome to MedictReferenceTypeScript SDK

TypeScript SDK

Complete reference for the Medict TypeScript SDK.

Installation

npm install @medplum/core @medplum/react

MedictClient

The main client class for interacting with the Medict API.

Constructor

import { MedictClient } from '@medplum/core';

const client = new MedictClient({
  baseUrl?: string;
  clientId?: string;
  clientSecret?: string;
  redirectUri?: string;
  cache?: CacheConfig;
  storage?: Storage;
  onUnauthenticated?: () => void;
});

Configuration Options

| Option | Type | Description | |--------|------|-------------| | baseUrl | string | Medict API base URL (default: 'https://api.medplum.com') | | clientId | string | OAuth client ID | | clientSecret | string | OAuth client secret (for server-side apps) | | redirectUri | string | OAuth redirect URI | | cache | CacheConfig | Cache configuration | | storage | Storage | Custom storage implementation | | onUnauthenticated | () => void | Callback for authentication errors |

Authentication

OAuth 2.0 Flow

// Sign in with redirect
await client.signInWithRedirect({
  clientId: 'your-client-id',
  redirectUri: 'https://your-app.com/callback',
  scope: 'openid profile email'
});

// Handle OAuth callback
const profile = await client.handleCodeExchange();

// Sign out
await client.signOut();

Client Credentials

// Server-to-server authentication
await client.authenticateWithClientCredentials();

Token Management

// Get current access token
const token = client.getAccessToken();

// Refresh token
const newToken = await client.refreshToken();

// Check if token is valid
const isValid = client.isTokenValid();

Resource Operations

Create Resource

const patient = await client.createResource({
  resourceType: 'Patient',
  name: [{
    use: 'official',
    family: 'Smith',
    given: ['John']
  }],
  gender: 'male',
  birthDate: '1990-01-01'
});

Read Resource

// Read single resource
const patient = await client.readResource('Patient', 'patient-id');

// Read multiple resources
const patients = await client.readResources([
  'Patient/patient-1',
  'Patient/patient-2'
]);

Update Resource

const updatedPatient = await client.updateResource({
  ...patient,
  name: [{
    use: 'official',
    family: 'Smith',
    given: ['John', 'Michael']
  }]
});

Delete Resource

// Delete single resource
await client.deleteResource('Patient', 'patient-id');

// Delete multiple resources
await client.deleteResources([
  'Patient/patient-1',
  'Patient/patient-2'
]);

Search Operations

// Search for patients by name
const patients = await client.searchResources('Patient', {
  name: 'Smith'
});

// Search with multiple parameters
const encounters = await client.searchResources('Encounter', {
  patient: 'Patient/patient-id',
  status: 'finished',
  date: 'ge2023-01-01'
});
// Search with complex queries
const observations = await client.searchResources('Observation', {
  patient: 'Patient/patient-id',
  category: 'vital-signs',
  'value-quantity': 'gt100',
  'value-quantity': 'lt200',
  _sort: 'date',
  _count: 10
});

// Search with includes
const encountersWithPatient = await client.searchResources('Encounter', {
  status: 'finished'
}, {
  _include: ['Encounter:patient']
});

Search Iterator

// Iterate through search results
for await (const patient of client.searchResourceIterator('Patient', {
  name: 'Smith'
})) {
  console.log(patient);
}

Batch Operations

Execute Batch

const batchResponse = await client.executeBatch({
  resourceType: 'Bundle',
  type: 'batch',
  entry: [
    {
      request: {
        method: 'POST',
        url: 'Patient'
      },
      resource: {
        resourceType: 'Patient',
        name: [{ family: 'Smith', given: ['John'] }]
      }
    }
  ]
});

Execute Transaction

const transaction = await client.executeTransaction({
  resourceType: 'Bundle',
  type: 'transaction',
  entry: [
    {
      request: {
        method: 'POST',
        url: 'Patient'
      },
      resource: {
        resourceType: 'Patient',
        name: [{ family: 'Smith', given: ['John'] }]
      }
    }
  ]
});

History and Versioning

Resource History

// Get history for a specific resource
const history = await client.readHistory('Patient', 'patient-id');

// Get history for a resource type
const patientHistory = await client.readHistory('Patient');

// Get history with parameters
const recentHistory = await client.readHistory('Patient', 'patient-id', {
  _count: 10,
  _since: '2023-01-01T00:00:00Z'
});

Version Management

// Read a specific version
const version = await client.readVersion('Patient', 'patient-id', 'version-1');

// Compare versions
const differences = await client.compareVersions(
  'Patient',
  'patient-id',
  'version-1',
  'version-2'
);

Custom Requests

HTTP Requests

// GET request
const response = await client.request('GET', '/fhir/Patient');

// POST request
const response = await client.request('POST', '/fhir/Patient', {
  name: [{ family: 'Smith', given: ['John'] }]
});

// PUT request
const response = await client.request('PUT', '/fhir/Patient/patient-id', {
  id: 'patient-id',
  name: [{ family: 'Smith', given: ['John'] }]
});

// DELETE request
const response = await client.request('DELETE', '/fhir/Patient/patient-id');

Request Options

const response = await client.request('GET', '/fhir/Patient', undefined, {
  headers: {
    'Custom-Header': 'value'
  },
  timeout: 5000
});

Error Handling

Error Types

try {
  const patient = await client.readResource('Patient', 'invalid-id');
} catch (error) {
  if (error instanceof MedictError) {
    switch (error.code) {
      case 'not-found':
        console.log('Resource not found');
        break;
      case 'unauthorized':
        console.log('Authentication required');
        break;
      case 'forbidden':
        console.log('Access denied');
        break;
      default:
        console.log('Error:', error.message);
    }
  }
}

Retry Logic

async function withRetry<T>(operation: () => Promise<T>, maxRetries = 3): Promise<T> {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await operation();
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      
      if (error.code === 'rate-limit') {
        await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
      } else {
        throw error;
      }
    }
  }
}

Caching

Cache Configuration

const client = new MedictClient({
  baseUrl: 'https://api.medplum.com',
  clientId: 'your-client-id',
  cache: {
    enabled: true,
    ttl: 300000, // 5 minutes
    maxSize: 1000
  }
});

Cache Operations

// Cache a resource
await client.cacheResource('Patient', 'patient-id');

// Clear cache
await client.clearCache();

// Get cached resource
const cached = client.getCachedResource('Patient', 'patient-id');

Events

Event Listeners

// Listen for authentication events
client.on('authenticate', (profile) => {
  console.log('User authenticated:', profile);
});

client.on('logout', () => {
  console.log('User logged out');
});

// Listen for token refresh
client.on('tokenRefresh', (token) => {
  console.log('Token refreshed:', token);
});

React Integration

MedictProvider

import { MedictProvider } from '@medplum/react';

function App() {
  return (
    <MedictProvider medplum={client}>
      <YourApp />
    </MedictProvider>
  );
}

React Hooks

import { useMedict, useResource, useSearchResources } from '@medplum/react';

function PatientComponent({ patientId }: { patientId: string }) {
  const medplum = useMedict();
  const patient = useResource('Patient', patientId);
  const encounters = useSearchResources('Encounter', {
    patient: `Patient/${patientId}`
  });

  if (!patient) {
    return <div>Loading...</div>;
  }

  return (
    <div>
      <h1>{patient.name?.[0]?.given?.[0]} {patient.name?.[0]?.family}</h1>
      <p>DOB: {patient.birthDate}</p>
    </div>
  );
}

Type Definitions

Common Types

// Resource types
type ResourceType = 'Patient' | 'Encounter' | 'Observation' | 'MedicationRequest';

// Search parameters
interface SearchParams {
  [key: string]: string | number | boolean;
}

// Search options
interface SearchOptions {
  _count?: number;
  _offset?: number;
  _sort?: string;
  _include?: string[];
  _revinclude?: string[];
}

// Cache configuration
interface CacheConfig {
  enabled: boolean;
  ttl: number;
  maxSize: number;
}

// Storage interface
interface Storage {
  getItem(key: string): string | null;
  setItem(key: string, value: string): void;
  removeItem(key: string): void;
}

Best Practices

1. Error Handling

Always handle errors gracefully and provide meaningful feedback to users.

2. Resource Validation

Validate resources before creating or updating them.

3. Use Search Efficiently

  • Use specific search parameters
  • Implement pagination for large result sets
  • Cache frequently accessed data

4. Authentication

  • Implement proper token refresh logic
  • Handle authentication errors
  • Use secure storage for tokens

5. Performance

  • Use batch operations when possible
  • Implement caching for frequently accessed data
  • Monitor API usage and response times

Next Steps