Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
ad8e2f0
[SampleApp] Revamp to latest version, make it clearer
Jun 23, 2026
41df6c5
self review: better comments, file names, function names, centralize …
Jun 25, 2026
7b962a5
feat: add template selection
hilnius Jun 25, 2026
35d04ef
fixes cursor & claude code reviews
hilnius Jun 26, 2026
fc7b23d
fix: make sure a failing call to generate note resets the 'generate' …
hilnius Jun 26, 2026
46667a3
build: enable github auto-dependabot-updates & merges
hilnius Jun 26, 2026
e288922
fix cursor latest review
hilnius Jun 26, 2026
3cf872c
fix cursor 3rd review
hilnius Jun 26, 2026
e7ed7b3
fix: disable note generation button until templates are loaded
hilnius Jun 26, 2026
a349de2
fix review - part one
hilnius Jun 26, 2026
f1c4dd5
2-space indentation
hilnius Jun 26, 2026
9bfba36
add editorconfig
hilnius Jun 26, 2026
282a2ce
self-review: make code easier to scan - more explicit
hilnius Jun 26, 2026
88639f5
feat: make transcript_items timeline resilient to interruptions
hilnius Jun 29, 2026
b6e774c
fix code review
hilnius Jun 29, 2026
e0754d0
use commits sha for ci workflow
hilnius Jun 29, 2026
0a87d60
remove unnecessary CORS
hilnius Jun 29, 2026
9591026
refactor: better variable naming, avoid unused temp variable
hilnius Jun 29, 2026
ac405eb
improve some nits on code quality and lifecycle in pages - add explic…
hilnius Jun 30, 2026
b238c78
remove serverClosed promise for simplicity of code
hilnius Jun 30, 2026
6e25bc7
add comment to be detailed on the problem of managing dropped sockets…
hilnius Jun 30, 2026
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
9 changes: 9 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
root = true

