1 Installation
npm install @qrcommunication/rppsapi
pnpm add @qrcommunication/rppsapi
yarn add @qrcommunication/rppsapi
fetch (disponible nativement depuis Node.js 18+). Aucune dépendance externe n'est requise.
"type": "module"). Assurez-vous que votre projet est configuré pour utiliser ESM, ou utilisez un bundler compatible (Vite, webpack 5+, esbuild).
Prérequis
| Environnement | Version minimale |
|---|---|
| Node.js | 18.0.0+ |
| TypeScript | 5.0+ (recommandé 5.7+) |
| Deno | 1.30+ (via npm specifier) |
| Bun | 1.0+ |
2 Quick Start
import { RppsClient } from '@qrcommunication/rppsapi';
import type { RppsFullProfile, RppsSearchResponse } from '@qrcommunication/rppsapi';
// Initialiser le client
// Sans fhirApiKey, la source FHIR est automatiquement désactivée
const client = new RppsClient({
fhirApiKey: process.env.FHIR_API_KEY, // optionnel — obtenir sur portal.api.esante.gouv.fr
timeout: 30_000,
});
// ─── Profil complet par numéro RPPS ───────────────────────────
const profil: RppsFullProfile = await client.getByRpps('10005173140');
console.log(
profil.identite.civiliteExercice.libelle,
profil.identite.prenomExercice,
profil.identite.nomExercice,
);
// → "Docteur Jean DUPONT"
console.log('Profession :', profil.profession.libelle);
// → "Médecin"
console.log('Activités :');
for (const activite of profil.activites) {
console.log(` - ${activite.structure.raisonSociale}`);
console.log(` ${activite.structure.adresse.codePostal} ${activite.structure.adresse.libelleCommune}`);
}
console.log('Diplômes :');
for (const da of profil.diplomesEtAutorisations) {
if (da.diplome) {
console.log(` - [Diplôme] ${da.diplome.diplome.libelle}`);
}
if (da.autorisation) {
console.log(` - [Autorisation] ${da.autorisation.disciplineAutorisation.libelle}`);
}
}
console.log('Savoir-faire :');
for (const sf of profil.savoirFaire) {
console.log(` - ${sf.savoirFaire.libelle} (${sf.typeSavoirFaire.libelle})`);
}
console.log('Cartes CPS :');
for (const carte of profil.cartesCps) {
console.log(` - ${carte.typeCarte.libelle} n°${carte.numeroCarte}`);
}
console.log('Messageries MSSanté :');
for (const bal of profil.messageriesMssante) {
console.log(` - ${bal.adresseBal} (${bal.typeBal})`);
}
// Métadonnées
console.log(`Durée totale : ${profil.metadata.totalDurationMs}ms`);
for (const source of profil.metadata.sources) {
const status = source.success
? `OK (${source.rowCount} lignes, ${source.durationMs}ms)`
: `ERREUR : ${source.error}`;
console.log(` ${source.source}: ${status}`);
}
// ─── Recherche par nom / prénom / code postal ──────────────────
const resultats: RppsSearchResponse = await client.search(
{ nom: 'DUPONT', codePostal: '75' },
{ page: 1, pageSize: 20 },
);
console.log(`${resultats.total} résultats trouvés`);
for (const r of resultats.results) {
console.log(` ${r.civilite} ${r.prenomExercice} ${r.nomExercice} — ${r.profession.libelle}`);
console.log(` RPPS: ${r.rpps} | ${r.structure.codePostal} ${r.structure.libelleCommune}`);
}
3
Configuration — RppsClientOptions
import { RppsClient } from '@qrcommunication/rppsapi';
const client = new RppsClient({
// URL de base de l'API FHIR Annuaire Santé (ANS)
// Défaut : 'https://gateway.api.esante.gouv.fr/fhir/v2'
fhirBaseUrl: 'https://gateway.api.esante.gouv.fr/fhir/v2',
// Clé API FHIR (ESANTE-API-KEY)
// Obtenir gratuitement sur https://portal.api.esante.gouv.fr
// Sans cette clé, la source FHIR est automatiquement désactivée
fhirApiKey: process.env.FHIR_API_KEY,
// URL de base de l'API Tabular data.gouv.fr
// Défaut : 'https://tabular-api.data.gouv.fr/api/resources'
tabularBaseUrl: 'https://tabular-api.data.gouv.fr/api/resources',
// Timeout par requête en millisecondes
// Défaut : 30000 (30 secondes)
timeout: 30_000,
// Nombre max de lignes par requête tabular
// Défaut : 100
tabularPageSize: 100,
// Désactiver certaines sources (utile pour réduire la latence)
// Valeurs possibles : 'fhir' | 'personne-activite' | 'diplomes'
// | 'savoir-faire' | 'carte-cps' | 'mssante'
disabledSources: ['fhir', 'mssante'],
// Headers HTTP supplémentaires envoyés avec chaque requête
headers: {
'X-App-Name': 'mon-application',
},
});
Interface RppsClientOptions
| Propriété | Type | Défaut | Description |
|---|---|---|---|
fhirBaseUrl |
string? |
'https://gateway.api.esante.gouv.fr/fhir/v2' |
URL de base de l'API FHIR Annuaire Santé (ANS) |
fhirApiKey |
string? |
'' |
Clé API FHIR (ESANTE-API-KEY). Sans cette clé, la source FHIR est automatiquement désactivée. |
tabularBaseUrl |
string? |
'https://tabular-api.data.gouv.fr/api/resources' |
URL de base de l'API Tabular data.gouv.fr |
timeout |
number? |
30000 |
Timeout en millisecondes par requête (AbortController) |
tabularPageSize |
number? |
100 |
Nombre de lignes par page pour les requêtes tabular |
disabledSources |
SourceName[]? |
[] |
Liste des sources à désactiver. Voir type SourceName. |
headers |
Record<string, string>? |
{} |
Headers HTTP supplémentaires ajoutés à chaque requête |
- Créer un compte sur portal.api.esante.gouv.fr
- Créer une application
- S'abonner à API Annuaire Santé en libre accès
- Récupérer la clé
ESANTE-API-KEYdans l'onglet Credentials
4 API Reference
async
client.getByRpps(rpps)
Récupère le profil complet d'un professionnel de santé à partir de son numéro RPPS. Interroge toutes les sources activées en parallèle via Promise.all() et fusionne les résultats en un seul objet structuré.
Signature
getByRpps(rpps: string): Promise<RppsFullProfile>
Paramètres
| Paramètre | Type | Description |
|---|---|---|
rpps |
string |
Numéro RPPS du praticien (exactement 11 chiffres). Les espaces en début/fin sont ignorés. |
Retour
Retourne une Promise<RppsFullProfile>. Lance une Error si le numéro RPPS est invalide (format incorrect).
Exemple
const profil = await client.getByRpps('10005173140');
// Identité
console.log(profil.identite.civiliteExercice.libelle); // "Docteur"
console.log(profil.identite.nomExercice); // "DUPONT"
console.log(profil.identite.prenomExercice); // "Jean"
// Profession
console.log(profil.profession.libelle); // "Médecin"
console.log(profil.profession.code); // "10"
console.log(profil.profession.categorie.libelle); // "Civil"
// Activités (structures d'exercice)
profil.activites.forEach((a) => {
console.log(a.structure.raisonSociale);
console.log(a.structure.adresse.codePostal, a.structure.adresse.libelleCommune);
console.log(a.modeExercice.libelle); // "Libéral"
});
// Diplômes et autorisations
profil.diplomesEtAutorisations.forEach((da) => {
if (da.diplome) console.log(da.diplome.diplome.libelle);
if (da.autorisation) console.log(da.autorisation.disciplineAutorisation.libelle);
});
// Savoir-faire
profil.savoirFaire.forEach((sf) => {
console.log(sf.savoirFaire.libelle, '—', sf.typeSavoirFaire.libelle);
});
// Cartes CPS/CPF
profil.cartesCps.forEach((c) => {
console.log(c.typeCarte.libelle, c.numeroCarte);
console.log(`Valide du ${c.dateDebutValidite} au ${c.dateFinValidite}`);
});
// Messageries MSSanté
profil.messageriesMssante.forEach((m) => {
console.log(m.adresseBal, m.typeBal);
console.log('Dématérialisation :', m.dematerialisation);
});
// Données FHIR (si clé API fournie)
if (profil.fhir.practitioner) {
console.log('FHIR id :', profil.fhir.practitioner.id);
console.log('PractitionerRoles :', profil.fhir.practitionerRoles.length);
}
// Métadonnées de la requête
console.log(`Interrogé le : ${profil.metadata.queriedAt}`);
console.log(`Durée totale : ${profil.metadata.totalDurationMs}ms`);
async
client.search(criteria, options?)
Recherche des professionnels de santé par nom, prénom et/ou code postal. La recherche est partielle et insensible à la casse (opérateur contains). Au moins un critère est obligatoire.
Signature
search(
criteria: RppsSearchCriteria,
options?: { page?: number; pageSize?: number }
): Promise<RppsSearchResponse>
Paramètres
| Paramètre | Type | Description |
|---|---|---|
criteria.nom |
string? |
Nom d'exercice (recherche partielle, insensible à la casse) |
criteria.prenom |
string? |
Prénom d'exercice (recherche partielle, insensible à la casse) |
criteria.codePostal |
string? |
Code postal (recherche exacte ou partielle — ex: '75' pour Paris) |
options.page |
number? |
Page à récupérer. Défaut : 1 |
options.pageSize |
number? |
Nombre de résultats par page. Défaut : valeur de tabularPageSize (100) |
Exemple
// Recherche par nom + code postal (département)
const r1 = await client.search({ nom: 'DUPONT', codePostal: '75' });
console.log(`${r1.total} résultats, page ${r1.page}/${Math.ceil(r1.total / r1.pageSize)}`);
for (const r of r1.results) {
console.log(`${r.civilite} ${r.prenomExercice} ${r.nomExercice}`);
console.log(` Profession : ${r.profession.libelle}`);
console.log(` Structure : ${r.structure.raisonSociale}`);
console.log(` Adresse : ${r.structure.codePostal} ${r.structure.libelleCommune}`);
console.log(` RPPS : ${r.rpps}`);
}
// Pagination
const page2 = await client.search(
{ nom: 'DUPONT', codePostal: '75' },
{ page: 2, pageSize: 20 },
);
// Recherche par prénom seul
const r2 = await client.search({ prenom: 'Nicolas' }, { pageSize: 10 });
// Enchaîner : recherche → profil complet
if (r1.results.length > 0) {
const profil = await client.getByRpps(r1.results[0].rpps);
console.log(`Diplômes : ${profil.diplomesEtAutorisations.length}`);
}
Sources individuelles
Pour un usage avancé, chaque source peut être interrogée directement en important les fonctions individuelles. Ces fonctions sont les briques internes utilisées par RppsClient.getByRpps().
import {
fetchFhirByRpps,
fetchPersonneActivite,
searchPersonneActivite,
fetchDiplomes,
fetchSavoirFaire,
fetchCartesCps,
fetchMssante,
} from '@qrcommunication/rppsapi';
| Fonction | Source | Retour | Description |
|---|---|---|---|
fetchFhirByRpps(rpps, opts) |
API FHIR ANS | Promise<FhirData> |
Practitioner + PractitionerRoles FHIR. Requiert une clé API dans les headers (ESANTE-API-KEY). |
fetchPersonneActivite(rpps, opts) |
data.gouv.fr Tabular | Promise<{ identite, identificationNationale, profession, activites }> |
Identité, profession et structures d'exercice. |
searchPersonneActivite(criteria, opts) |
data.gouv.fr Tabular | Promise<{ results, total }> |
Recherche par nom/prénom/code postal. |
fetchDiplomes(rpps, opts) |
data.gouv.fr Tabular | Promise<DiplomeEtAutorisation[]> |
Diplômes et autorisations d'exercice. |
fetchSavoirFaire(rpps, opts) |
data.gouv.fr Tabular | Promise<SavoirFaire[]> |
Savoir-faire et compétences spécifiques. |
fetchCartesCps(rpps, opts) |
data.gouv.fr Tabular | Promise<CarteCps[]> |
Cartes CPS et CPF (professionnelles de santé). |
fetchMssante(rpps, opts) |
data.gouv.fr Tabular | Promise<MessagerieMssante[]> |
Boîtes aux lettres de messagerie sécurisée MSSanté. |
Exemple d'utilisation directe
import { fetchFhirByRpps, fetchDiplomes } from '@qrcommunication/rppsapi';
// Interroger uniquement la source FHIR
const fhirData = await fetchFhirByRpps('10005173140', {
baseUrl: 'https://gateway.api.esante.gouv.fr/fhir/v2',
timeout: 15_000,
headers: {
'ESANTE-API-KEY': process.env.FHIR_API_KEY ?? '',
},
});
console.log(fhirData.practitioner?.id);
console.log(fhirData.practitionerRoles.length);
// Interroger uniquement les diplômes
const diplomes = await fetchDiplomes('10005173140', {
baseUrl: 'https://tabular-api.data.gouv.fr/api/resources',
timeout: 20_000,
pageSize: 100,
headers: {},
});
console.log(`${diplomes.length} diplôme(s)`);
diplomes.forEach((da) => {
if (da.diplome) console.log(da.diplome.diplome.libelle);
});
validateRpps(rpps)
Valide et normalise un numéro RPPS. Le RPPS doit contenir exactement 11 chiffres. Les espaces en début et fin de chaîne sont supprimés. Lance une Error si le format est invalide.
import { validateRpps } from '@qrcommunication/rppsapi';
// Valide (retourne le RPPS nettoyé)
const rpps = validateRpps('10005173140'); // → '10005173140'
const rppsWithSpaces = validateRpps(' 10005173140 '); // → '10005173140'
// Invalide (lance une Error)
try {
validateRpps('123'); // Moins de 11 chiffres
} catch (error) {
console.error(error.message);
// → 'Numéro RPPS invalide : "123". Le RPPS doit contenir exactement 11 chiffres.'
}
// Utilisation pour pré-valider avant un appel API
function isValidRpps(rpps: string): boolean {
try {
validateRpps(rpps);
return true;
} catch {
return false;
}
}
5 Interfaces TypeScript
Toutes les interfaces sont exportées depuis le package principal et utilisables directement.
import type {
RppsFullProfile,
RppsClientOptions,
RppsSearchCriteria,
RppsSearchResponse,
RppsSearchResult,
Identite,
Profession,
Activite,
Structure,
AdresseStructure,
DiplomeEtAutorisation,
SavoirFaire,
CarteCps,
MessagerieMssante,
FhirData,
CodeLabel,
SourceStatus,
SourceName,
} from '@qrcommunication/rppsapi';
RppsFullProfile
Profil complet d'un professionnel de santé. Retourné par getByRpps().
interface RppsFullProfile {
rpps: string; // Numéro RPPS (11 chiffres)
identificationNationale: string; // Identification nationale PP (préfixe type + RPPS)
identite: Identite; // Identité du praticien
profession: Profession; // Profession principale
activites: Activite[]; // Structures d'exercice
diplomesEtAutorisations: DiplomeEtAutorisation[]; // Diplômes et autorisations
savoirFaire: SavoirFaire[]; // Savoir-faire et compétences
cartesCps: CarteCps[]; // Cartes CPS/CPF
messageriesMssante: MessagerieMssante[]; // Messageries MSSanté (BAL personnelles)
fhir: FhirData; // Données FHIR brutes (usage avancé)
metadata: {
sources: SourceStatus[]; // Statut de chaque source interrogée
queriedAt: string; // ISO 8601 — date/heure de la requête
totalDurationMs: number; // Durée totale en millisecondes
};
}
Identite
interface Identite {
civilite: CodeLabel; // Civilité administrative (ex: { code: 'M', libelle: 'M.' })
civiliteExercice: CodeLabel; // Civilité d'exercice (ex: { code: 'DR', libelle: 'Docteur' })
nomExercice: string; // Nom de famille d'exercice
prenomExercice: string; // Prénom d'exercice
}
Profession
interface Profession {
code: string; // Code profession (ex: '10' = Médecin)
libelle: string; // Libellé profession (ex: 'Médecin')
categorie: CodeLabel; // Catégorie professionnelle (ex: { code: 'C', libelle: 'Civil' })
}
Activite
interface Activite {
modeExercice: CodeLabel; // Mode d'exercice (ex: 'Libéral', 'Salarié')
savoirFaire: CodeLabel; // Savoir-faire principal de l'activité
typeSavoirFaire: CodeLabel; // Type de savoir-faire
secteurActivite: CodeLabel; // Secteur d'activité (ex: 'Cabinet libéral')
sectionTableauPharmaciens: CodeLabel; // Section tableau pharmaciens (si applicable)
role: CodeLabel; // Rôle dans la structure
genreActivite: CodeLabel; // Genre d'activité
autoriteEnregistrement: string; // Autorité d'enregistrement
structure: Structure; // Structure d'exercice associée
}
Structure
interface Structure {
identifiantTechnique: string; // Identifiant technique
raisonSociale: string; // Raison sociale du site
enseigneCommerciale: string; // Enseigne commerciale
numeroSiret: string; // Numéro SIRET
numeroSiren: string; // Numéro SIREN
numeroFinessSite: string; // N° FINESS du site
numeroFinessEtablissementJuridique: string; // N° FINESS établissement juridique
telephone: string;
telephone2: string;
telecopie: string;
email: string;
codeDepartement: string;
libelleDepartement: string;
ancienIdentifiant: string;
adresse: AdresseStructure;
}
AdresseStructure
interface AdresseStructure {
complementDestinataire: string;
complementPointGeographique: string;
numeroVoie: string;
indiceRepetitionVoie: string;
codeTypeVoie: string;
libelleTypeVoie: string;
libelleVoie: string;
mentionDistribution: string;
bureauCedex: string;
codePostal: string;
codeCommune: string;
libelleCommune: string;
codePays: string;
libellePays: string;
}
DiplomeEtAutorisation
interface DiplomeEtAutorisation {
diplome: {
typeDiplome: CodeLabel; // Type de diplôme (ex: 'Doctorat d'État de médecine')
diplome: CodeLabel; // Diplôme (code + libellé)
} | null;
autorisation: {
typeAutorisation: CodeLabel; // Type d'autorisation
disciplineAutorisation: CodeLabel; // Discipline concernée
} | null;
}
SavoirFaire
interface SavoirFaire {
profession: CodeLabel; // Profession associée
categorieProfessionnelle: CodeLabel; // Catégorie professionnelle
typeSavoirFaire: CodeLabel; // Type (ex: 'Spécialité ordinale', 'DESC')
savoirFaire: CodeLabel; // Savoir-faire (ex: 'Cardiologie')
}
CarteCps
interface CarteCps {
typeCarte: CodeLabel; // Type de carte (CPS, CPF, CPE...)
numeroCarte: string; // Numéro de la carte
identifiantNationalCarte: string; // Identifiant national
dateDebutValidite: string; // Date de début de validité (format AAAA-MM-JJ)
dateFinValidite: string; // Date de fin de validité (format AAAA-MM-JJ)
dateOpposition: string; // Date d'opposition (si applicable)
dateMiseAJour: string; // Date de dernière mise à jour
}
MessagerieMssante
interface MessagerieMssante {
typeBal: string; // Type de boîte aux lettres ('Personnelle', 'Organisationnelle')
adresseBal: string; // Adresse e-mail MSSanté (ex: jean.dupont@medecin.mssante.fr)
dematerialisation: boolean; // Indique si la BAL accepte la dématérialisation
savoirFaire: CodeLabel; // Savoir-faire associé
structure: {
identifiant: string;
typeIdentifiant: string;
serviceRattachement: string;
raisonSociale: string;
enseigneCommerciale: string;
adresse: {
complementLocalisation: string;
complementDistribution: string;
numeroVoie: string;
complementNumeroVoie: string;
typeVoie: string;
libelleVoie: string;
lieuDit: string;
ligneAcheminement: string;
codePostal: string;
departement: string;
pays: string;
};
};
}
FhirData
interface FhirData {
practitioner: FhirPractitioner | null; // Ressource FHIR Practitioner (null si non trouvé)
practitionerRoles: FhirPractitionerRole[]; // Ressources FHIR PractitionerRole associées
}
CodeLabel
// Type de base omniprésent dans les données RPPS
interface CodeLabel {
code: string; // Code court (ex: '10', 'M', 'C')
libelle: string; // Libellé complet (ex: 'Médecin', 'M.', 'Civil')
}
SourceStatus
interface SourceStatus {
source: SourceName; // Identifiant de la source interrogée
success: boolean; // true si la requête a réussi
rowCount: number; // Nombre de lignes/ressources retournées
durationMs: number; // Durée de la requête en millisecondes
error?: string; // Message d'erreur si success === false (ou 'disabled')
}
// Valeurs possibles pour SourceName :
type SourceName =
| 'fhir'
| 'personne-activite'
| 'diplomes'
| 'savoir-faire'
| 'carte-cps'
| 'mssante';
RppsSearchCriteria
// Au moins un champ obligatoire
interface RppsSearchCriteria {
nom?: string; // Nom d'exercice (recherche partielle, insensible à la casse)
prenom?: string; // Prénom d'exercice (recherche partielle, insensible à la casse)
codePostal?: string; // Code postal (ex: '75' pour Paris, '69001' pour Lyon 1er)
}
RppsSearchResponse
interface RppsSearchResponse {
results: RppsSearchResult[]; // Résultats de la page courante
total: number; // Nombre total de résultats correspondants
page: number; // Numéro de la page courante
pageSize: number; // Taille de la page
durationMs: number; // Durée de la requête en millisecondes
criteria: RppsSearchCriteria; // Critères utilisés (écho)
}
RppsSearchResult
interface RppsSearchResult {
rpps: string;
identificationNationale: string;
civilite: string;
nomExercice: string;
prenomExercice: string;
profession: CodeLabel;
categorieProfessionnelle: CodeLabel;
modeExercice: CodeLabel;
savoirFaire: CodeLabel;
structure: {
raisonSociale: string;
codePostal: string;
libelleCommune: string;
departement: string;
};
}
6 Sources de données
Le SDK agrège 6 sources publiques en parallèle pour construire le profil complet.
| Source | API | Données fournies | Clé API requise |
|---|---|---|---|
personne-activite |
data.gouv.fr Tabular API | Identité, profession, structures d'exercice, adresses | Non |
diplomes |
data.gouv.fr Tabular API | Diplômes d'État, autorisations d'exercice | Non |
savoir-faire |
data.gouv.fr Tabular API | Spécialités, DESC, savoir-faire ordinaux | Non |
carte-cps |
data.gouv.fr Tabular API | Cartes CPS, CPF, CPE (professionnelles de santé) | Non |
mssante |
data.gouv.fr Tabular API | Boîtes aux lettres de messagerie sécurisée MSSanté | Non |
fhir |
API FHIR Annuaire Santé (ANS) | Ressources FHIR R4 : Practitioner + PractitionerRole | Oui (ESANTE-API-KEY) |
RppsFullProfile avec les données disponibles. Les sources en erreur ont success: false dans metadata.sources. Aucune exception n'est levée pour les erreurs partielles.
7 Différences vs SDK PHP
| Aspect | SDK TypeScript/JS | SDK PHP |
|---|---|---|
| Package | @qrcommunication/rppsapi |
qrcommunication/rppsapi |
| Runtime | Node.js 18+ / Bun / Deno | PHP 8.1+ |
| Format de module | ESM uniquement ("type": "module") |
PSR-4 Composer |
| Dépendances | Zéro dépendance — fetch natif | guzzlehttp/guzzle ^7.0 |
| Parallélisme | Promise.all() — requêtes simultanées natives |
Requêtes concurrentes Guzzle (pool) |
| Timeout | En millisecondes (timeout: 30_000) |
En secondes (timeout: 30.0) |
| Critères de recherche | Objet littéral { nom, prenom, codePostal } |
Classe DTO RppsSearchCriteria |
| Exceptions | Error native JavaScript |
RppsException (classe personnalisée) |
| Exportation des types | Interfaces TypeScript exportées | DTOs PHP avec propriétés typées |
| Sources individuelles | Fonctions exportées directement | Méthodes de classe (usage interne) |
Équivalences de configuration
// TypeScript
const client = new RppsClient({
fhirApiKey: process.env.FHIR_API_KEY,
timeout: 30_000, // millisecondes
disabledSources: ['mssante'],
});
// PHP (équivalent)
$client = new RppsClient(new RppsClientOptions(
fhirApiKey: $_ENV['FHIR_API_KEY'],
timeout: 30.0, // secondes
disabledSources: ['mssante'],
));
8 Gestion des erreurs
Erreurs de validation
Les erreurs de validation (RPPS invalide, aucun critère de recherche) lancent une Error native JavaScript immédiatement, avant toute requête réseau.
try {
const profil = await client.getByRpps('123'); // RPPS invalide
} catch (error) {
if (error instanceof Error) {
console.error(error.message);
// → 'Numéro RPPS invalide : "123". Le RPPS doit contenir exactement 11 chiffres.'
}
}
try {
const resultats = await client.search({}); // Aucun critère
} catch (error) {
if (error instanceof Error) {
console.error(error.message);
// → 'Au moins un critère de recherche est obligatoire (nom, prenom ou codePostal).'
}
}
Erreurs partielles (sources)
Les erreurs réseau sur une source individuelle ne propagent pas d'exception. La source est marquée success: false dans les métadonnées, et les données correspondantes sont remplacées par des valeurs par défaut (tableaux vides, objets vides).
const profil = await client.getByRpps('10005173140');
// Vérifier l'état de chaque source
for (const source of profil.metadata.sources) {
if (!source.success) {
console.warn(
`Source "${source.source}" indisponible : ${source.error}`
);
// Continuer quand même avec les données disponibles
}
}
// Vérifier qu'une source critique a réussi
const fhirSource = profil.metadata.sources.find((s) => s.source === 'fhir');
if (fhirSource && !fhirSource.success && fhirSource.error !== 'disabled') {
console.warn('FHIR indisponible — données partielles');
}
Timeout
// Configurer un timeout court pour les environnements sensibles
const client = new RppsClient({ timeout: 10_000 }); // 10 secondes
try {
const profil = await client.getByRpps('10005173140');
} catch (error) {
if (error instanceof Error && error.name === 'AbortError') {
console.error('Requête annulée (timeout)');
}
}
Erreurs HTTP
// Les erreurs HTTP (4xx, 5xx) sur une source individuelle sont capturées
// et stockées dans metadata.sources[n].error
// Format du message d'erreur : "HTTP 503: Service Unavailable — https://..."
const profil = await client.getByRpps('10005173140');
const mssanteStatus = profil.metadata.sources.find((s) => s.source === 'mssante');
if (mssanteStatus?.error?.startsWith('HTTP 5')) {
console.warn('Serveur MSSanté temporairement indisponible');
}
9 Exemples avancés
Désactiver des sources pour accélérer la réponse
// Ne récupérer que l'identité et les activités (sources les plus rapides)
const clientRapide = new RppsClient({
disabledSources: ['fhir', 'diplomes', 'savoir-faire', 'carte-cps', 'mssante'],
});
const profil = await clientRapide.getByRpps('10005173140');
// Seule la source personne-activite est interrogée → < 1 seconde
Profils en parallèle pour une liste de RPPS
const client = new RppsClient();
const rppsList = ['10005173140', '10003704284', '10006847543'];
// Interroger les 3 profils simultanément
const [profil1, profil2, profil3] = await Promise.all(
rppsList.map((rpps) => client.getByRpps(rpps))
);
console.log(profil1.identite.nomExercice);
console.log(profil2.identite.nomExercice);
console.log(profil3.identite.nomExercice);
Parcourir toutes les pages de résultats
import { RppsClient } from '@qrcommunication/rppsapi';
import type { RppsSearchResult } from '@qrcommunication/rppsapi';
const client = new RppsClient();
async function fetchAllResults(nom: string): Promise {
const pageSize = 100;
let page = 1;
const allResults: RppsSearchResult[] = [];
while (true) {
const response = await client.search({ nom }, { page, pageSize });
allResults.push(...response.results);
if (allResults.length >= response.total || response.results.length === 0) {
break;
}
page++;
}
return allResults;
}
const tousLesDupont = await fetchAllResults('DUPONT');
console.log(`${tousLesDupont.length} médecins nommés DUPONT`);
Utilisation dans une API Express / Fastify
import express from 'express';
import { RppsClient, validateRpps } from '@qrcommunication/rppsapi';
const router = express.Router();
// Instancier une seule fois (connexion réutilisée)
const client = new RppsClient({
fhirApiKey: process.env.FHIR_API_KEY,
timeout: 20_000,
});
// GET /api/rpps/:rpps
router.get('/:rpps', async (req, res) => {
try {
const rpps = validateRpps(req.params.rpps);
const profil = await client.getByRpps(rpps);
res.json(profil);
} catch (error) {
if (error instanceof Error && error.message.includes('invalide')) {
res.status(400).json({ error: error.message });
} else {
res.status(500).json({ error: 'Erreur interne' });
}
}
});
// GET /api/rpps/search?nom=DUPONT&codePostal=75
router.get('/search', async (req, res) => {
const { nom, prenom, codePostal, page = '1', pageSize = '20' } = req.query as Record;
try {
const resultats = await client.search(
{ nom, prenom, codePostal },
{ page: parseInt(page), pageSize: parseInt(pageSize) },
);
res.json(resultats);
} catch (error) {
if (error instanceof Error) {
res.status(400).json({ error: error.message });
} else {
res.status(500).json({ error: 'Erreur interne' });
}
}
});
export default router;
Utilisation avec Deno
import { RppsClient } from 'npm:@qrcommunication/rppsapi';
const client = new RppsClient({
fhirApiKey: Deno.env.get('FHIR_API_KEY'),
});
const profil = await client.getByRpps('10005173140');
console.log(profil.identite.nomExercice);