{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreigjxgejvxf77xzyem2i72aclts34kiypfxngxlwvbje5gc5u6d2vu",
"uri": "at://did:plc:4hfnzpcvsy5k3uwjqhaynx45/app.bsky.feed.post/3mmqr7jp4aac2"
},
"description": "APIs sind überall. Jede App, jeder Dienst, jedes Dashboard will irgendwo Daten holen, schicken oder so tun, als wäre alles nahtlos. Zeit, diesen unsichtbaren Kellnern des Internets mal auf die Finger zu schauen. Mit Python, FastAPI und genug Sarkasmus für drei Statuscodes.",
"path": "/apis-die-kellner-des-internets-die-standig-missverstanden-werden/",
"publishedAt": "2026-05-26T10:16:46.000Z",
"site": "https://codeundkram.de",
"tags": [
"Was ist eine API?",
"Warum APIs überall sind",
"HTTP: Die Sprache der Web-APIs",
"REST oder die Kunst, Dinge vernünftig zu benennen",
"JSON: Das Laminat der Datenformate",
"Statuscodes: Die Ampel des Backends",
"Wir bauen eine kleine API mit FastAPI",
"Projektstruktur",
"Der erste Endpunkt",
"Datenmodell mit Pydantic",
"CRUD ohne Datenbank",
"Fehler sauber zurückgeben",
"Authentifizierung mit API-Key",
"curl: Testen wie ein Erwachsener",
"Was diese API noch nicht kann",
"Fazit",
"@app.get",
"@app.post",
"@app.put",
"@app.delete"
],
"textContent": "APIs sind überall.\n\nAuf dem Handy. Im Browser. In Apps. In Smart-Home-Systemen. In Buchhaltungstools. In Wetter-Widgets. In diesen traurigen Unternehmensportalen, bei denen man nach dem Login sofort merkt, dass irgendwo ein Backend weint.\n\nUnd trotzdem klingen APIs für viele immer noch wie etwas, das nur Menschen mit Hoodie, Koffeinproblem und einer beunruhigend engen Beziehung zu Terminalfenstern verstehen.\n\nDabei ist eine API im Kern gar nicht so kompliziert.\n\nEine API ist eine Schnittstelle.\n\nAlso ein geregelter Weg, wie ein Programm mit einem anderen Programm spricht.\n\nDas ist alles.\n\nNatürlich hat die IT daraus wieder einen ganzen Zoo gebaut: REST, GraphQL, SOAP, JSON, XML, OAuth, Tokens, Rate Limits, Webhooks, Versionierung, Statuscodes, CORS und andere Begriffe, die klingen, als hätte jemand einen Router in einen Scrabble-Beutel geworfen.\n\nAber im Kern bleibt es simpel:\n\nEin Programm fragt etwas.\n\nEin anderes Programm antwortet.\n\nWenn alles gut läuft.\n\nWenn nicht, kommt `500 Internal Server Error`, und irgendwo sagt jemand: „Komisch, bei mir lokal ging’s.“\n\n## Inhaltsverzeichnis\n\n 1. Was ist eine API?\n 2. Warum APIs überall sind\n 3. HTTP: Die Sprache der Web-APIs\n 4. REST oder die Kunst, Dinge vernünftig zu benennen\n 5. JSON: Das Laminat der Datenformate\n 6. Statuscodes: Die Ampel des Backends\n 7. Wir bauen eine kleine API mit FastAPI\n 8. Projektstruktur\n 9. Der erste Endpunkt\n 10. Datenmodell mit Pydantic\n 11. CRUD ohne Datenbank\n 12. Fehler sauber zurückgeben\n 13. Authentifizierung mit API-Key\n 14. curl: Testen wie ein Erwachsener\n 15. Was diese API noch nicht kann\n 16. Fazit\n\n\n\n* * *\n\n## 1. Was ist eine API?\n\nAPI steht für **Application Programming Interface**.\n\nAuf Deutsch: Programmierschnittstelle.\n\nDas klingt sofort nach DIN-Ordner, Fachabteilung und einem Meeting, das auch eine E-Mail hätte sein können. Deshalb bleiben wir lieber bei API.\n\nEine API legt fest, wie Software miteinander spricht.\n\nWenn du eine Wetter-App öffnest, hat diese App nicht selbst kleine Wetterfrösche im Gerät. Sie fragt einen Wetterdienst über eine API:\n\n> Wie wird das Wetter in Hannover?\n\nDer Wetterdienst antwortet:\n\n> 8 Grad, Regen, Wind, also Hannover.\n\nWenn du in einer Shopping-App eine Bestellung aufgibst, redet die App mit APIs für Produkte, Warenkorb, Zahlung, Versand und wahrscheinlich noch acht Trackingdiensten, die wissen möchten, ob du bei „Socken, 3er-Pack“ emotional gezögert hast.\n\nWenn dein Smart-Home-System eine Lampe einschaltet, geht auch irgendwo eine API-Anfrage los.\n\nUnd wenn die Lampe nicht reagiert, nennt man das Fortschritt.\n\nEine API ist also nicht magisch. Sie ist ein Vertrag.\n\nNicht juristisch. Technisch.\n\nSie sagt:\n\n * Diese Adresse kannst du aufrufen.\n * Diese Daten musst du schicken.\n * Diese Antwort bekommst du zurück.\n * Wenn du Unsinn machst, bekommst du einen Fehler.\n\n\n\nIm besten Fall ist dieser Vertrag gut dokumentiert.\n\nIm schlechtesten Fall gibt es eine Wiki-Seite von 2018, drei veraltete Beispiele und einen Entwickler, der sagt: „Das müsste eigentlich gehen.“\n\nDann weiß man: Es wird ein langer Tag.\n\n* * *\n\n## 2. Warum APIs überall sind\n\nFrüher war Software oft ein großer Klotz.\n\nEin Programm. Eine Datenbank. Ein Server. Eine Oberfläche. Alles zusammen. Wenn es kaputtging, ging wenigstens alles kaputt. Das war ehrlich.\n\nHeute besteht Software oft aus vielen Teilen.\n\nFrontend hier. Backend da. Authentifizierung woanders. Zahlungsdienst extern. E-Mail-Versand über Dienstleister. Suche über eigene Engine. Dateien in der Cloud. Monitoring in noch einer Cloud. Und irgendwo ein Kubernetes-Cluster, das nachts Geräusche macht.\n\nAPIs halten diesen ganzen Zirkus zusammen.\n\nSie sind die Kellner des Internets.\n\nDas Frontend sagt:\n\n> Ich hätte gern die Liste der letzten Blogartikel.\n\nDie API geht in die Küche, fragt die Datenbank, richtet alles in JSON an und bringt es zurück.\n\nDas Frontend sagt:\n\n> Danke, aber bitte ohne Passwort-Hash.\n\nDie API sagt hoffentlich:\n\n> Natürlich.\n\nWenn nicht, nennt man das Datenpanne.\n\nAPIs machen Systeme modular. Man kann eine App bauen, die mit einem Backend spricht. Man kann mehrere Frontends an dieselbe API hängen. Eine Website. Eine Mobile App. Ein internes Dashboard. Ein kleines CLI-Tool für Leute, die grafische Oberflächen als persönliche Beleidigung empfinden.\n\nAPIs ermöglichen Automatisierung.\n\nStatt irgendwo manuell Daten einzutragen, kann ein System sie senden. Statt Tabellen per E-Mail herumzuschubsen, können Systeme miteinander reden. Statt Menschen mit Copy & Paste zu quälen, quält man jetzt Entwickler mit OAuth.\n\nFortschritt ist relativ.\n\n* * *\n\n## 3. HTTP: Die Sprache der Web-APIs\n\nDie meisten modernen APIs im Web sprechen HTTP.\n\nHTTP kennst du vom Browser. Jede Website wird darüber geladen. Aber HTTP kann mehr als nur Webseiten ausliefern. Es ist auch hervorragend geeignet, strukturierte Anfragen zu senden und Antworten zurückzubekommen.\n\nEine HTTP-Anfrage besteht grob aus:\n\n * Methode\n * URL\n * Headern\n * optionalem Body\n\n\n\nDie Methode sagt, was du ungefähr willst.\n\nDie URL sagt, worauf du es anwenden willst.\n\nHeader enthalten Zusatzinformationen.\n\nDer Body enthält Daten, wenn du welche mitschickst.\n\nEin Beispiel:\n\n\n GET /articles HTTP/1.1\n Host: api.example.com\n Accept: application/json\n\n\nDas heißt:\n\n> Gib mir bitte die Artikel. Am liebsten als JSON. Und benimm dich.\n\nEine Antwort könnte so aussehen:\n\n\n HTTP/1.1 200 OK\n Content-Type: application/json\n\n [\n {\n \"id\": 1,\n \"title\": \"DNS: Das Telefonbuch des Internets\"\n }\n ]\n\n\nDas ist im Grunde ein Gespräch.\n\nNur ohne Smalltalk.\n\nUnd ohne „Ich hoffe, diese Mail findet dich gut“.\n\nAllein dafür muss man HTTP mögen.\n\n* * *\n\n## 4. REST oder die Kunst, Dinge vernünftig zu benennen\n\nREST steht für **Representational State Transfer**.\n\nDas klingt wie ein Begriff, den jemand erfunden hat, damit Konferenzfolien wichtiger wirken.\n\nIn der Praxis meint REST meistens: Wir behandeln Dinge als Ressourcen und sprechen sie über URLs an.\n\nZum Beispiel:\n\n\n GET /articles\n GET /articles/42\n POST /articles\n PUT /articles/42\n DELETE /articles/42\n\n\nDas ist schön lesbar.\n\n`GET /articles` holt alle Artikel.\n\n`GET /articles/42` holt Artikel 42.\n\n`POST /articles` erstellt einen neuen Artikel.\n\n`PUT /articles/42` ersetzt oder aktualisiert Artikel 42.\n\n`DELETE /articles/42` löscht Artikel 42.\n\nKlar. Verständlich. Fast schon verdächtig.\n\nNatürlich schaffen es Teams trotzdem, APIs zu bauen wie:\n\n\n POST /doArticleAction\n POST /getArticleById\n GET /deleteUserFinalReallyNow\n\n\nDas ist dann kein REST.\n\nDas ist ein Hilferuf.\n\nGute API-Strukturen sind nicht nur schön. Sie helfen beim Denken. Wenn URLs sauber benannt sind, versteht man schneller, was passiert. Wenn Methoden sinnvoll verwendet werden, muss man nicht jedes Mal raten, ob `POST /user/updateThing` jetzt Daten holt, ändert oder versehentlich einen Praktikanten alarmiert.\n\nREST ist keine Religion.\n\nAber ein bisschen Ordnung schadet selten.\n\nAußer man arbeitet in historisch gewachsenen Enterprise-Systemen. Dann ist Ordnung eine ferne Sage.\n\n* * *\n\n## 5. JSON: Das Laminat der Datenformate\n\nJSON steht für **JavaScript Object Notation**.\n\nEs ist das Datenformat, das heute fast überall benutzt wird, weil es einfach, lesbar und ausreichend langweilig ist.\n\nEin JSON-Objekt sieht so aus:\n\n\n {\n \"id\": 1,\n \"title\": \"APIs: Die Kellner des Internets\",\n \"published\": false,\n \"tags\": [\"API\", \"Wissen\", \"Kram\"]\n }\n\n\nDas kann man lesen.\n\nSogar ohne Zertifikat.\n\nJSON besteht aus Objekten, Arrays, Strings, Zahlen, Booleans und `null`, also dem Datenäquivalent von „weiß ich auch nicht“.\n\nEs ist nicht perfekt.\n\nDatumswerte sind nervig. Kommentare gibt es nicht. Große Zahlen können je nach Sprache komisch werden. Und sobald jemand tief verschachtelte JSON-Strukturen baut, sieht das Ganze aus wie ein IKEA-Regal nach einem Umzug.\n\nAber JSON funktioniert.\n\nUnd in der IT gewinnt oft nicht das Schönste, sondern das, was ausreichend gut funktioniert und von genug Leuten unterstützt wird.\n\nAlso quasi das Gegenteil von Druckertreibern.\n\n* * *\n\n## 6. Statuscodes: Die Ampel des Backends\n\nHTTP-Statuscodes sagen, wie eine Anfrage ausgegangen ist.\n\nDie wichtigsten Gruppen:\n\n * **2xx** : Alles gut.\n * **3xx** : Woanders lang.\n * **4xx** : Du hast Mist geschickt.\n * **5xx** : Der Server hat Mist gebaut.\n\n\n\nEin paar Klassiker:\n\n\n 200 OK\n 201 Created\n 400 Bad Request\n 401 Unauthorized\n 403 Forbidden\n 404 Not Found\n 409 Conflict\n 422 Unprocessable Entity\n 500 Internal Server Error\n\n\n`200 OK` heißt: Hat geklappt.\n\n`201 Created` heißt: Wurde erstellt.\n\n`400 Bad Request` heißt: Deine Anfrage war kaputt.\n\n`401 Unauthorized` heißt: Wer bist du überhaupt?\n\n`403 Forbidden` heißt: Ich weiß, wer du bist, aber du darfst das trotzdem nicht.\n\n`404 Not Found` heißt: Gibt es nicht.\n\n`500 Internal Server Error` heißt: Der Server ist gerade innerlich umgefallen.\n\nGute APIs nutzen Statuscodes sinnvoll.\n\nSchlechte APIs geben immer `200 OK` zurück und schreiben dann in den Body:\n\n\n {\n \"success\": false,\n \"error\": \"Something went wrong\"\n }\n\n\nDas ist ungefähr so hilfreich wie eine Ampel, die immer grün zeigt, aber darunter ein Schild mit „Vielleicht doch rot“ hat.\n\nBitte nicht.\n\n* * *\n\n## 7. Wir bauen eine kleine API mit FastAPI\n\nGenug Theorie.\n\nWir bauen eine kleine API.\n\nNicht riesig. Kein Microservice-Drama. Keine Datenbank. Keine Container-Orchestrierung. Kein Architekturdiagramm, bei dem man danach Urlaub braucht.\n\nNur eine kleine API für Blogartikel.\n\nWir nutzen **Python** und **FastAPI**.\n\nFastAPI ist ein modernes Python-Framework für APIs. Es ist schnell, angenehm lesbar und erzeugt automatisch eine interaktive Dokumentation. Also genau das, was viele APIs dringend bräuchten, aber aus unerklärlichen Gründen durch ein Word-Dokument in SharePoint ersetzen.\n\nInstallation:\n\n\n python -m venv .venv\n source .venv/bin/activate\n pip install fastapi uvicorn\n\n\nUnter Windows sieht die Aktivierung etwas anders aus:\n\n\n python -m venv .venv\n .venv\\Scripts\\Activate.ps1\n pip install fastapi uvicorn\n\n\nDanach können wir loslegen.\n\n* * *\n\n## 8. Projektstruktur\n\nWir halten es simpel:\n\n\n api-demo/\n ├── main.py\n └── requirements.txt\n\n\nIn `requirements.txt` schreiben wir:\n\n\n fastapi\n uvicorn\n\n\nUnd in `main.py` kommt unser Code.\n\nJa, nur eine Datei.\n\nFür ein echtes Projekt wäre das irgendwann zu wenig. Aber für den Anfang ist eine Datei besser als ein Enterprise-Ordnerbaum mit 47 Unterverzeichnissen und keiner Ahnung, wo die Route liegt.\n\n* * *\n\n## 9. Der erste Endpunkt\n\nUnsere erste API:\n\n\n from fastapi import FastAPI\n\n app = FastAPI(title=\"CODEundKRAM API\")\n\n\n @app.get(\"/\")\n def read_root():\n return {\"message\": \"API läuft. Noch. Bitte nichts anfassen.\"}\n\n\nStarten:\n\n\n uvicorn main:app --reload\n\n\nDann im Browser öffnen:\n\n\n http://127.0.0.1:8000/\n\n\nAntwort:\n\n\n {\n \"message\": \"API läuft. Noch. Bitte nichts anfassen.\"\n }\n\n\nGlückwunsch.\n\nDu hast eine API gebaut.\n\nNoch keine gute.\n\nAber eine API.\n\nDas ist wie der erste Kaffee am Montag: technisch vorhanden, emotional noch ausbaufähig.\n\nFastAPI liefert außerdem automatisch Dokumentation:\n\n\n http://127.0.0.1:8000/docs\n\n\nDort kannst du Endpunkte direkt testen.\n\nDas ist sehr angenehm.\n\nUnd ein bisschen gefährlich, weil Menschen plötzlich Dinge anklicken, die sie nicht verstehen.\n\nAlso wie überall im Internet.\n\n* * *\n\n## 10. Datenmodell mit Pydantic\n\nEine API braucht Datenmodelle.\n\nWir wollen Blogartikel verwalten. Jeder Artikel hat:\n\n * ID\n * Titel\n * Slug\n * Inhalt\n * Veröffentlichungsstatus\n\n\n\nFastAPI nutzt Pydantic für Datenvalidierung.\n\nDas heißt: Wir beschreiben, wie Daten aussehen sollen, und FastAPI prüft automatisch, ob eingehende Daten passen.\n\n\n from fastapi import FastAPI\n from pydantic import BaseModel, Field\n\n app = FastAPI(title=\"CODEundKRAM API\")\n\n\n class ArticleCreate(BaseModel):\n title: str = Field(min_length=3, max_length=120)\n slug: str = Field(min_length=3, max_length=160)\n content: str = Field(min_length=1)\n published: bool = False\n\n\n class Article(ArticleCreate):\n id: int\n\n\n`ArticleCreate` beschreibt die Daten, die ein Client zum Erstellen eines Artikels schicken muss.\n\n`Article` erweitert das Modell um eine ID.\n\nWenn jemand versucht, einen Artikel mit leerem Titel zu erstellen, blockt FastAPI das automatisch.\n\nDas ist gut.\n\nDenn wenn man Daten nicht prüft, prüfen sie später die eigene Geduld.\n\n* * *\n\n## 11. CRUD ohne Datenbank\n\nCRUD steht für:\n\n * Create\n * Read\n * Update\n * Delete\n\n\n\nAlso erstellen, lesen, ändern, löschen.\n\nDie vier Grundnahrungsmittel jeder Verwaltungssoftware.\n\nWir bauen das mit einer Liste im Speicher. Keine Datenbank. Das heißt: Nach einem Neustart ist alles weg.\n\nAlso ungefähr wie manche Managemententscheidungen nach dem Quartalsmeeting.\n\n\n from fastapi import FastAPI, HTTPException\n from pydantic import BaseModel, Field\n\n app = FastAPI(title=\"CODEundKRAM API\")\n\n\n class ArticleCreate(BaseModel):\n title: str = Field(min_length=3, max_length=120)\n slug: str = Field(min_length=3, max_length=160)\n content: str = Field(min_length=1)\n published: bool = False\n\n\n class Article(ArticleCreate):\n id: int\n\n\n articles: list[Article] = []\n next_id = 1\n\n\n @app.get(\"/\")\n def read_root():\n return {\"message\": \"API läuft. Noch. Bitte nichts anfassen.\"}\n\n\n @app.get(\"/articles\", response_model=list[Article])\n def list_articles():\n return articles\n\n\n @app.post(\"/articles\", response_model=Article, status_code=201)\n def create_article(payload: ArticleCreate):\n global next_id\n\n article = Article(id=next_id, **payload.model_dump())\n articles.append(article)\n next_id += 1\n\n return article\n\n\n @app.get(\"/articles/{article_id}\", response_model=Article)\n def get_article(article_id: int):\n for article in articles:\n if article.id == article_id:\n return article\n\n raise HTTPException(status_code=404, detail=\"Artikel nicht gefunden\")\n\n\n @app.put(\"/articles/{article_id}\", response_model=Article)\n def update_article(article_id: int, payload: ArticleCreate):\n for index, article in enumerate(articles):\n if article.id == article_id:\n updated = Article(id=article_id, **payload.model_dump())\n articles[index] = updated\n return updated\n\n raise HTTPException(status_code=404, detail=\"Artikel nicht gefunden\")\n\n\n @app.delete(\"/articles/{article_id}\", status_code=204)\n def delete_article(article_id: int):\n for index, article in enumerate(articles):\n if article.id == article_id:\n articles.pop(index)\n return None\n\n raise HTTPException(status_code=404, detail=\"Artikel nicht gefunden\")\n\n\nDamit haben wir eine kleine REST-API.\n\nEndpunkte:\n\n\n GET /\n GET /articles\n POST /articles\n GET /articles/{article_id}\n PUT /articles/{article_id}\n DELETE /articles/{article_id}\n\n\nDas ist bereits brauchbar.\n\nNicht produktionsreif.\n\nAber brauchbar.\n\nAlso schon mehr als viele interne Tools, die seit 2014 „nur vorübergehend“ laufen.\n\n* * *\n\n## 12. Fehler sauber zurückgeben\n\nFehler passieren.\n\nClients schicken Unsinn.\n\nServer finden Dinge nicht.\n\nMenschen löschen Artikel, die sie später doch brauchen.\n\nDas Leben ist hart.\n\nEine gute API gibt Fehler sauber zurück.\n\nNicht so:\n\n\n {\n \"error\": \"Oops\"\n }\n\n\nSondern verständlich:\n\n\n {\n \"detail\": \"Artikel nicht gefunden\"\n }\n\n\nMit passendem Statuscode:\n\n\n 404 Not Found\n\n\nFastAPI macht das mit `HTTPException` sehr einfach:\n\n\n raise HTTPException(status_code=404, detail=\"Artikel nicht gefunden\")\n\n\nFür Validierungsfehler erzeugt FastAPI automatisch hilfreiche Antworten.\n\nWenn jemand zum Beispiel einen zu kurzen Titel sendet:\n\n\n {\n \"title\": \"Hi\",\n \"slug\": \"test-artikel\",\n \"content\": \"Text\",\n \"published\": false\n }\n\n\nDann kommt ein Fehler zurück, weil `title` mindestens drei Zeichen haben muss.\n\nDas ist angenehm.\n\nUnd erspart einem viele manuelle Prüfungen.\n\nManuelle Prüfungen sind wie manuelle Buchhaltung: manchmal nötig, aber niemand sollte stolz darauf sein, wenn es anders ginge.\n\n* * *\n\n## 13. Authentifizierung mit API-Key\n\nUnsere API ist bisher offen.\n\nJeder kann Artikel erstellen, ändern und löschen.\n\nDas ist im lokalen Beispiel okay.\n\nIm echten Internet wäre das ungefähr so klug wie ein Haustürschlüssel unter der Fußmatte mit Schild „Schlüssel hier“.\n\nWir bauen eine einfache API-Key-Prüfung ein.\n\nNicht als perfekte Sicherheitslösung. Eher als Prinzip.\n\n\n from fastapi import Depends, FastAPI, Header, HTTPException\n from pydantic import BaseModel, Field\n\n API_KEY = \"super-geheim-bitte-nicht-in-echt-so-machen\"\n\n app = FastAPI(title=\"CODEundKRAM API\")\n\n\n def require_api_key(x_api_key: str | None = Header(default=None)):\n if x_api_key != API_KEY:\n raise HTTPException(status_code=401, detail=\"Ungültiger API-Key\")\n\n\nJetzt können wir schreibende Endpunkte schützen:\n\n\n @app.post(\"/articles\", response_model=Article, status_code=201, dependencies=[Depends(require_api_key)])\n def create_article(payload: ArticleCreate):\n ...\n\n\nOder bei `PUT` und `DELETE`:\n\n\n @app.put(\"/articles/{article_id}\", response_model=Article, dependencies=[Depends(require_api_key)])\n def update_article(article_id: int, payload: ArticleCreate):\n ...\n\n\n @app.delete(\"/articles/{article_id}\", status_code=204, dependencies=[Depends(require_api_key)])\n def delete_article(article_id: int):\n ...\n\n\nDer Client muss dann einen Header mitsenden:\n\n\n X-API-Key: super-geheim-bitte-nicht-in-echt-so-machen\n\n\nWichtig: API-Keys gehören nicht hart in den Code.\n\nIm echten Leben nutzt man Umgebungsvariablen, Secret-Manager oder andere Mechanismen.\n\nHardcoded Secrets sind wie Passwörter auf Post-its.\n\nNur mit Git-Historie.\n\nAlso schlimmer.\n\n* * *\n\n## 14. curl: Testen wie ein Erwachsener\n\nMan kann APIs im Browser testen.\n\nMan kann Tools wie Postman, Insomnia oder Bruno nutzen.\n\nOder man nimmt `curl`.\n\n`curl` ist dieses kleine Kommandozeilenwerkzeug, das aussieht wie 1998, aber immer noch zuverlässig Dinge tut, während moderne Apps erst einmal ein Workspace-Update brauchen.\n\nArtikel erstellen:\n\n\n curl -X POST \"http://127.0.0.1:8000/articles\" \\\n -H \"Content-Type: application/json\" \\\n -H \"X-API-Key: super-geheim-bitte-nicht-in-echt-so-machen\" \\\n -d '{\n \"title\": \"DNS: Das Telefonbuch des Internets\",\n \"slug\": \"dns-telefonbuch-des-internets\",\n \"content\": \"DNS ist wichtig. Leider merkt man das meistens erst, wenn es kaputt ist.\",\n \"published\": false\n }'\n\n\nAlle Artikel abrufen:\n\n\n curl \"http://127.0.0.1:8000/articles\"\n\n\nEinzelnen Artikel abrufen:\n\n\n curl \"http://127.0.0.1:8000/articles/1\"\n\n\nArtikel ändern:\n\n\n curl -X PUT \"http://127.0.0.1:8000/articles/1\" \\\n -H \"Content-Type: application/json\" \\\n -H \"X-API-Key: super-geheim-bitte-nicht-in-echt-so-machen\" \\\n -d '{\n \"title\": \"DNS: Immer noch meistens schuld\",\n \"slug\": \"dns-immer-noch-meistens-schuld\",\n \"content\": \"Der Artikel wurde aktualisiert. DNS hat zugesehen.\",\n \"published\": true\n }'\n\n\nArtikel löschen:\n\n\n curl -X DELETE \"http://127.0.0.1:8000/articles/1\" \\\n -H \"X-API-Key: super-geheim-bitte-nicht-in-echt-so-machen\"\n\n\nWenn das funktioniert, hast du eine API, die man programmatisch nutzen kann.\n\nUnd falls nicht, hast du immerhin Logs.\n\nLogs sind wie Tagebücher.\n\nNur ehrlicher.\n\n* * *\n\n## 15. Was diese API noch nicht kann\n\nUnsere kleine API ist ein Lernbeispiel.\n\nSie kann einiges:\n\n * Artikel auflisten\n * Artikel erstellen\n * Artikel abrufen\n * Artikel ändern\n * Artikel löschen\n * Daten validieren\n * einfache API-Key-Prüfung\n * automatische Dokumentation\n\n\n\nAber sie kann auch sehr vieles nicht.\n\nSie hat keine Datenbank.\n\nNach Neustart ist alles weg.\n\nDas ist bei Demo-Code okay. In Produktion wäre das mutig. Also dieses spezielle Mutig, das später in Postmortems landet.\n\nSie hat keine Benutzerverwaltung.\n\nEin API-Key ist nicht dasselbe wie ein echtes Auth-System mit Rollen, Berechtigungen und sauberem Token-Handling.\n\nSie hat kein Rate Limiting.\n\nNiemand hindert einen Client daran, die API mit Anfragen zu bewerfen wie ein Kleinkind mit Bauklötzen.\n\nSie hat kein Logging-Konzept.\n\nKeine Metriken.\n\nKeine Tests.\n\nKeine Datenbankmigrationen.\n\nKeine saubere Projektstruktur.\n\nKeine Versionierung.\n\nKeine CORS-Konfiguration.\n\nKeine Deployment-Strategie.\n\nKurz: Sie ist ein Anfang.\n\nUnd Anfänge sind gut.\n\nSolange man sie nicht heimlich als fertige Plattform verkauft.\n\nFür echte Projekte würde man ergänzen:\n\n * PostgreSQL oder SQLite für Persistenz\n * SQLAlchemy oder SQLModel für Datenbankzugriff\n * Alembic für Migrationen\n * pytest für Tests\n * OAuth2 oder JWT für Authentifizierung\n * CORS-Konfiguration für Browser-Clients\n * Docker für reproduzierbares Deployment\n * strukturierte Logs\n * Monitoring\n * Rate Limits\n * API-Versionierung, zum Beispiel `/v1/articles`\n\n\n\nUnd dann kommt irgendwann jemand und sagt:\n\n> Können wir noch schnell Mandantenfähigkeit einbauen?\n\nDann weiß man:\n\nDas Projekt ist erwachsen geworden.\n\nOder verloren.\n\nManchmal beides.\n\n* * *\n\n## 16. Fazit\n\nAPIs sind keine Magie.\n\nSie sind Verträge zwischen Programmen.\n\nEin Client fragt.\n\nEin Server antwortet.\n\nDazwischen liegen HTTP, JSON, Statuscodes, Authentifizierung, Dokumentation, Validierung und gelegentlich menschliches Leid.\n\nGute APIs sind langweilig im besten Sinne.\n\nSie sind verständlich.\n\nVorhersehbar.\n\nSauber benannt.\n\nGut dokumentiert.\n\nSie liefern passende Statuscodes.\n\nSie validieren Daten.\n\nSie verstecken interne Details.\n\nSie tun nicht so, als sei `200 OK` eine angemessene Antwort auf alles, inklusive Vollkatastrophe.\n\nSchlechte APIs dagegen sind wie schlechte Self-Service-Portale: Man spürt, dass irgendwo jemand Arbeit sparen wollte, und leider war es nicht der Nutzer.\n\nMit Python und FastAPI kann man sehr schnell APIs bauen, die ordentlich wirken, gut dokumentiert sind und Spaß machen.\n\nDas ist selten genug.\n\nNatürlich ist der Weg von Demo-Code zu Produktion länger.\n\nAber wer verstanden hat, wie Endpunkte, Methoden, Modelle, Statuscodes und Authentifizierung zusammenspielen, hat den wichtigsten Schritt getan.\n\nDer Rest ist dann nur noch Datenbank, Sicherheit, Tests, Deployment, Monitoring, Logging, Skalierung, Fehlerbehandlung, Rate Limiting, Dokumentation, Versionierung, CORS, Secrets, Backups und die Frage, warum es auf dem Server nicht läuft, obwohl es lokal ging.\n\nAlso fast nichts.\n\nWillkommen bei APIs.\n\nDie Kellner des Internets.\n\nMeist unsichtbar.\n\nOft unterschätzt.\n\nUnd sobald sie Mist bauen, steht der ganze Laden ohne Bestellung da.",
"title": "APIs: Die Kellner des Internets, die ständig missverstanden werden",
"updatedAt": "2026-05-26T12:16:46.462Z"
}