[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true
19 changes: 19 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
version: 2
updates:
# Single root install (npm workspaces, one package-lock.json) covers both
# frontend and backend dependencies.
- package-ecosystem: npm
directory: /
schedule:
interval: weekly
groups:
all-deps:
patterns: ["*"]

- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
groups:
all-actions:
patterns: ["*"]
30 changes: 30 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: CI

# Gate for PRs (incl. Dependabot) and pushes to main. There's no test suite, so the
# meaningful checks for this repo are: it typechecks and it builds.
on:
pull_request:
push:
branches: [main]

jobs:
ci:
name: Typecheck & build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
with:
node-version: 24
cache: npm
# One root install covers both workspaces (single package-lock.json).
- run: npm ci
- name: Typecheck frontend
run: npx tsc --noEmit -p frontend/tsconfig.json
- name: Typecheck backend
run: npx tsc --noEmit -p backend/tsconfig.json
# The frontend has no `build` npm script (dev-only repo); build via Vite directly
# as a bundle sanity check (catches ?raw imports, the worklet asset config, etc.).
- name: Build frontend
working-directory: frontend
run: npx vite build
28 changes: 28 additions & 0 deletions .github/workflows/dependabot-auto-merge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Dependabot auto-merge

# Auto-merge Dependabot patch/minor bumps once CI is green. Major bumps are left for a
# human. Requires repo setting "Allow auto-merge" and a branch-protection rule on main
# that requires the CI check — `--auto` waits for that check before merging.
on: pull_request

permissions:
contents: write
pull-requests: write

jobs:
auto-merge:
if: github.actor == 'dependabot[bot]'
runs-on: ubuntu-latest
steps:
- id: metadata
uses: dependabot/fetch-metadata@25dd0e34f4fe68f24cc83900b1fe3fe149efef98
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Enable auto-merge for patch/minor updates
if: >-
steps.metadata.outputs.update-type == 'version-update:semver-patch' ||
steps.metadata.outputs.update-type == 'version-update:semver-minor'
run: gh pr merge --auto --squash "$PR_URL"
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ logs
npm-debug.log*

# Folders
.cache/
node_modules/
dist/

# Editor directories and files
.vscode/
.idea/
.claude/
.DS_Store
*.suo
*.ntvs*
Expand Down
84 changes: 33 additions & 51 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,83 +1,65 @@
# Nabla Core API Sample App

![Sample app screenshot](static/sample_app_screenshot.png)

A minimal web app (in `app/`) that shows how to interact with the [Nabla Core API](https://docs.nabla.com).
An example integration for the [Nabla Core API](https://docs.nabla.com), showing an
end-to-end medical encounter: stream audio for live transcription, generate a
clinical note, then derive normalized FHIR data and patient instructions from it.

---

## Quick Start

### 0. Prerequisites

- Node.js v22+
- Node.js v24+
- A Nabla Core API account ([contact us](mailto:api@nabla.com) to create one)

### 1. Download and setup the project
### 1. Clone, install, and run the Sample App

```bash
git clone git@github.com:nabla/sample-app.git
cd sample-app/
cd sample-app
npm install
npm run dev
```

### 2. Create an OAuth client
### 2. Explore the app

- Sign in to the Core API admin console: [Log in](https://pro.nabla.com/login).
- Follow the [documentation](https://docs.nabla.com/guides/authentication#1-creating-an-oauth-client) to create a new OAuth Client with the "Public Key (static)" method.
If it didn't open automatically, navigate to http://localhost:5173/onboarding.html and
follow the configuration steps.

### 3. Generate user tokens
The home page links the **Full Encounter Demo** and a set of **in-depth guides** that showcase
individual endpoints with live WebSocket message inspection and annotated code.

You need to use this OAuth client to generate initial user access and refresh tokens for the app. In a realistic architecture, this work would be done by a dedicated authentication backend server on your side. For simplicity's sake, however, we provide an helper node script that imitates a backend server that would create and authenticate a Core API user.
The backend stores the keypair, config, and tokens under `.cache/`. To start over, delete that
folder.

This script is located under `scripts/generate-tokens.js` and expects the following CLI required arguments:
* `--uuid` (type:`string`): the OAuth client UUID for authentication
* `--private-key` (type:`string`): the path to the private key file (generally `private_key.pem` if you followed the documentation closely at the previous step)
* `--hostname` (type:`string`): Nabla's API hostname: `us.api.nabla.com` for US region or `eu.api.nabla.com` for EU region.

Run the following command to generate a pair of user access/refresh tokens:
### 3. Explore the code

```bash
node scripts/generate-tokens.js \
--uuid=<oauth-uuid> \
--private-key=<private-key-file> \
--hostname=us.api.nabla.com
```
The project is split into two parts:

> ℹ️ **Need a server token instead?**
> Pass the `--type=server` argument to the command above to generate a long-lived **server access token** rather than user access/refresh tokens. Use this when calling the Server API directly from your own tools.
| Folder | What it is |
|--------|------------|
| `frontend/` | The reference UI — Vite + TypeScript + Tailwind. This is the code you're meant to read. |
| `backend/` | A tiny Express server that stands in for **your** auth backend: it holds the OAuth client key, mints tokens, and provisions a user. |

### 4. Configure the frontend
The backend exists so the sample is realistic: in production, token minting and user
provisioning belong on a server you control, not in the browser. You can read it in a few
minutes (`backend/src/auth.ts`).

To launch the app, the following environment variables need to be set:
- `VITE_NABLA_ACCESS_TOKEN`: a user access token
- `VITE_NABLA_REFRESH_TOKEN`: a user refresh token
- `VITE_NABLA_API_HOSTNAME`: Nabla's API hostname: `us.api.nabla.com` for US region or `eu.api.nabla.com` for EU region.

Create a `.env.local` file at the root of the project, and add the credentials generated in **Step&nbsp;3** (or any other source you use):

```env
VITE_NABLA_ACCESS_TOKEN=my_user_access_token
VITE_NABLA_REFRESH_TOKEN=my_user_refresh_token
VITE_NABLA_API_HOSTNAME=us.api.nabla.com
```
To explore the codebase, start with `frontend/src/pages/full-encounter-demo/encounter.ts` —
the entrypoint to the full encounter demo. It's a thin orchestrator that wires the per-step
modules (`setup`, `record`, `work-on-note`) together; each step keeps its controller and DOM
rendering side by side.

### 5. Launch the app
For focused, single-endpoint walkthroughs — with live WebSocket message inspection and
annotated code snippets — see the in-depth pages under `frontend/src/pages/in-depth/`.

Run the following command and navigate to http://localhost:5173/
## API version

```bash
npm run dev
```

> [!NOTE]
> **API version notice**
>
> Please note that this sample app is only compatible with a specific version of the API, specified at the beginning of the [commonUtils.js](app/shared/commonUtils.js) file.

---
This sample targets a specific API version, pinned in `frontend/src/api/version.ts` and
`backend/src/version.ts`.

## Further reading

- **Authentication guide:** <https://docs.nabla.com/guides/authentication>
- **Full API docs:** <https://docs.nabla.com>
**API docs:** <https://docs.nabla.com>
Loading