Skip to content

spurin/rest-labspace

Repository files navigation

REST Labspace

A hands-on teaching app for REST APIs. Students click endpoints in the Character Lab and watch the character react in real time. The lessons in labspace/ walk through GET / PUT / PATCH / DELETE / POST and are surfaced by the Docker Labspace tutorial pane at runtime.

Stack

  • Backend - Python 3.12 + FastAPI + Uvicorn, in-memory state, auto Swagger UI at /docs.
  • Frontend - React + TypeScript + Vite, served by nginx in prod. The character is a @bigheads/core avatar bound to CharacterState - every Big Heads option is a controllable input.
  • Labspace shell - provided at runtime by the published OCI artifact oci://dockersamples/labspace-content-dev (tutorial pane, IDE, service tabs).
  • Two app services - backend on :8000, frontend on :3030; CORS enabled.

Run as a Labspace (tutorial pane + IDE + service tabs)

CONTENT_PATH=$PWD docker compose up --watch

compose.yaml is a 2-line stub that include:s the Docker-provided Labspace runtime (oci://dockersamples/labspace-content-dev) plus our compose.override.yaml. The override pulls the published images from Docker Hub - no local builds.

Run standalone (just the lab UI + API, published images)

docker compose -f compose.override.yaml up

Pulls spurin/rest-labspace-{backend,frontend} from Docker Hub.

Local development (build from source)

Use the dev overlay which adds build: + develop.watch on top of the publish-ready override:

docker compose -f compose.override.yaml -f compose.dev.yaml up --build --watch

Or no Docker at all - two terminals:

# backend
cd backend
python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
.venv/bin/uvicorn app.main:app --reload --port 8000
# frontend
cd frontend
npm install
npm run dev   # http://localhost:5173

The frontend reads VITE_API_BASE (default http://localhost:8000) for both fetch calls and the curl preview.

Tutorial flow

  1. GET /api/character - read current state
  2. PUT /api/character/hair {"color":"blue","style":"afro"} - replace one full sub-resource
  3. PUT /api/character/accessories/glasses {"value":"shades"} - set a value
  4. DELETE /api/character/accessories/glasses - reset to "none"
  5. PATCH /api/character {"hair":{"color":"pink"},"face":{"eyes":"heart"}} - multi-field partial update
  6. POST /api/character/interactions {"type":"wink"} - fire a one-shot animation

Each step is also walked through in labspace/ - those markdown files are the lessons surfaced by the Docker Labspace tutorial pane.

Project layout

backend/             FastAPI service (in-memory state)
frontend/            React + Vite + Big Heads avatar
labspace/            Markdown lessons surfaced by the Docker Labspace runtime
labspace.yaml        Labspace metadata (title, sections, services)
compose.yaml         Labspace include stub (oci://… + override)
compose.override.yaml  Publish-ready services referencing pushed images
compose.dev.yaml     Local-dev overlay: adds build: + develop.watch
build-and-push.sh    Multi-arch (amd64 + arm64) image build & push

Build & push images

Multi-arch (linux/amd64 + linux/arm64) build & push to Docker Hub. Assumes docker login already done.

./build-and-push.sh           # tags as :latest
./build-and-push.sh 0.2.0     # tags as :0.2.0 plus :latest

Pushes:

  • spurin/rest-labspace-backend:<version>
  • spurin/rest-labspace-frontend:<version>

Publish as a Docker Labspace

Two steps: push the images, then publish the OCI artifact that bundles the compose stack and lesson tarball.

Step 1 - push images (multi-arch, Docker Hub):

./build-and-push.sh

Step 2 - publish the Labspace artifact. We layer three compose configs together: the Docker-provided Labspace base, our app services, and a generated pipeline file that inlines the labspace/ + project/ content as a base64 tarball. The snippet below tar-encodes the content, writes the two temp files, then publishes:

tar -czf /tmp/project.tar.gz labspace project
B64=$(base64 < /tmp/project.tar.gz | tr -d '\n')

cat > /tmp/compose.override.yaml <<'EOF'
services:
  backend:
    image: spurin/rest-labspace-backend:latest
    pull_policy: always
    container_name: rest-labspace-backend
    ports:
      - "8000:8000"
    environment:
      CORS_ORIGINS: "*"
    labels:
      labspace-resource: "true"

  frontend:
    image: spurin/rest-labspace-frontend:latest
    pull_policy: always
    container_name: rest-labspace-frontend
    ports:
      - "3031:80"
    depends_on:
      - backend
    labels:
      labspace-resource: "true"
EOF

cat > /tmp/compose.pipeline.yaml <<EOF
configs:
  labspace-content:
    content: "$B64"

services:
  configurator:
    configs:
      - source: labspace-content
        target: /etc/labspace-support/content.tar.base64
    environment:
      PROJECT_TAR_PATH: /etc/labspace-support/content.tar.base64
      PROJECT_CLONE_URL: https://github.com/spurin/rest-labspace
EOF

CONTENT_PATH=/tmp docker compose \
  -f oci://dockersamples/labspace:latest \
  -f /tmp/compose.override.yaml \
  -f /tmp/compose.pipeline.yaml \
  publish spurin/rest-labspace:latest --with-env -y

Consumers launch the lab with:

docker compose -f oci://docker.io/spurin/rest-labspace:latest up

Licence

The Unlicense - this code is dedicated to the public domain. See LICENSE.

About

Docker Desktop Labspace - Learning REST API's

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors