Zum Inhalt springen
Docs
Deutsch
Esc
navigateopen⌘Jpreview
Auf dieser Seite

Recipes

Practical Fuxam API v4 patterns — sync users, look up courses by code, safe ETag updates, and idempotent creates.

These recipes are end-to-end patterns built only on the documented v4 conventions: Basic Auth, the { data, meta } envelope, cursor pagination, Idempotency-Key, and ETag / If-Match. Use them as templates in your middleware; swap resource paths as needed and confirm field names on the generated endpoint reference pages.

Base URL: https://fuxam.app/api/v4

Why recipes help

Reference pages show one operation at a time. Real integrations chain operations: list → filter → get → patch, or create → retry. The patterns below encode those sequences so university IT teams can ship reliable sync jobs without rediscovering concurrency and pagination edge cases.

Before you start

Recipe: Sync all users

Goal: Pull every user into an external system using cursor pagination.

When to use: Nightly identity sync with User Management as the source of truth for who exists in Fuxam.

#!/usr/bin/env bash
set -euo pipefail

BASE="https://fuxam.app/api/v4"
CURSOR=""
LIMIT=100

while true; do
  if [ -z "$CURSOR" ]; then
    URL="${BASE}/users?limit=${LIMIT}&fields=id,email"
  else
    URL="${BASE}/users?limit=${LIMIT}&fields=id,email&cursor=${CURSOR}"
  fi

  RESP=$(curl -sS -u "${CLIENT_ID}:${CLIENT_SECRET}" \
    -H "Accept: application/json" "$URL")

  # Upsert each object in RESP.data into your SIS / directory mirror.
  printf '%s\n' "$RESP" | jq -c '.data[]'

  HAS_MORE=$(printf '%s' "$RESP" | jq -r '.pagination.has_more')
  CURSOR=$(printf '%s' "$RESP" | jq -r '.pagination.next_cursor // empty')

  if [ "$HAS_MORE" != "true" ]; then
    break
  fi
done

Pseudocode:

cursor = null
loop:
  response = GET /users?limit=100&fields=id,email[&cursor=cursor]
  upsert_all(response.data)
  if response.pagination.has_more is false: break
  cursor = response.pagination.next_cursor

Recipe: Look up a course by code, then read by id

Goal: Resolve a human-facing course code to a stable id, then fetch the full resource.

When to use: Migrations from v3 (where code addressing was more common), SIS imports that only know course codes, or linking to courses staff manage in Courses Management.

CODE="CS101"

LIST=$(curl -sS -u "${CLIENT_ID}:${CLIENT_SECRET}" \
  -H "Accept: application/json" \
  "https://fuxam.app/api/v4/courses?code=${CODE}&limit=25")

COURSE_ID=$(printf '%s' "$LIST" | jq -r '.data[0].id // empty')

if [ -z "$COURSE_ID" ]; then
  echo "No course found for code ${CODE}" >&2
  exit 1
fi

curl -sS -u "${CLIENT_ID}:${CLIENT_SECRET}" \
  -H "Accept: application/json" \
  -D - \
  "https://fuxam.app/api/v4/courses/${COURSE_ID}"

HTTP sequence:

GET /api/v4/courses?code=CS101&limit=25 HTTP/1.1
Host: fuxam.app
Authorization: Basic <base64(clientId:clientSecret)>
Accept: application/json
GET /api/v4/courses/{id} HTTP/1.1
Host: fuxam.app
Authorization: Basic <base64(clientId:clientSecret)>
Accept: application/json

Recipe: Safely update a resource with ETag

Goal: Change a single resource without overwriting someone else’s concurrent edit.

When to use: Any automated PATCH, PUT, or DELETE on a single resource that emits ETag on GET.

USER_ID="usr_123"

# 1) Read current version
HEADERS=$(mktemp)
BODY=$(mktemp)

