diff --git a/.github/workflows/contract.yml b/.github/workflows/contract.yml
index 014cae4..b6f3664 100644
--- a/.github/workflows/contract.yml
+++ b/.github/workflows/contract.yml
@@ -29,7 +29,13 @@ jobs:
run: |
uv sync
uv run uvicorn app.api:app --port 7000 &
- timeout 30 bash -c 'until curl -sf http://localhost:7000/ > /dev/null; do sleep 1; done'
+ timeout 30 bash -c 'until curl -sf http://localhost:7000/health > /dev/null; do sleep 1; done'
+
+ - name: "Contract: GET /health returns ok"
+ run: |
+ body=$(curl -sf http://localhost:7000/health)
+ echo "$body"
+ echo "$body" | jq -e '.status == "ok"'
- name: "Contract: GET / returns version info"
run: |
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index ff0eb34..d506944 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -33,8 +33,8 @@ jobs:
- name: Build and start the stack
run: docker compose up -d --build
- - name: Wait for the stack (nginx up and backend responding through it)
- run: timeout 60 bash -c 'until curl -sf http://localhost:8080/api/ > /dev/null; do sleep 2; done'
+ - name: Wait for the stack (nginx up and backend healthy through it)
+ run: timeout 60 bash -c 'until curl -sf http://localhost:8080/api/health > /dev/null; do sleep 2; done'
- name: "Frontend served"
run: curl -sf http://localhost:8080/ | grep -q "
React Template"
diff --git a/README.md b/README.md
index a3a8c69..342407f 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# react-template
+# react-fastapi-template
Fullstack template at Komorebi AI: a **React frontend** (`frontend/`) paired with a
**FastAPI backend** (`backend/`). Use it as the starting point for technical tests,
@@ -63,13 +63,15 @@ contract fails visibly.
The frontend's typed endpoint wrappers (`frontend/src/api/backend.ts`) match
`backend/app/api.py`:
-| Endpoint | Response | Used for |
-| ------------------- | -------------------------------- | -------------------- |
-| `GET /api/` | `{"app-api": "version ..."}` | status/version chip |
-| `POST /api/predict` | `{"input": n}` → `{"output": n}` | mock prediction demo |
+| Endpoint | Response | Used for |
+| ------------------- | -------------------------------- | -------------------------- |
+| `GET /api/` | `{"app-api": "version ..."}` | status/version chip |
+| `POST /api/predict` | `{"input": n}` → `{"output": n}` | mock prediction demo |
+| `GET /api/health` | `{"status": "ok"}` | container healthchecks, CI |
Both proxies (Vite in dev, nginx in prod) strip the `/api` prefix before forwarding to
-the backend.
+the backend. `/health` is the stable endpoint — keep it when replacing the example
+endpoints, since the compose healthcheck and CI smoke tests rely on it.
## Starting a project from this template
diff --git a/backend/app/api.py b/backend/app/api.py
index 2bb70a3..93994b7 100644
--- a/backend/app/api.py
+++ b/backend/app/api.py
@@ -40,6 +40,16 @@ def read_root() -> dict[str, str]:
return {"app-api": f"version {__version__}"}
+@app.get("/health")
+def health() -> dict[str, str]:
+ """Health check for container orchestration and monitoring.
+
+ Stable endpoint: keep it when replacing the example endpoints below,
+ since Docker healthchecks and CI smoke tests rely on it.
+ """
+ return {"status": "ok"}
+
+
@app.post("/predict")
def predict(request: Request) -> Response:
"""Mock prediction endpoint."""
diff --git a/compose.yaml b/compose.yaml
index e61744e..defe62f 100644
--- a/compose.yaml
+++ b/compose.yaml
@@ -3,6 +3,19 @@ services:
build: ./backend
# The backend image runs uvicorn on port 80 (see backend/Dockerfile).
# Not published to the host: the frontend's nginx proxies /api to it.
+ healthcheck:
+ # python (always present in the image) instead of curl/wget (not installed)
+ test:
+ [
+ "CMD",
+ "python",
+ "-c",
+ "import urllib.request; urllib.request.urlopen('http://127.0.0.1:80/health')",
+ ]
+ interval: 10s
+ timeout: 3s
+ retries: 5
+ start_period: 5s
frontend:
build: ./frontend
@@ -12,4 +25,5 @@ services:
# Same nginx config as the image default, plus the /api -> backend proxy.
- ./frontend/docker/nginx.compose.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- - backend
+ backend:
+ condition: service_healthy
diff --git a/docs/backend-sync.md b/docs/backend-sync.md
index 81385de..ac973ce 100644
--- a/docs/backend-sync.md
+++ b/docs/backend-sync.md
@@ -19,7 +19,7 @@ three post-render adjustments:
backend's pyproject is not at the git root, so the version must be derived from this
repo's metadata. (Cleaner long-term fix: an `scm_root` question in the template.)
3. **Point `_src_path`** in `.copier-answers.yml` at the template's GitHub URL and keep
- the file — projects created from react-template use it to run `copier update` on
+ the file — projects created from react-fastapi-template use it to run `copier update` on
their own backends.
Then `uv lock` (with `SETUPTOOLS_SCM_PRETEND_VERSION`) and `uvx sync-with-uv`, exactly
@@ -56,11 +56,11 @@ red PR instead of a silent break.
## Workflow for python-copier-template
-Ready to drop in as `.github/workflows/sync-react-template.yml` (mirrors the existing
+Ready to drop in as `.github/workflows/sync-react-fastapi-template.yml` (mirrors the existing
`sync-template.yml`; requires the same `GH_TOKEN` secret):
```yaml
-name: sync-react-template
+name: sync-react-fastapi-template
on:
push:
@@ -88,9 +88,9 @@ jobs:
run: |
copier copy --defaults --vcs-ref=HEAD \
--data project_name="React Template Backend" \
- --data project_description="FastAPI backend for react-template" \
+ --data project_description="FastAPI backend for react-fastapi-template" \
--data package_name="app" \
- --data github_repo="react-template" \
+ --data github_repo="react-fastapi-template" \
--data project_type="application" \
--data python_version="3.14" \
--data include_api=true \
@@ -98,10 +98,15 @@ jobs:
--data include_docker=true \
. /tmp/rendered
+ # backend/ is a subdirectory of react-fastapi-template, not a repo root: its
+ # workflows are owned by react-fastapi-template (rendered ones assume repo root)
+ # and its version must derive from react-fastapi-template's git metadata.
+ # Keep .copier-answers.yml (pointed at this repo's URL) so projects
+ # created from react-fastapi-template can `copier update` their backends.
- name: Adjust render for subdirectory embedding
run: |
rm -rf /tmp/rendered/.github
- sed -i 's|^\[tool.setuptools_scm\]$|[tool.setuptools_scm]\nroot = ".."|' \
+ sed -i 's|^\[tool.setuptools_scm\]$|[tool.setuptools_scm]\n# Backend lives in a subdirectory of the react-fastapi-template repo; version comes\n# from the repo root git metadata (applied by the template sync job)\nroot = ".."|' \
/tmp/rendered/pyproject.toml
sed -i 's|^_src_path:.*|_src_path: https://github.com/Komorebi-AI/python-copier-template.git|' \
/tmp/rendered/.copier-answers.yml
@@ -116,22 +121,22 @@ jobs:
run: uvx sync-with-uv
working-directory: /tmp/rendered
- - name: Checkout react-template
+ - name: Checkout react-fastapi-template
uses: actions/checkout@v6
with:
- repository: Komorebi-AI/react-template
+ repository: Komorebi-AI/react-fastapi-template
token: ${{ secrets.GH_TOKEN }}
- path: react-template
+ path: react-fastapi-template
- name: Replace backend/
run: |
- rm -rf react-template/backend
- mkdir react-template/backend
- cp -r /tmp/rendered/. react-template/backend/
+ rm -rf react-fastapi-template/backend
+ mkdir react-fastapi-template/backend
+ cp -r /tmp/rendered/. react-fastapi-template/backend/
- name: Check for changes
id: changes
- working-directory: react-template
+ working-directory: react-fastapi-template
run: |
git add -A
if git diff --cached --quiet; then
@@ -142,7 +147,7 @@ jobs:
- name: Create pull request
if: steps.changes.outputs.has_changes == 'true'
- working-directory: react-template
+ working-directory: react-fastapi-template
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
run: |
@@ -158,10 +163,10 @@ jobs:
if ! gh pr list --head "$BRANCH" --json number --jq '.[0].number' | grep -q .; then
gh pr create \
--title "sync: update backend/ from python-copier-template" \
- --body "Automated sync of backend/ from python-copier-template."
+ --body "Automated sync of backend/ from [python-copier-template](https://github.com/Komorebi-AI/python-copier-template)."
fi
- # Requires react-template branch protection with the Backend and
+ # Requires react-fastapi-template branch protection with the Backend and
# Contract checks required, and "Allow auto-merge" enabled.
gh pr merge "$BRANCH" --auto --squash
```
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index e374791..521ca33 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -1,11 +1,11 @@
{
- "name": "react-template",
+ "name": "react-fastapi-template",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
- "name": "react-template",
+ "name": "react-fastapi-template",
"version": "1.0.0",
"dependencies": {
"@emotion/react": "^11.14.0",
diff --git a/frontend/package.json b/frontend/package.json
index e1d9e2e..a1fcf0f 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,5 +1,5 @@
{
- "name": "react-template",
+ "name": "react-fastapi-template",
"version": "1.0.0",
"private": true,
"type": "module",