Try it free in the sandbox · first FHIR call in 5 minutesFree · 30 synthetic patients · no card
Як створити медичний API за допомогою TypeScript
Посібник: API Express на TypeScript, який створює записи пацієнтів, зберігає показники артеріального тиску як спостереження FHIR R4, читає їх назад, відображає помилки та перевіряє вебхуки — без власної медичної бази даних.
Цей посібник створює бекенд API програми для відстеження артеріального тиску на TypeScript з Express: реєстрація створює медичний запис користувача, показники зберігаються як кодовані спостереження HL7 FHIR R4, програма зчитує їх назад, а кінцева точка вебхука отримує інформацію про нові дані, без власної медичної бази даних. Ваш API зберігає те, що є специфічним для вашого продукту (користувачі, сесії, налаштування). Медичні дані, їх історія, згода та походження зберігаються в Anpheros, до якого ви звертаєтеся через TypeScript SDK.
Архітектура
mobile / web app ──► your API (Express, TypeScript) ──► Anpheros (FHIR R4 record per patient)
│ users, sessions, settings │ observations, conditions, documents,
│ user id → patient id │ provenance, consent, audit
◄──────────── webhooks ──────────────┘
Ваша база даних зберігає один додатковий стовпець для кожного користувача: їхній ідентифікатор пацієнта Anpheros. Усі клінічні дані проходять через API, тому вони кодуються, версіонуються та можуть бути прочитані будь-якою системою FHIR або іншими додатками пацієнта, на використання яких він надає згоду. Бекенд медичного додатку та база даних детально пояснює це розділення.
Перед початком роботи
- Node.js 18 або новіша версія.
- Ключ для пісочниці (
sk_test_…) з platform.anpheros.com/dashboard (Отримати ключ для пісочниці). Зберігайте його лише на сервері; він ніколи не повинен потрапити в додаток.
mkdir bp-api && cd bp-api
npm init -y && npm pkg set type=module
npm install express @anpheros/sdk
npm install -D typescript tsx @types/express @types/node
export ANPHEROS_KEY=sk_test_…
Крок 1: клієнт та ваше сховище користувачів
// server.ts
import express from 'express';
import { Anpheros, AnpherosError, apiKey, verifyWebhookSignature } from '@anpheros/sdk';
const anpheros = new Anpheros({ auth: apiKey(process.env.ANPHEROS_KEY!) });
const app = express();
// Your own user table. In production this is your database; the only medical link is patientId.
type User = { id: string; email: string; patientId: string };
const users = new Map<string, User>();
// Stand-in for your real authentication (session cookie, JWT…): it must set the signed-in user.
function currentUser(req: express.Request): User {
const user = users.get(req.header('x-user-id') ?? '');
if (!user) throw new HttpError(401, 'not signed in');
return user;
}
class HttpError extends Error {
constructor(readonly status: number, message: string) { super(message); }
}
Крок 2: Реєстрація створює медичну карту
Коли хтось реєструється, створіть для нього пацієнта в Anpheros і вставте власний ідентифікатор користувача в identifiers, щоб запис завжди можна було знайти з вашого боку:
app.post('/signup', express.json(), async (req, res, next) => {
try {
const { email, given, family, birthDate } = req.body;
const userId = crypto.randomUUID();
const patient = await anpheros.patients.create(
{ given, family, birth_date: birthDate, identifiers: [{ system: 'https://bp.example/user', value: userId }] },
{ author_type: 'patient' },
);
users.set(userId, { id: userId, email, patientId: patient.id });
res.status(201).json({ userId });
} catch (e) { next(e); }
});
Відповідь від Anpheros — це новий пацієнт, з id, що належить вашому проєкту, та його FHIR-посилання:
{"id": "27296656-c2ba-4970-8424-1dcd91163e0a", "given": "Ioana", "family": "Marin", "birth_date": "1979-04-12", "gender": "female",
"identifiers": [{"system": "https://bp.example/user", "value": "u_8f2c"}], "updated_at": "2026-10-12T20:16:51.143Z",
"fhir": "Patient/27296656-c2ba-4970-8424-1dcd91163e0a"}
Крок 3: збереження показника
Додаток надсилає {systolic, diastolic, measuredAt}. Ваш API валідує його та зберігає одне спостереження артеріального тиску, LOINC 85354-9, з його двома компонентами:
app.post('/readings', express.json(), async (req, res, next) => {
try {
const user = currentUser(req);
const { systolic, diastolic, measuredAt } = req.body;
if (![systolic, diastolic].every((v) => Number.isFinite(v) && v > 20 && v < 300)) {
throw new HttpError(400, 'systolic and diastolic must be numbers in mmHg');
}
const obs = await anpheros.observations.create(user.patientId, {
code: '85354-9',
category: 'vital-signs',
effective_at: measuredAt ?? new Date().toISOString(),
components: [
{ code: '8480-6', value: systolic, unit: 'mm[Hg]' },
{ code: '8462-4', value: diastolic, unit: 'mm[Hg]' },
],
author_type: 'patient',
}, { source_system: 'bp-app' });
res.status(201).json({ id: obs.id, measuredAt: obs.effective_at });
} catch (e) { next(e); }
});
Що Anpheros зберігає та повертає:
{"id": "78188270-e65e-49bd-89d0-e776226e9614", "patient": "27296656-…", "code": "85354-9", "system": "http://loinc.org",
"display": "Blood pressure panel", "category": "vital-signs", "effective_at": "2026-10-10T08:30:00Z",
"components": [{"code": "8480-6", "system": "http://loinc.org", "value": 138.0, "unit": "mm[Hg]"},
{"code": "8462-4", "system": "http://loinc.org", "value": 88.0, "unit": "mm[Hg]"}],
"status": "final", "updated_at": "2026-10-12T20:16:51.153Z", "fhir": "Observation/78188270-…"}
SDK надсилає Idempotency-Key з кожним створенням, тому повторна спроба після мережевої помилки не призведе до подвійного збереження показника. author_type: 'patient' та source_system потрапляють до походження показника: лікар, інший додаток або модель ШІ зможуть пізніше визначити, що людина виміряла це вдома за допомогою вашого додатка.
Крок 4: Зчитування показників назад
app.get('/readings', async (req, res, next) => {
try {
const user = currentUser(req);
const page = await anpheros.observations.list(user.patientId, {
code: '85354-9',
from: typeof req.query.from === 'string' ? req.query.from : undefined,
limit: 50,
});
res.json({
readings: page.data.map((o) => ({
id: o.id,
measuredAt: o.effective_at,
systolic: o.components.find((c) => c.code === '8480-6')?.value,
diastolic: o.components.find((c) => c.code === '8462-4')?.value,
})),
next: page.next_cursor,
});
} catch (e) { next(e); }
});
Ваш API повертає дані у форматі, який потрібен вашому додатку. Збережені дані залишаються стандартними. Оскільки ідентифікатор користувача перевіряється за допомогою currentUser, один користувач ніколи не зможе запитати ідентифікатор пацієнта іншого користувача.
Крок 5: помилки
Помилки Anpheros надходять як AnpherosError з status, type, message, field та requestId. Передавайте помилки валідації до додатка, а решту приховуйте:
app.use((err: unknown, _req: express.Request, res: express.Response, _next: express.NextFunction) => {
if (err instanceof HttpError) return res.status(err.status).json({ error: err.message });
if (err instanceof AnpherosError && err.status === 400) {
return res.status(400).json({ error: err.message, field: err.field });
}
console.error(err instanceof AnpherosError ? `anpheros ${err.status} ${err.type} request=${err.requestId}` : err);
res.status(502).json({ error: 'medical record service unavailable, try again' });
});
Наприклад, показник без дати відхиляється Anpheros з {"error": {"type": "invalid", "message": "Field required (at effective_at)", "field": "effective_at"}}. Обмеження швидкості (429) та серверні помилки повторно обробляються SDK, перш ніж ви їх побачите. Записуйте requestId та цитуйте його, якщо звертаєтеся до служби підтримки. Повний список див. у розділі Помилки.
Крок 6: Отримуйте сповіщення про нові дані за допомогою вебхука
Показники також можуть надходити з інших джерел: клініка, що записує до того самого запису, лабораторний звіт, інший додаток, до якого підключилася особа. Зареєструйте кінцеву точку один раз:
curl -X POST https://platform.anpheros.com/v1/webhooks -H "Authorization: Bearer $ANPHEROS_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://bp.example/webhooks/anpheros", "events": ["resource.created", "consent.revoked"]}'
Відповідь містить secret (whsec_…), показаний один раз. Перевіряйте кожну доставку за необробленим тілом перед тим, як довіряти їй:
app.post('/webhooks/anpheros', express.raw({ type: 'application/json' }), async (req, res) => {
const ok = await verifyWebhookSignature({
secret: process.env.ANPHEROS_WEBHOOK_SECRET!,
header: req.header('anpheros-signature'),
body: req.body as Buffer,
});
if (!ok) return res.status(400).end();
const event = JSON.parse((req.body as Buffer).toString('utf8'));
// Events carry ids and versions, not clinical data: fetch what you need, then update caches or notify.
console.log(event.type, event.data);
res.status(204).end();
});
app.listen(3000, () => console.log('bp-api on http://localhost:3000'));
Типи подій: resource.created, resource.updated, resource.deleted, consent.granted, consent.revoked, document.finalized та ping. Доставки підписуються, повторюються та можуть бути відтворені (Вебхуки).
Запуск
npx tsx server.ts
curl -X POST localhost:3000/signup -H 'content-type: application/json' \
-d '{"email":"[email protected]","given":"Ioana","family":"Marin","birthDate":"1979-04-12"}'
# {"userId":"…"}
curl -X POST localhost:3000/readings -H 'content-type: application/json' -H 'x-user-id: <userId>' \
-d '{"systolic":138,"diastolic":88,"measuredAt":"2026-10-10T08:30:00Z"}'
curl localhost:3000/readings -H 'x-user-id: <userId>'
Замініть заповнювач x-user-id на ваші справжні облікові дані перед тим, як будь-що покине ваш комп'ютер.
Подальші кроки
- Користувачі, які вже мають запис. Ключ API надає доступ до записів пацієнтів, створених вашим проєктом. Щоб підключитися до існуючого запису особи, зібраного іншими додатками, клініками чи лабораторіями, використовуйте OAuth та їхню згоду: Як реалізувати згоду пацієнта за допомогою SMART on FHIR.
- Суворий FHIR, коли це необхідно.
anpheros.fhir.search,create,transaction,everythingтаsummaryнадають вам FHIR R4 API з тими ж ідентифікаторами. - Функції ШІ.
anpheros.context.buildготує контекст із мітками джерел для моделі: Як створити додаток для охорони здоров'я на основі ШІ з бекендом FHIR. - Виробництво. Ключі для виробничого середовища (
sk_live_…) видаються перевіреним організаціям з підписаною угодою про обробку даних (DPA); див. Ціни та Обмеження та ліміти швидкості.
Часті запитання
Чи потрібна мені власна база даних для медичних даних?
Ні, не для самих медичних даних. Ваша база даних зберігає користувачів, сесії та налаштування продукту, а також ідентифікатор пацієнта для кожного користувача. Спостереження, стани, ліки, документи, їхня історія та хто їх написав, зберігаються у FHIR-записі.
Чи може мобільний додаток викликати Anpheros безпосередньо?
З ключем API — ні: ключ отримує доступ до кожного пацієнта вашого проєкту і має залишатися на вашому сервері. Мобільний додаток може викликати Anpheros безпосередньо з OAuth-токеном, який користувач надав для свого власного запису, як описано в Як створити медичний додаток за допомогою Flutter.
З яким фреймворком це працює?
З будь-яким. SDK потребує лише fetch, тому він працює в Express, Fastify, Hono, NestJS, обробниках маршрутів Next.js, Deno або Bun.
Пов'язане
- Healthcare API: на що звернути увагу в API для охорони здоров'я та короткий огляд Anpheros API
- Medical data API: куди надходить кожен тип медичних даних
- Як зберігати медичні записи за допомогою FHIR R4
- Webhooks та Errors
- Backend та база даних медичного застосунку
Try it free in the sandbox · first FHIR call in 5 minutesFree · 30 synthetic patients · no card