curl -sS -u "${CLIENT_ID}:${CLIENT_SECRET}" \
  -H "Accept: application/json" \
  -D "$HEADERS" \
  -o "$BODY" \
  "https://fuxam.app/api/v4/users/${USER_ID}"

ETAG=$(awk 'BEGIN{IGNORECASE=1} /^ETag:/{print $2}' "$HEADERS" | tr -d '\r')

# 2) Write with If-Match
HTTP_CODE=$(curl -sS -u "${CLIENT_ID}:${CLIENT_SECRET}" \
  -o /tmp/patch-response.json -w "%{http_code}" \
  -X PATCH \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -H "If-Match: ${ETAG}" \
  -d @patch-body.json \
  "https://fuxam.app/api/v4/users/${USER_ID}")

if [ "$HTTP_CODE" = "412" ]; then
  echo "precondition_failed — re-GET, merge, retry" >&2
  exit 1
fi

Pseudocode:

resource, etag = GET /users/{id}          # capture ETag header
try:
  PATCH /users/{id} with If-Match: etag
catch 412 precondition_failed:
  resource, etag = GET /users/{id} again
  merge_changes(resource)
  retry PATCH with new etag
catch 428 precondition_required:
  ensure If-Match is sent

Recipe: Create with idempotency

Goal: Create a resource once, even if the HTTP client retries after a timeout.

When to use: Provisioning jobs, webhook consumers that may deliver twice, any create that must not duplicate.

IDEMPOTENCY_KEY="$(uuidgen)"

curl -sS \
  -u "${CLIENT_ID}:${CLIENT_SECRET}" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: ${IDEMPOTENCY_KEY}" \
  -d @create-body.json \
  "https://fuxam.app/api/v4/users"
POST /api/v4/users HTTP/1.1
Host: fuxam.app
Authorization: Basic <base64(clientId:clientSecret)>
Content-Type: application/json
Idempotency-Key: a1b2c3d4-e5f6-7890-abcd-ef1234567890
Accept: application/json

# Body: JSON create payload from the endpoint reference

Behavior to expect:

Situation Result
First successful create 201 with the new resource in data
Retry with same key and equivalent create Cached original response (including 201) — no duplicate
Conflicting in-flight key May return 409 — wait and retry with the same key, or inspect conflict handling in your job

Recipe: Handle rate limits while paging

Goal: Finish a large sync without failing the job when you hit 1000 requests/min.

loop pages:
  response = GET list
  if status == 429:
    sleep(Retry-After)
    retry same request
  process response.data
  advance cursor
# Illustrative fragment — honor Retry-After from response headers
RETRY_AFTER=60
sleep "$RETRY_AFTER"

Pair this with larger limit values so you need fewer requests per full walk. Details: Errors and idempotency.

Choosing a pattern

Need Recipe
Mirror the directory Sync all users
SIS knows course codes only Look up by code, then by id
Update without clobbering UI edits Safely update with ETag
Provision without duplicates Create with idempotency
Large sync under load Rate-limit-aware paging

Best practices across recipes

  • Log meta.requestId on every response.
  • Prefer server-side jobs over browser-side calls with long-lived secrets.
  • Separate integrations per workload so rate limits and rotations stay isolated (Authentication).
  • After coding a recipe, open the matching reference card from the Overview browse section to confirm request/response schemas.

Common pitfalls

Pitfall Better approach
Syncing with limit=1 Use up to 100 for bulk jobs
Creating without Idempotency-Key Always set a UUID per logical create
Patching without If-Match GET first; send ETag
Assuming code is the primary key for courses Resolve code → id, then use id

FAQ

Can I adapt these recipes to curricula or rooms?

Yes — the envelope, pagination, auth, idempotency, and ETag rules are shared. Replace paths with the resource you need from the overview browse cards (for example curricula or rooms), and confirm filters on that endpoint’s reference page.

Do recipes replace the OpenAPI reference?

No. Recipes teach sequences and conventions. Field-level schemas live on the generated endpoint pages such as Users and Courses.

War diese Seite hilfreich?