# Πώς να δημιουργήσετε ένα API υγειονομικής περίθαλψης με TypeScript

> Οδηγός: ένα API Express σε TypeScript που δημιουργεί εγγραφές ασθενών, αποθηκεύει μετρήσεις πίεσης αίματος ως παρατηρήσεις FHIR R4, τις ανακτά και επαληθεύει webhooks — χωρίς δική σας ιατρική βάση δεδομένων.

Source: https://developers.anpheros.com/el/guides/healthcare-api-typescript

**Αυτός ο οδηγός δημιουργεί το backend API μιας εφαρμογής παρακολούθησης πίεσης αίματος σε TypeScript με Express: η εγγραφή δημιουργεί το ιατρικό αρχείο του χρήστη, οι μετρήσεις αποθηκεύονται ως κωδικοποιημένες παρατηρήσεις HL7 FHIR R4, η εφαρμογή τις ανακτά και ένα webhook endpoint ενημερώνεται για νέα δεδομένα, χωρίς δική σας ιατρική βάση δεδομένων.** Το API σας διατηρεί ό,τι είναι συγκεκριμένο για το προϊόν σας (χρήστες, συνεδρίες, ρυθμίσεις). Τα ιατρικά δεδομένα, το ιστορικό τους, η συγκατάθεση και η προέλευση ζουν στο Anpheros, στο οποίο καλείτε μέσω του [TypeScript SDK](https://developers.anpheros.com/el/guides/sdks).

## Αρχιτεκτονική

```
 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 ή από άλλες εφαρμογές του ασθενούς στις οποίες δίνει τη συγκατάθεσή του. Το [Medical app backend and database](https://developers.anpheros.com/el/guides/medical-app-backend) εξηγεί λεπτομερώς τον διαχωρισμό.

## Πριν ξεκινήσετε

- Node.js 18 ή νεότερη έκδοση.
- Ένα κλειδί sandbox (`sk_test_…`) από το [platform.anpheros.com/dashboard](https://platform.anpheros.com/dashboard/) (**Λήψη κλειδιού sandbox**). Διατηρήστε το μόνο στον διακομιστή. δεν πρέπει ποτέ να φτάσει στην εφαρμογή.

```bash
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: ο πελάτης και η αποθήκη χρηστών σας

```ts
// 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`, ώστε ο φάκελος να μπορεί πάντα να βρεθεί από την πλευρά σας:

```ts
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 του:

```json
{"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`, με τα δύο συστατικά της:

```ts
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:

```json
{"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` εισέρχονται στην προέλευση της μέτρησης: ένας γιατρός, μια άλλη εφαρμογή ή ένα μοντέλο AI μπορούν αργότερα να διαπιστώσουν ότι το άτομο μέτρησε αυτό στο σπίτι με την εφαρμογή σας.

## Βήμα 4: Ανάγνωση μετρήσεων

```ts
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`. Προωθήστε τα σφάλματα επικύρωσης στην εφαρμογή και αποκρύψτε τα υπόλοιπα:

```ts
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"}}`. Τα όρια ρυθμού (rate limits) (`429`) και τα σφάλματα διακομιστή επαναδοκιμάζονται από το SDK πριν τα δείτε. Καταγράψτε το `requestId` και αναφέρετέ το αν επικοινωνήσετε με την υποστήριξη. Η πλήρης λίστα βρίσκεται στο [Σφάλματα](https://developers.anpheros.com/el/guides/errors).

## Βήμα 6: Ενημέρωση για νέα δεδομένα με webhook

Οι αναγνώσεις μπορούν επίσης να προέρχονται από αλλού: μια κλινική που γράφει στο ίδιο αρχείο, μια εργαστηριακή αναφορά, μια άλλη εφαρμογή που συνδέθηκε το άτομο. Καταχωρήστε ένα endpoint μία φορά:

```bash
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_…`), που εμφανίζεται μία φορά. Επαληθεύστε κάθε παράδοση σε σχέση με το ακατέργαστο σώμα πριν την εμπιστευτείτε:

```ts
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`. Οι παραδόσεις υπογράφονται, επαναλαμβάνονται και μπορούν να αναπαραχθούν ([Webhooks](https://developers.anpheros.com/el/guides/webhooks)).

## Εκτέλεση

```bash
npx tsx server.ts
curl -X POST localhost:3000/signup -H 'content-type: application/json' \
     -d '{"email":"ioana@example.com","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` placeholder με την πραγματική σας πιστοποίηση πριν οτιδήποτε φύγει από τη μηχανή σας.

## Περαιτέρω Βήματα

- **Άτομα που έχουν ήδη εγγραφή.** Ένα κλειδί API φτάνει στους ασθενείς που δημιούργησε το έργο σας. Για να συνδεθείτε με την υπάρχουσα εγγραφή ενός ατόμου, που συλλέχθηκε από άλλες εφαρμογές, κλινικές ή εργαστήρια, χρησιμοποιήστε OAuth και τη συγκατάθεσή τους: [Πώς να υλοποιήσετε τη συγκατάθεση ασθενούς με SMART on FHIR](https://developers.anpheros.com/el/guides/smart-on-fhir-backend).
- **Αυστηρό FHIR όταν το χρειάζεστε.** `anpheros.fhir.search`, `create`, `transaction`, `everything` και `summary` σας παρέχουν το [FHIR R4 API](https://developers.anpheros.com/el/guides/store-medical-records-fhir) με τα ίδια αναγνωριστικά.
- **Λειτουργίες AI.** Το `anpheros.context.build` προετοιμάζει ένα πλαίσιο με ετικέτες πηγής για ένα μοντέλο: [Πώς να δημιουργήσετε μια εφαρμογή υγειονομικής περίθαλψης AI με backend FHIR](https://developers.anpheros.com/el/guides/build-ai-healthcare-app).
- **Παραγωγή.** Τα κλειδιά παραγωγής (`sk_live_…`) εκδίδονται σε επαληθευμένους οργανισμούς με υπογεγραμμένη DPA· δείτε [Τιμολόγηση](https://developers.anpheros.com/el/guides/pricing) και [Όρια και όρια ρυθμού](https://developers.anpheros.com/el/guides/limits).

## Συχνές ερωτήσεις

### Χρειάζομαι δική μου βάση δεδομένων για ιατρικά δεδομένα;
Όχι για τα ίδια τα ιατρικά δεδομένα. Η βάση δεδομένων σας διατηρεί χρήστες, συνεδρίες και ρυθμίσεις προϊόντων, συν το αναγνωριστικό ασθενούς για κάθε χρήστη. Παρατηρήσεις, καταστάσεις, φαρμακευτικές αγωγές, έγγραφα, το ιστορικό τους και ποιος τα έγραψε αποθηκεύονται στο αρχείο FHIR.

### Μπορεί η εφαρμογή για κινητά να καλέσει απευθείας το Anpheros;
Με ένα κλειδί API, όχι: το κλειδί φτάνει σε κάθε ασθενή του έργου σας και πρέπει να παραμείνει στον διακομιστή σας. Μια εφαρμογή για κινητά μπορεί να καλέσει απευθείας το Anpheros με ένα διακριτικό OAuth που παραχώρησε το άτομο για το δικό του αρχείο, όπως στο [Πώς να δημιουργήσετε μια εφαρμογή υγειονομικής περίθαλψης με Flutter](https://developers.anpheros.com/el/guides/healthcare-app-flutter).

### Με ποιο πλαίσιο λειτουργεί αυτό;
Οποιοδήποτε. Το SDK χρειάζεται μόνο `fetch`, οπότε εκτελείται σε Express, Fastify, Hono, NestJS, χειριστές διαδρομών Next.js, Deno ή Bun.

## Σχετικά

- [Healthcare API](https://developers.anpheros.com/el/guides/healthcare-api): τι να προσέξετε σε ένα Healthcare API και μια επισκόπηση του Anpheros API
- [Medical data API](https://developers.anpheros.com/el/guides/medical-data-api): πού πηγαίνει κάθε είδος ιατρικών δεδομένων
- [Πώς να αποθηκεύσετε ιατρικά αρχεία χρησιμοποιώντας FHIR R4](https://developers.anpheros.com/el/guides/store-medical-records-fhir)
- [Webhooks](https://developers.anpheros.com/el/guides/webhooks) και [Σφάλματα](https://developers.anpheros.com/el/guides/errors)
- [Backend και βάση δεδομένων ιατρικής εφαρμογής](https://developers.anpheros.com/el/guides/medical-app-backend)
