Skip to content

Commit b496f8d

Browse files
authored
chore: add fastapi example from markdown (#2251)
1 parent 094d6a4 commit b496f8d

12 files changed

Lines changed: 1252 additions & 0 deletions

File tree

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.superdoc-state/
2+
.venv/
3+
__pycache__/
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# FastAPI + SuperDoc Collaboration (Barebones)
2+
3+
Tiny demo focused on one thing: open a realtime collaboration session from Python and mutate it via HTTP.
4+
5+
`main.py` is currently hardcoded for the local `@y/hub` server in `./yjs-hub`.
6+
It is also hardcoded to use the repo CLI at `apps/cli/src/index.ts` (via bun)
7+
and local state at `examples/collaboration/fastapi/.superdoc-state`.
8+
9+
## 1) FastAPI setup
10+
11+
```bash
12+
cd /Users/nickjbernal/dev/superdoc/examples/collaboration/fastapi
13+
uv venv .venv
14+
source .venv/bin/activate
15+
uv pip install -r requirements.txt
16+
```
17+
18+
## 2) Start a collaboration server
19+
20+
### Option A: Local `@y/hub` (for this example)
21+
22+
From the FastAPI folder:
23+
24+
```bash
25+
cd /Users/nickjbernal/dev/superdoc/examples/collaboration/fastapi
26+
./run-yjs-hub.sh
27+
```
28+
29+
Or manually:
30+
31+
```bash
32+
cd /Users/nickjbernal/dev/superdoc/examples/collaboration/fastapi/yjs-hub
33+
pnpm install --ignore-workspace --lockfile=false
34+
pnpm run deps:up
35+
pnpm run dev
36+
```
37+
38+
`deps:up` requires Docker daemon running (Docker Desktop on macOS).
39+
If you already run Redis/Postgres locally, use:
40+
41+
```bash
42+
./run-yjs-hub.sh --no-docker
43+
```
44+
45+
The bundled `yjs-hub` demo is ephemeral by default (no persistence across server restarts).
46+
47+
This serves websocket rooms at:
48+
49+
```text
50+
ws://127.0.0.1:8081/v1/collaboration/:documentId
51+
```
52+
53+
### Option B: Internal repo dev collab server
54+
55+
```bash
56+
cd /Users/nickjbernal/dev/superdoc
57+
pnpm dev:collab
58+
```
59+
60+
If you use Option B, update `main.py` to point back to that server URL.
61+
62+
## 3) Start FastAPI
63+
64+
```bash
65+
cd /Users/nickjbernal/dev/superdoc/examples/collaboration/fastapi
66+
uvicorn main:app --reload --port 8000
67+
```
68+
69+
## 4) Test Python API
70+
71+
```bash
72+
curl "http://127.0.0.1:8000/status"
73+
curl "http://127.0.0.1:8000/insert?text=hello%20world"
74+
```
75+
76+
## Endpoints
77+
78+
- `GET /` returns open result + collab config.
79+
- `GET /status` returns current document/session status.
80+
- `GET /insert?text=...` inserts text into the live collaborative doc.
81+
- `GET /download` exports the current session as `.docx` and downloads it.
13.1 KB
Binary file not shown.
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# Non-Disclosure Agreement
2+
3+
**Effective Date:** January 15, 2026
4+
5+
This Non-Disclosure Agreement ("Agreement") is entered into by and between the following parties:
6+
7+
| Party | Name | Role |
8+
|---|---|---|
9+
| Disclosing Party | Meridian Dynamics Inc. | Provider of Confidential Information |
10+
| Receiving Party | Orion Consulting Group LLC | Recipient of Confidential Information |
11+
12+
collectively referred to as the "Parties."
13+
14+
## 1. Purpose
15+
16+
The Disclosing Party intends to share certain proprietary and confidential information with the Receiving Party for the sole purpose of:
17+
18+
1. Evaluating a potential business partnership between the Parties
19+
2. Conducting technical due diligence on the Disclosing Party's platform
20+
3. Preparing a joint proposal for the Albright Municipal Infrastructure Project
21+
22+
## 2. Definition of confidential information
23+
24+
"Confidential Information" means any non-public information disclosed by the Disclosing Party, whether in writing, orally, or by inspection, including but not limited to:
25+
26+
- Trade secrets, inventions, and patent applications
27+
- Software source code, algorithms, and system architecture
28+
- Financial records, projections, and pricing models
29+
- Customer lists, vendor agreements, and sales data
30+
- Marketing strategies and product roadmaps
31+
- Employee records and compensation structures
32+
33+
### 2.1 Exclusions
34+
35+
Confidential Information does not include information that:
36+
37+
- Was publicly available at the time of disclosure
38+
- Becomes publicly available through no fault of the Receiving Party
39+
- Was already known to the Receiving Party prior to disclosure, as documented in writing
40+
- Is independently developed by the Receiving Party without use of the Confidential Information
41+
- Is disclosed with the prior written consent of the Disclosing Party
42+
43+
## 3. Obligations of the receiving party
44+
45+
The Receiving Party agrees to:
46+
47+
1. Hold all Confidential Information in strict confidence
48+
2. Not disclose any Confidential Information to third parties without prior written consent
49+
3. Limit internal access to personnel who:
50+
- Have a legitimate need to know
51+
- Are bound by confidentiality obligations no less restrictive than this Agreement
52+
4. Use the Confidential Information solely for the purposes outlined in Section 1
53+
5. Notify the Disclosing Party immediately upon discovery of any unauthorized disclosure
54+
55+
### 3.1 Permitted disclosures
56+
57+
The Receiving Party may disclose Confidential Information if required by:
58+
59+
- A valid court order or subpoena
60+
- Applicable federal, state, or local law
61+
- A regulatory authority with jurisdiction over the Receiving Party
62+
63+
provided that the Receiving Party gives the Disclosing Party prompt written notice and cooperates in seeking a protective order.
64+
65+
## 4. Term and termination
66+
67+
| Provision | Duration |
68+
|---|---|
69+
| Agreement term | 2 years from the Effective Date |
70+
| Confidentiality obligations | 5 years from the date of each disclosure |
71+
| Return of materials | 30 days after termination |
72+
73+
Either Party may terminate this Agreement at any time by providing 30 days' written notice to the other Party. Upon termination, the Receiving Party shall:
74+
75+
1. Cease all use of Confidential Information
76+
2. Return or destroy all copies of Confidential Information in its possession
77+
3. Provide written certification of destruction within 30 days
78+
79+
## 5. Remedies
80+
81+
The Parties acknowledge that:
82+
83+
- Monetary damages may be insufficient to remedy a breach of this Agreement
84+
- The Disclosing Party shall be entitled to seek injunctive relief in addition to any other remedies available at law or in equity
85+
- The prevailing party in any legal action arising from this Agreement shall be entitled to recover reasonable attorneys' fees and costs
86+
87+
## 6. General provisions
88+
89+
### 6.1 Governing law
90+
91+
This Agreement shall be governed by and construed in accordance with the laws of the State of Delaware, without regard to its conflict of laws principles.
92+
93+
### 6.2 Entire agreement
94+
95+
This Agreement constitutes the entire understanding between the Parties with respect to the subject matter hereof and supersedes all prior negotiations, representations, and agreements.
96+
97+
### 6.3 Amendment
98+
99+
No modification of this Agreement shall be effective unless made in writing and signed by both Parties.
100+
101+
### 6.4 Severability
102+
103+
If any provision of this Agreement is held to be invalid or unenforceable, the remaining provisions shall continue in full force and effect.
104+
105+
---
106+
107+
**IN WITNESS WHEREOF**, the Parties have executed this Agreement as of the Effective Date.
108+
109+
| | Meridian Dynamics Inc. | Orion Consulting Group LLC |
110+
|---|---|---|
111+
| Signature | _________________________ | _________________________ |
112+
| Name | Dr. Elena Vasquez | Marcus Chen |
113+
| Title | Chief Executive Officer | Managing Partner |
114+
| Date | January 15, 2026 | January 15, 2026 |
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
from __future__ import annotations
2+
3+
import logging
4+
from importlib.metadata import PackageNotFoundError, version
5+
from pathlib import Path
6+
7+
from fastapi import FastAPI, Query
8+
from fastapi.responses import FileResponse
9+
from superdoc import SuperDocClient
10+
11+
REPO_ROOT = Path(__file__).resolve().parents[3]
12+
EXAMPLE_ROOT = Path(__file__).resolve().parent
13+
14+
# Hardcoded demo config.
15+
DOC_PATH = EXAMPLE_ROOT / "assets" / "doc-template.docx"
16+
MARKDOWN_PATH = EXAMPLE_ROOT / "assets" / "fake-nda.md"
17+
DOWNLOAD_PATH = EXAMPLE_ROOT / ".superdoc-state" / "download.docx"
18+
19+
COLLAB_PROVIDER = "y-websocket"
20+
COLLAB_URL = "ws://127.0.0.1:8081/v1/collaboration"
21+
COLLAB_DOCUMENT_ID = "superdoc-dev-room"
22+
COLLAB_SYNC_TIMEOUT_MS = 60_000
23+
24+
# Hardcode local CLI + state to keep behavior deterministic for this repo example.
25+
# Using .ts means the SDK will execute via bun.
26+
CLI_BIN = REPO_ROOT / "apps" / "cli" / "src" / "index.ts"
27+
CLI_STATE_DIR = EXAMPLE_ROOT / ".superdoc-state"
28+
CLIENT_ENV = {
29+
"SUPERDOC_CLI_BIN": str(CLI_BIN),
30+
"SUPERDOC_CLI_STATE_DIR": str(CLI_STATE_DIR),
31+
}
32+
33+
# Keep open timeout above sync timeout, and watchdog above open timeout.
34+
OPEN_TIMEOUT_MS = 90_000
35+
WATCHDOG_TIMEOUT_MS = 120_000
36+
37+
app = FastAPI(title="SuperDoc FastAPI Collaboration Demo")
38+
logger = logging.getLogger("uvicorn.error")
39+
40+
try:
41+
SUPERDOC_SDK_VERSION = version("superdoc-sdk")
42+
except PackageNotFoundError:
43+
SUPERDOC_SDK_VERSION = "not installed"
44+
45+
46+
@app.on_event("startup")
47+
def on_startup() -> None:
48+
logger.info("superdoc-sdk version: %s", SUPERDOC_SDK_VERSION)
49+
50+
client = SuperDocClient(
51+
env=CLIENT_ENV,
52+
watchdog_timeout_ms=WATCHDOG_TIMEOUT_MS,
53+
)
54+
55+
open_result = client.doc.open(
56+
{
57+
"doc": str(DOC_PATH),
58+
"collaboration": {
59+
"providerType": COLLAB_PROVIDER,
60+
"url": COLLAB_URL,
61+
"documentId": COLLAB_DOCUMENT_ID,
62+
"syncTimeoutMs": COLLAB_SYNC_TIMEOUT_MS,
63+
},
64+
},
65+
timeout_ms=OPEN_TIMEOUT_MS,
66+
)
67+
markdown_content = MARKDOWN_PATH.read_text(encoding="utf-8")
68+
client.doc.insert({"value": markdown_content, "type": "markdown"})
69+
70+
app.state.client = client
71+
app.state.open_result = open_result
72+
73+
74+
@app.on_event("shutdown")
75+
def on_shutdown() -> None:
76+
client = app.state.client
77+
client.doc.close({})
78+
client.dispose()
79+
80+
81+
@app.get("/")
82+
def root() -> dict:
83+
return {
84+
"ok": True,
85+
"openResult": app.state.open_result,
86+
"collab": {
87+
"providerType": COLLAB_PROVIDER,
88+
"url": COLLAB_URL,
89+
"documentId": COLLAB_DOCUMENT_ID,
90+
},
91+
}
92+
93+
94+
@app.get("/status")
95+
def status() -> dict:
96+
return app.state.client.doc.status({})
97+
98+
99+
@app.get("/insert")
100+
def insert(text: str = Query(...)) -> dict:
101+
return app.state.client.doc.insert({"value": text})
102+
103+
104+
@app.get("/download")
105+
def download() -> FileResponse:
106+
DOWNLOAD_PATH.parent.mkdir(parents=True, exist_ok=True)
107+
app.state.client.doc.save({"out": str(DOWNLOAD_PATH), "force": True})
108+
return FileResponse(
109+
path=str(DOWNLOAD_PATH),
110+
media_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
111+
filename=DOWNLOAD_PATH.name,
112+
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fastapi>=0.110.0
2+
uvicorn>=0.27.0
3+
superdoc-sdk==1.0.0a20
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)"
5+
cd "$ROOT_DIR"
6+
7+
echo "[collab] starting server from packages/superdoc (same as pnpm dev:collab)"
8+
pnpm --prefix packages/superdoc run collab-server
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
ROOT_DIR="$(cd "$(dirname "$0")" && pwd)"
5+
cd "$ROOT_DIR/yjs-hub"
6+
7+
if ! node -e "require.resolve('@y/hub/package.json')" >/dev/null 2>&1; then
8+
pnpm install --ignore-workspace --lockfile=false
9+
fi
10+
11+
USE_DOCKER=1
12+
if [ "${1:-}" = "--no-docker" ]; then
13+
USE_DOCKER=0
14+
fi
15+
16+
if [ "$USE_DOCKER" -eq 1 ]; then
17+
if docker info >/dev/null 2>&1; then
18+
pnpm run deps:up
19+
else
20+
echo "[yjs-hub] Docker daemon is not running."
21+
echo "[yjs-hub] Start Docker Desktop and retry, or run: ./run-yjs-hub.sh --no-docker"
22+
echo "[yjs-hub] In --no-docker mode you must provide local Redis (6379) and Postgres (5432/yhub)."
23+
exit 1
24+
fi
25+
fi
26+
27+
pnpm run dev

0 commit comments

Comments
 (0)