{"id":25893,"date":"2023-09-15T20:22:16","date_gmt":"2023-09-15T18:22:16","guid":{"rendered":"https:\/\/blog.mi.hdm-stuttgart.de\/?p=25893"},"modified":"2023-09-15T20:22:18","modified_gmt":"2023-09-15T18:22:18","slug":"entwickeln-einer-edge-anwendung-mit-cloudflare","status":"publish","type":"post","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2023\/09\/15\/entwickeln-einer-edge-anwendung-mit-cloudflare\/","title":{"rendered":"Entwickeln einer Edge-Anwendung mit Cloudflare"},"content":{"rendered":"\n<h1 class=\"wp-block-heading\">Einleitung<\/h1>\n\n\n\n<p>Englisch spielt eine gro\u00dfe Rolle in meinem Beruf und Alltag, doch immer noch passieren mir Grammatikfehler. Um meine Englischkenntnisse zu verbessern, habe ich eine kleine Webseite entwickelt, auf der das Schreiben von englischen S\u00e4tzen ge\u00fcbt werden kann. Dem Nutzer wird ein Satz pr\u00e4sentiert, der dann in die festgelegte Sprache \u00fcbersetzt werden muss. Satzteile, die fehlerhaft \u00fcbersetzt wurden, werden rot makiert. Alle absolvierten Aufgaben werden auf einer \u00dcbersichtsseite gespeichert. Aufgaben k\u00f6nnen zudem favorisiert werden, was einen besseren \u00dcberblick \u00fcber eigene Fehler erm\u00f6glicht.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Ziel des Projektes<\/h1>\n\n\n\n<p>Ein zentrales Ziel dieses Projekts bestand darin, den Nutzern eine hohe Performance zu bieten. Zudem war es mir pers\u00f6nlich wichtig, mein Wissen \u00fcber neue Technologien zu erweitern. Deshalb lag das Hauptziel des Projekts auf das Verwenden einer Edge-Umgebung. Ich wollte lernen, was es bedeutet eine App auf einer Edge Umgebung zu deployen und welche Vor- und Nachteile dies mit sich bringt.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Die Anwendung<\/h1>\n\n\n\n<p>Die Webseite kann unter dem folgendem Link gefunden werden: <a href=\"https:\/\/language-trainer.pages.dev\" title=\"https:\/\/language-trainer.pages.dev\">https:\/\/language-trainer.pages.dev<\/a><\/p>\n\n\n\n<p>Als erstes muss ein Account erstellt werden, um die Anwendung benutzen zu k\u00f6nnen.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/CleanShot-2023-09-14-at-01.28.09@2x.png\"><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"25943\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2023\/09\/15\/entwickeln-einer-edge-anwendung-mit-cloudflare\/cleanshot-2023-09-14-at-01-28-092x\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/CleanShot-2023-09-14-at-01.28.09@2x.png\" data-orig-size=\"1028,970\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"CleanShot-2023-09-14-at-01.28.09@2x\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/CleanShot-2023-09-14-at-01.28.09@2x-1024x966.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/CleanShot-2023-09-14-at-01.28.09@2x-1024x966.png\" alt=\"\" class=\"wp-image-25943\" width=\"742\" height=\"700\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/CleanShot-2023-09-14-at-01.28.09@2x-1024x966.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/CleanShot-2023-09-14-at-01.28.09@2x-300x283.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/CleanShot-2023-09-14-at-01.28.09@2x-768x725.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/CleanShot-2023-09-14-at-01.28.09@2x.png 1028w\" sizes=\"auto, (max-width: 742px) 100vw, 742px\" \/><\/a><\/figure>\n\n\n\n<p>Im n\u00e4chsten Schritt muss dann die E-Mail-Adresse best\u00e4tigt werden.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/CleanShot-2023-09-14-at-10.22.37@2x.png\"><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"25945\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2023\/09\/15\/entwickeln-einer-edge-anwendung-mit-cloudflare\/cleanshot-2023-09-14-at-10-22-372x\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/CleanShot-2023-09-14-at-10.22.37@2x.png\" data-orig-size=\"1056,618\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"CleanShot-2023-09-14-at-10.22.37@2x\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/CleanShot-2023-09-14-at-10.22.37@2x-1024x599.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/CleanShot-2023-09-14-at-10.22.37@2x-1024x599.png\" alt=\"\" class=\"wp-image-25945\" width=\"743\" height=\"434\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/CleanShot-2023-09-14-at-10.22.37@2x-1024x599.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/CleanShot-2023-09-14-at-10.22.37@2x-300x176.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/CleanShot-2023-09-14-at-10.22.37@2x-768x449.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/CleanShot-2023-09-14-at-10.22.37@2x.png 1056w\" sizes=\"auto, (max-width: 743px) 100vw, 743px\" \/><\/a><\/figure>\n\n\n\n<p>Dann ist man angemeldet und kann beginnen, mein Englisch zu \u00fcben. In der Mitte der Webseite wird der zu \u00fcbersetzende Satz angezeigt. Direkt unter diesem Satz befindet sich das Antwortfeld, in das die \u00dcbersetzung eingegeben wird. Anschlie\u00dfend muss nur noch die Eingabetaste gedr\u00fcckt oder auf den &#8220;Submit&#8221;-Knopf geklickt werden, um die Aufgabe einzureichen.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/image_1694680487119_0.png\"><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"25950\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2023\/09\/15\/entwickeln-einer-edge-anwendung-mit-cloudflare\/image_1694680487119_0\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/image_1694680487119_0.png\" data-orig-size=\"2536,1726\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"image_1694680487119_0\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/image_1694680487119_0-1024x697.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/image_1694680487119_0-1024x697.png\" alt=\"\" class=\"wp-image-25950\" width=\"752\" height=\"511\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/image_1694680487119_0-1024x697.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/image_1694680487119_0-300x204.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/image_1694680487119_0-768x523.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/image_1694680487119_0-1536x1045.png 1536w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/image_1694680487119_0-2048x1394.png 2048w\" sizes=\"auto, (max-width: 752px) 100vw, 752px\" \/><\/a><\/figure>\n\n\n\n<p>Wenn die Aufgabe eingereicht wurde, erscheint nach kurzer Zeit die L\u00f6sung oberhalb der Aufgabenbox. So kann sofort gesehen werden, ob die \u00dcbersetzung korrekt war. Dazu k\u00f6nnen L\u00f6sungen durch das Klicken auf den Stern favorisiert werden. <\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/image_1694680925193_0.png\"><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"25949\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2023\/09\/15\/entwickeln-einer-edge-anwendung-mit-cloudflare\/image_1694680925193_0\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/image_1694680925193_0.png\" data-orig-size=\"2536,1726\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"image_1694680925193_0\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/image_1694680925193_0-1024x697.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/image_1694680925193_0-1024x697.png\" alt=\"\" class=\"wp-image-25949\" width=\"753\" height=\"512\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/image_1694680925193_0-1024x697.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/image_1694680925193_0-300x204.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/image_1694680925193_0-768x523.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/image_1694680925193_0-1536x1045.png 1536w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/image_1694680925193_0-2048x1394.png 2048w\" sizes=\"auto, (max-width: 753px) 100vw, 753px\" \/><\/a><\/figure>\n\n\n\n<p>Auf der rechten Seite kann die Sprache eingestellt werden. Dabei kann durch einen Klick auf den mittleren Knopf, die Frage mit der Eingabe Sprache gewechselt werden.<\/p>\n\n\n\n<p>Auf der linken Seite befindet sich das Navigationsmen\u00fc. Von dort aus kann zu den Seiten &#8220;History&#8221; und &#8220;Favorites&#8221; navigiert werden.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/image_1694683433264_0.png\"><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"25954\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2023\/09\/15\/entwickeln-einer-edge-anwendung-mit-cloudflare\/image_1694683433264_0\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/image_1694683433264_0.png\" data-orig-size=\"2536,1726\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"image_1694683433264_0\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/image_1694683433264_0-1024x697.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/image_1694683433264_0-1024x697.png\" alt=\"\" class=\"wp-image-25954\" width=\"764\" height=\"519\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/image_1694683433264_0-1024x697.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/image_1694683433264_0-300x204.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/image_1694683433264_0-768x523.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/image_1694683433264_0-1536x1045.png 1536w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/image_1694683433264_0-2048x1394.png 2048w\" sizes=\"auto, (max-width: 764px) 100vw, 764px\" \/><\/a><\/figure>\n\n\n\n<p>Ganz unten auf der linken Seite wird die E-Mail-Adresse des angemeldeten Accounts angezeigt. Durch einen Klick \u00f6ffnet sich ein Dropdown-Men\u00fc, \u00fcber das sich der Benutzer ausloggen kann.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full is-resized\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/CleanShot-2023-09-14-at-11.28.16@2x.png\"><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"25955\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2023\/09\/15\/entwickeln-einer-edge-anwendung-mit-cloudflare\/cleanshot-2023-09-14-at-11-28-162x\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/CleanShot-2023-09-14-at-11.28.16@2x.png\" data-orig-size=\"524,262\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"CleanShot-2023-09-14-at-11.28.16@2x\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/CleanShot-2023-09-14-at-11.28.16@2x.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/CleanShot-2023-09-14-at-11.28.16@2x.png\" alt=\"\" class=\"wp-image-25955\" width=\"367\" height=\"184\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/CleanShot-2023-09-14-at-11.28.16@2x.png 524w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/CleanShot-2023-09-14-at-11.28.16@2x-300x150.png 300w\" sizes=\"auto, (max-width: 367px) 100vw, 367px\" \/><\/a><\/figure>\n\n\n\n<h1 class=\"wp-block-heading\">Verwendete Technologien<\/h1>\n\n\n\n<p>Die Anwendung setzt sich aus einem Next.js-Frontend, einem Hono.js-Backend und einer SQLite-Datenbank zusammen.<\/p>\n\n\n\n<p>Als Basis f\u00fcr das Frontend dient Next.js, das aufgrund seiner F\u00e4higkeit, die Seiten im Voraus zu rendern, hervorragend geeignet ist. Dies erm\u00f6glicht die statische Generierung aller Seiten w\u00e4hrend des Build-Schritts, die bei jedem Seitenaufruf wiederverwendet werden. Dadurch eliminiert es die Generierung auf der Client-Seite, was zu wesentlich k\u00fcrzeren Ladezeiten f\u00fchrt.<\/p>\n\n\n\n<p>Das Backend nutzt Hono.js, da es sich um ein schnelles, kleines Backend-Framework handelt, welches f\u00fcr die Nutzung in Edge-Umgebungen entwickelt wurde. Ein weiterer Vorteil von Hono.js ist die Verf\u00fcgbarkeit zahlreicher zus\u00e4tzlicher Middlewares f\u00fcr eine Vielzahl von Funktionen. So habe ich die CORS-Middleware implementiert, um sicherzustellen, dass ausschlie\u00dflich mein Frontend Zugriff auf das Backend hat.<\/p>\n\n\n\n<p>Um Fehler zu vermeiden, wurde auf eine vollst\u00e4ndige typsichere Entwicklung geachtet. Hierzu wurde TypeScript im Frontend sowie im Backend eingesetzt. F\u00fcr die Datenbank wurde das Drizzle ORM verwendet.<br>Es ist type safe und generiert Types f\u00fcr die Datenbanktabellen. Sollten die Daten nicht dem erwarteten Schema entsprechen, wird die Abfrage rot unterstrichen und es wird angezeigt, was korrigiert werden muss. DrizzleORM ist zudem ein leichtes und schnelles ORM, das problemlos in der EDGE-Umgebung ausgef\u00fchrt werden kann.<\/p>\n\n\n\n<p>Die Kommunikation zwischen Frontend und Backend wird \u00fcber tRPC realisiert, um eine vollst\u00e4ndig typsichere API-Kommunikation zu erm\u00f6glichen. Das bedeutet, dass im Frontend ersichtlich ist, welche Daten das Backend ben\u00f6tigt und welche es bereitstellt. Um die Eingabedaten zu validieren, nutze ich die Bibliothek Zod.<br>Dazu habe ich die TanStack Query Bibliothek auf dem Client verwendet, um Daten abzurufen und zu senden. Sie bietet zahlreiche hilfreiche Hooks, die den Abrufprozess erleichtert. Beispielsweise werden Daten automatisch erneut abgerufen, wenn sie veraltet sind. Des Weiteren sind Caching und State Management integriert. Ein zus\u00e4tzlicher Vorteil ist der \u201cuseInfiniteQuery\u201d Hook, der die Implementierung von unendlichem Scrollen in der Historie und bei den Favoriten erheblich erleichterte.<\/p>\n\n\n\n<p>Da Frontend und Backend als separate Projekte sind, habe ich ein Monorepo mit Turborepo erstellt. Turborepo ist ein Build-System, das f\u00fcr die Verwaltung von Monorepos konzipiert wurde. Es bestimmt, welche Elemente bereits gebaut wurden und welche \u00c4nderungen erfordern, um nur die ben\u00f6tigten Aspekte zu erstellen. Ich nutze Turborepo haupts\u00e4chlich zur Einrichtung eines Monorepos, sodass ich sowohl Backend als auch Frontend gleichzeitig starten kann. Hierf\u00fcr richtete ich die Packages &#8220;Apps&#8221; und &#8220;Packages&#8221; ein. In &#8220;Apps&#8221; befindet sich die Nextjs-Anwendung und unter &#8220;Packages&#8221; das API-Backend mit dem Hono.js-Backend. Turborepo erkennt diese Projekte anhand der in den Verzeichnissen enthaltenen package.json-Dateien. Wird das folgende Package.json-Script im Hauptprojekt ausgef\u00fchrt, starten Frontend und Backend gleichzeitig.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"\" data-line=\"\">&quot;dev&quot;: &quot;turbo dev --parallel --filter={next-app,api}&quot;<\/code><\/pre>\n\n\n\n<p>Hierbei stehen &#8220;next-app&#8221; und &#8220;api&#8221; f\u00fcr die Namen in der package.json-Datei. Turborepo f\u00fchrt dann jeweils das Dev-Skript f\u00fcr &#8220;next-app&#8221; und &#8220;api&#8221; aus. Somit laufen dann Backend und Frontend in einem Terminal, was einen \u00fcbersichtlichen Einblick \u00fcber die Logs bietet.<\/p>\n\n\n\n<p>Ein weiterer Vorteil von Turborepo ist seine Effizienz bei der Installation: Es erstellt nur ein node_modules-Verzeichnis im Hauptordner, das alle Pakete aller Anwendungen enth\u00e4lt. Dies ist besonders praktisch, wenn Front- und Backend dieselben Pakete verwenden. In diesem Fall wird jedes Paket nur einmal installiert.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Infrastruktur<\/h1>\n\n\n\n<p>Mit den verwendeten Technologien musste ich dann das Hono.js-Backend, Next.js-Frontend und eine SQLite Datenbank deployen.<\/p>\n\n\n\n<p>Zuerst habe ich dar\u00fcber Gadanken gemacht, wie ich das Backend deployen sollte. Die Wahl fiel dabei auf Cloudflare Workers, da diese recht bekannt f\u00fcr ihre Serverless Edge-Umgebung sind.<\/p>\n\n\n\n<p>Serverless bedeutet, dass die Anwendung automatisch skaliert und der Entwickler sich keine Sorgen dar\u00fcber machen muss. Ein typisches Problem von Serverless-Architekturen besteht in sogenannte &#8220;Cold Starts&#8221;, bei denen die erste Anfrage einige Sekunden ben\u00f6tigen kann, bevor der Server antwortet. Das liegt daran, dass die Serverinstanz erst gestartet werden muss, was die Benutzererfahrung beeintr\u00e4chtigen kann. Bei Cloudflare Workers gibt es allerdings keine solchen &#8220;Cold Starts&#8221;. Edge bedeutet, dass die Anwendung so nahe wie m\u00f6glich beim Nutzer ausgef\u00fchrt wird. Je nach Standort des Nutzers erfolgt der Zugriff daher immer auf den n\u00e4chstgelegenen Server, was zu einer niedrigen Latenz und kurze Ladezeiten f\u00fchrt.<\/p>\n\n\n\n<p>Beim Next.js-Frontend ist Vercel, der Ersteller des Frameworks, eine beliebte Wahl f\u00fcr das Deployment der App. Ich hatte damit aber bereits schon Erfahrung und da bereits Cloudflare f\u00fcr das Backend verwendet hatte, beschloss ich, auch f\u00fcr das Frontend auf Cloudflare zu setzen. Hier kam Cloudflare Pages ins Spiel, das viele Frameworks unterst\u00fctzt, darunter auch Next.js und ebenfalls in einer Edge-Umgebung l\u00e4uft.<\/p>\n\n\n\n<p>F\u00fcr die Datenbank erw\u00e4gte ich zun\u00e4chst Neon oder Planetscale als Deployment-Optionen, da beide einen Serverless-Treiber anbieten, der in einer Edge-Umgebung ausgef\u00fchrt werden kann. Bei Neon w\u00e4re es eine PostgreSQL-Datenbank gewesen und bei Planetscale eine MySQL-Datenbank. Obwohl ich normalerweise PostgreSQL bevorzuge, wurde ich auf die Datenbankoption Cloudflare D1 aufmerksam.<\/p>\n\n\n\n<p>Ein Vorteil von Cloudflare D1 besteht darin, dass die Datenbank einfach an den Worker gebunden werden kann. Zudem ist Cloudflare D1 im Vergleich zu anderen Anbietern PostgreSQL relativ schnell.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/CleanShot-2023-09-15-at-16.01.45@2x.png\"><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"25938\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2023\/09\/15\/entwickeln-einer-edge-anwendung-mit-cloudflare\/cleanshot-2023-09-15-at-16-01-452x\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/CleanShot-2023-09-15-at-16.01.45@2x.png\" data-orig-size=\"1254,458\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"CleanShot-2023-09-15-at-16.01.45@2x\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/CleanShot-2023-09-15-at-16.01.45@2x-1024x374.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/CleanShot-2023-09-15-at-16.01.45@2x-1024x374.png\" alt=\"\" class=\"wp-image-25938\" width=\"879\" height=\"321\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/CleanShot-2023-09-15-at-16.01.45@2x-1024x374.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/CleanShot-2023-09-15-at-16.01.45@2x-300x110.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/CleanShot-2023-09-15-at-16.01.45@2x-768x280.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/CleanShot-2023-09-15-at-16.01.45@2x.png 1254w\" sizes=\"auto, (max-width: 879px) 100vw, 879px\" \/><\/a><figcaption class=\"wp-element-caption\">https:\/\/blog.cloudflare.com\/d1-turning-it-up-to-11\/<\/figcaption><\/figure>\n\n\n\n<p>In Zukunft soll es noch m\u00f6glich sein, schreibgesch\u00fctzte Replikate \u00fcber das globale Cloudflare-Netzwerk zu verteilen. Daraus w\u00fcrde resultieren, dass die Daten so nah wie m\u00f6glich beim Nutzer gespeichert werden, was die Datenladegeschwindigkeit noch weiter verbessern k\u00f6nnte. Aktuell befindet sich Cloudflare D1 noch in der Alpha-Version, was bedeutet, dass die Speicherkapazit\u00e4t der Datenbank auf 500 MB begrenzt ist.<\/p>\n\n\n\n<p>Da bei der Durchf\u00fchrung von \u00dcbersetzungsaufgaben die L\u00f6sungen zum Nutzer gespeichert werden, musste zus\u00e4tzlich eine Authentifizierung implementiert werden.<\/p>\n\n\n\n<p>Hierbei entschied ich mich f\u00fcr Supabase Auth, da es durch den Auth-Client einfach einzurichten ist und ich mich nicht um die Speicherung kritischer Authentifizierungsdaten k\u00fcmmern muss.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Deployement<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">Frontend<\/h2>\n\n\n\n<p>Das Next.js-Frontend wurde mittels einer Github Action auf Cloudflare Pages deployt. Damit die Github Action die Next.js Anwendung auf Cloudflare Pages deployen kann, m\u00fcssen die Variablen &#8220;CF_API_TOKEN&#8221;, &#8220;CF_ACCOUNT_ID&#8221; und &#8220;CF_PROJECT_NAME&#8221; als Secrets auf Github hinterlegt sein.<br><br>Die Github Action besitzt die folgenden Stages:<\/p>\n\n\n\n<p><strong>Checkout code:<\/strong> Um das Github-Repository auszuchecken.<br><strong>Setup Node.js environment:<\/strong> Um die Node-Version 18 festzulegen.<br><strong>Monorepo install:<\/strong> Hier habe ich diese <a href=\"https:\/\/gist.github.com\/belgattitude\/838b2eba30c324f1f0033a797bab2e31\">Composite github action<\/a> verwendet. Diese bringt den Vorteil mit, dass der Cache erhalten bleibt. Dadurch kann das Projekt schneller installiert werden.<br><strong>Build:<\/strong> Zun\u00e4chst wird das Arbeitsverzeichnis auf Next.js ge\u00e4ndert. Anschlie\u00dfend werden die Umgebungsvariablen exportiert. Abschlie\u00dfend wird ein Cloudflare-CLI-Tool f\u00fcr Next.js ausgef\u00fchrt, um die Anwendung zu bauen.<br><strong>Publish to Cloudflare Pages:<\/strong> Im letzen Stage wird das zuvor erstellte Build auf Cloudflare deployt mit den Umgebungsvariablen die auf Github abgespeichert wurden.<\/p>\n\n\n\n<p>Somit wird sichergestellt, dass das Next.js-Frontend erfolgreich auf Cloudflare Pages deployt wird.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Backend<\/h2>\n\n\n\n<p>Das Backend habe ich \u00fcber Cloudflare Wrangler CLI deployt. Dies hat sich als leichter herausgestellt als ich es mir vorgestellt habe.<\/p>\n\n\n\n<p>Als erstes musste ich das Deployment vorbereiten. Dazu musste ich als erstes die ben\u00f6tigten Umgebungsvariablen in das Projekt auf Cloudflare abspeichern. Mit dem folgendem CLI-Befehl konnte ich die Umgebungsvariablen zu dem Projekt hochladen. Sofern noch kein Cloudflare-Worker mit dem &#8220;PROJECT_NAME&#8221; existiert, wird ein neues Projekt erstellt.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"\" data-line=\"\">echo &lt;VALUE&gt; | wrangler secret put &lt;NAME&gt; -- name &lt;PROJECT_NAME&gt;<\/code><\/pre>\n\n\n\n<p>Da das Backend auch eine Datenbank ben\u00f6tigt, habe ich als n\u00e4chsten Schritt eine Datenbank erstellt, indem ich den folgenden Befehl verwendet habe.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"\" data-line=\"\">wrangler d1 create &lt;DB_NAME&gt;<\/code><\/pre>\n\n\n\n<p>Das Ergebnis sieht dann wie folgt aus.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"\" data-line=\"\">\u2705 Successfully created DB &#039;example&#039; in region WEUR\nCreated your database using D1&#039;s new storage backend. The new storage backend is\nnot yet recommended for production workloads, but backs up your data via\npoint-in-time restore.\n\n[[d1_databases]]\nbinding = &quot;DB&quot; # i.e. available in your Worker on env.DB\ndatabase_name = &quot;example&quot;\ndatabase_id = &quot;8a6819bc-51a0-4fc2-9ae6-9014ac78f2a7&quot;<\/code><\/pre>\n\n\n\n<p>Das Ende des Terminal-Outputs ist wichtig, da diese Information ben\u00f6tigt wird, um den Worker mit der Datenbank zu verkn\u00fcpfen.<\/p>\n\n\n\n<p>Im n\u00e4chsten Schritt muss dann eine wrangler.toml-Datei erstellt werden. Dies ist die Konfigurationsdatei, die f\u00fcr das Deployment ben\u00f6tigt wird.<\/p>\n\n\n\n<p>In dieser Datei muss unter &#8220;name&#8221; den Projektnamen definiert werden, sowie den &#8220;compatibility_date&#8221;, die &#8220;account_id&#8221; und die Bindings f\u00fcr die Datenbank eingeben werden. Es ist wichtig, dass der gleiche Projektname wie bei der Speicherung Umgebungsvariablen verwendet wird. Das Ganze sollte dann in etwa so aussehen:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"\" data-line=\"\">name = &quot;language-trainer&quot;\ncompatibility_date = &quot;2023-01-01&quot;\nsend_metrics = false\naccount_id = &quot;...&quot;\n\n[[d1_databases]]\nbinding = &quot;DB&quot;\ndatabase_name = &quot;example&quot;\ndatabase_id = &quot;8a6819bc-51a0-4fc2-9ae6-9014ac78f2a7&quot;\nmigrations_dir = &quot;migrations&quot;<\/code><\/pre>\n\n\n\n<p>Jetzt muss das Backend noch deployt werden. Dazu muss der folgende Befehl eingegeben werden.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"\" data-line=\"\">wrangler deploy &lt;Main Entry File&gt;<\/code><\/pre>\n\n\n\n<p>Zum Abschluss muss noch eine Migration auf die Datenbank angewendet werden. Sollte noch keine Migration vorhanden sein, kann mit dem folgenden Befehl eine Migration erstellt werden.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"\" data-line=\"\">pnpm generate<\/code><\/pre>\n\n\n\n<p>Die erstellte Migration muss dann noch mit folgendem Befehl angewendet werden.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"\" data-line=\"\">wrangler d1 migrations apply &lt;DB_NAME&gt;<\/code><\/pre>\n\n\n\n<h1 class=\"wp-block-heading\">Probleme und L\u00f6sungen<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">Das Frontend \u00fcber die Cloudflare-Webseite deployen<\/h2>\n\n\n\n<p>Als ich versuchte, das Next.js-Frontend \u00fcber die Cloudflare-Webseite zu deployen, erhielt ich st\u00e4ndig Build-Fehler. Interessanterweise traten diese Fehler nicht auf, wenn ich das Cloudflare CLI f\u00fcr Next.js lokal ausgef\u00fchrt habe.<\/p>\n\n\n\n<p>Das machte es ziemlich schwierig, den Fehler zu finden. Der erste Fehler weist darauf hin, dass tRPC den Typ &#8220;favorites&#8221; nicht besitzt.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"\" data-line=\"\">| 21:43:48.962 | \u25b2  Type error: Property &#039;favorites&#039; does not exist on type &#039;&quot;The property &#039;useContext&#039; in your router collides with a built-in method, rename this router or procedure on your backend.&quot; | &quot;The property &#039;withTRPC&#039; in your router collides with a built-in method, rename this router or procedure on your backend.&quot; | &quot;The property &#039;useQueries&#039; in your router collides with a built-i...&#039;. |\n| 21:43:48.962 | \u25b2  Property &#039;favorites&#039; does not exist on type &#039;&quot;The property &#039;useContext&#039; in your router collides with a built-in method, rename this router or procedure on your backend.&quot;&#039;. |\n| 21:43:48.962 | \u25b2   |\n| 21:43:48.963 | \u25b2     8 |  |\n| 21:43:48.963 | \u25b2     9 |   const { data, isSuccess, fetchNextPage, hasNextPage, isFetching, isFetchingNextPage, refetch } = |\n| 21:43:48.963 | \u25b2  **&gt; 10 |     trpc.favorites.getFavorites.useInfiniteQuery(** |\n| 21:43:48.963 | \u25b2       |          **^** |\n| 21:43:48.963 | \u25b2    11 |       { |\n| 21:43:48.963 | \u25b2    12 |         limit: LIMIT, |\n| 21:43:48.964 | \u25b2    13 |       }, |\n| 21:43:49.041 | \u25b2  ELIFECYCLE\u2009 Command failed with exit code 1. |<\/code><\/pre>\n\n\n\n<p>Bei der Erstellung des tRPC-Clients gebe ich den importierten &#8220;AppRouter&#8221;-Typ aus dem Backend an.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"\" data-line=\"\">import type { AppRouter } from &#039;api\/src\/router&#039;;\n\nexport const trpc = createTRPCNext&lt;AppRouter&gt;({\n  ...\n})<\/code><\/pre>\n\n\n\n<p>Somit sollte das Frontend eigentlich wissen, dass der App-Router den &#8220;favorites&#8221;-Router enth\u00e4lt.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"\" data-line=\"\">import { favoritesRouter } from &#039;.\/routes\/favorites&#039;;\n\nexport const appRouter = router({\n  favorites: favoritesRouter,\n});\n\nexport type AppRouter = typeof appRouter;<\/code><\/pre>\n\n\n\n<p>Bei der Erstellung des tRPC-Clients importiere ich den &#8220;AppRouter&#8221;-Typ vom Backend. Doch ich hatte den Pfad des Next.js-Projekts als Root-Ordner angegeben, was m\u00f6glicherweise das Problem verursachte, da der Typ vom Backend dann m\u00f6glicherweise nicht importiert werden konnte.<br>Deshalb habe ich nach alternativen Wegen gesucht, das Frontend zu deployen und habe letztendlich Github Actions daf\u00fcr eingesetzt. Damit funktionierte es dann. Im Nachhinein stellte ich fest, dass es \u00fcber die Cloudflare Webseite funktioniert h\u00e4tte, wenn ich in der Next.js-Konfiguration die TypeScript-Build-Fehler ignoriert h\u00e4tte.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"\" data-line=\"\">typescript: {\n      ignoreBuildErrors: true,\n    },<\/code><\/pre>\n\n\n\n<p>Das w\u00e4re sicherlich nicht eine optimale L\u00f6sung gewesen, gegangen w\u00e4re es.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Port-Konflikt<\/h2>\n\n\n\n<p>Bei der Bearbeitung einer Mutation im Backend war ich mir unsicher, ob diese so funktioniert wie gedacht. Ich konnte zwar durch die Browser-Console sehen, dass ein Request und eine Response  stattfanden, doch die Daten in der Response waren &#8220;null&#8221;. Um zu \u00fcberpr\u00fcfen, ob alles richtig funktionierte, f\u00fcgte ich einige console.logs im Backend hinzu. Allerdings erhielt ich keine Ausgaben.<\/p>\n\n\n\n<p>Nach einem Blick in die Dokumentation des Backend-Frameworks stellte ich fest, dass es eine Logging-Middleware gibt. Diese hatte ich dann implementiert, aber wieder wurden keine Ausgaben im Terminal angezeigt. Nach einigen Neustarts bemerkte ich, dass das Backend jedes Mal auf einem anderen Port startete anstatt den vorgegebenen Port zu verwenden.<\/p>\n\n\n\n<p>Nachdem ich die laufenden Ports \u00fcberpr\u00fcft hatte, fiel mir auf, dass es zwei offene Ports mit dem Namen &#8220;Workerd&#8221; gab, was f\u00fcr den Cloudflare-Worker steht. Nachdem ich beide Ports geschlossen und das Projekt neu gestartet hatte, funktionierte der Code wie erwartet und die Logs wurden ausgegeben.<\/p>\n\n\n\n<p>Es stellte sich also heraus, dass mein Frontend das Backend von einem \u00e4lteren Code-Stand anfragte und nicht den aktuellen Code, an dem ich gerade arbeitete.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Gitignore<\/h2>\n\n\n\n<p>W\u00e4hrend der Entwicklung erstellte ich einige Types und speicherte diese im &#8220;types&#8221;-Ordner ab. Als ich jedoch versuchte, das Projekt zu deployen, trat pl\u00f6tzlich ein Fehler beim Import der Types auf: Angeblich konnte der Pfad nicht gefunden werden. Ich \u00fcberpr\u00fcfte als Erstes den Pfad und war \u00fcber den Fehler verwundert, da alles korrekt aussah. Dann bemerkte ich jedoch, dass der &#8220;types&#8221;-Ordner im GitHub-Repository fehlte. Somit war dann klar, dass dieser Ordner aufgrund der Gitignore-Datei nicht committet wurde.<br><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">CORS Error<\/h2>\n\n\n\n<p>Als das Backend und das Frontend deployt waren, konnte das Frontend aufgrund eines CORS-Fehlers nicht auf das Backend zugreifen.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/SCR-20230817-rdek_1694731446844_0.png\"><img loading=\"lazy\" decoding=\"async\" width=\"909\" height=\"237\" data-attachment-id=\"25997\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2023\/09\/15\/entwickeln-einer-edge-anwendung-mit-cloudflare\/scr-20230817-rdek_1694731446844_0\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/SCR-20230817-rdek_1694731446844_0.png\" data-orig-size=\"909,237\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"SCR-20230817-rdek_1694731446844_0\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/SCR-20230817-rdek_1694731446844_0.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/SCR-20230817-rdek_1694731446844_0.png\" alt=\"\" class=\"wp-image-25997\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/SCR-20230817-rdek_1694731446844_0.png 909w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/SCR-20230817-rdek_1694731446844_0-300x78.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/09\/SCR-20230817-rdek_1694731446844_0-768x200.png 768w\" sizes=\"auto, (max-width: 909px) 100vw, 909px\" \/><\/a><\/figure>\n\n\n\n<p>Der Fehler entstand dadurch, dass ich die URL des Backends aus der Browser-Suchleiste kopierte und dabei ein Slash am Ende der URL hinzuf\u00fcgt war.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Learnings<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Workers<\/strong>: Arbeiten mit Workers war eine angenehme Erfahrung. Nicht nur wegen ihrer beeindruckenden Edge Performance und keinen Cold Starts, sondern auch wegen dem einfachen deployen \u00fcber die CLI. Dazu kann mit &#8220;Miniflare&#8221; ein Cloudflare Worker lokal simuliert werden, was den Worker auch einfach zum entwickeln in einer lokalen Umgebung macht. Auch das verkn\u00fcpfen mit der Datenbank war einfach.<\/li>\n\n\n\n<li><strong>Serverless:<\/strong> Dadurch das nur Serverless-Anbieter eingesetzt wurden, musste ich mir keine Gedanken um Skalierung und Wartung von Servern machen, was viel Zeit gespart hat.<\/li>\n\n\n\n<li><strong>Kosten:<\/strong> Mit dem kostenlosen Plan stehen jeden Tag 100.000 kostenlose Anfragen zur Verf\u00fcgung. Der Bezahltarif beginnt bei f\u00fcnf Euro. Damit erh\u00e4lt man jeden Monat 10 Millionen Requests und jede zus\u00e4tzliche Millionen Requests kostet $0,50. Dieser geringe Preis war \u00fcberraschend.<\/li>\n\n\n\n<li><strong>SQLite:<\/strong> Es war das erste mal das ich in einem Projekt SQLite verwendet habe. Hier habe ich gelernt dass es bei SQLite keine Boolean Werte gibt und noch zus\u00e4tzliche Befehle hinter einem query hinzugef\u00fcgt werden m\u00fcssen, um die queries auszuf\u00fchren. So muss um einen Eintrag von der Datenbank zu bekommen, hinter dem query noch ein <code class=\"\" data-line=\"\">get<\/code> und bei einem insert noch ein <code class=\"\" data-line=\"\">run<\/code> hinzugef\u00fcgt werden<\/li>\n\n\n\n<li><strong>Github Actions:<\/strong> Durch die Einrichtung von Github Actions wird bei jedem Commit automatisch die Next.js-App deployt. Das beschleunigt und vereinfacht die Entwicklung und dar\u00fcber hinaus kann die CI\/CD nach Belieben angepasst werden.<\/li>\n\n\n\n<li><strong>Turborepo:<\/strong> Turborepo erweist sich als n\u00fctzlich, wenn die Anwendung aus mehreren Apps besteht. Mit einem Skriptbefehl im Root des Projekts k\u00f6nnen beide Apps gleichzeitig ausgef\u00fchrt werden. Hinzu kommt der Vorteil, dass bei der Installation nur ein einziges node_modules-Verzeichnis f\u00fcr beide Apps erstellt wird.<\/li>\n<\/ul>\n\n\n\n<h1 class=\"wp-block-heading\">Fazit<\/h1>\n\n\n\n<p>Trotz einiger kleiner Herausforderungen hat sich das Deployment der Anwendung als relativ unkompliziert herausgestellt. Cloudflare bietet zudem eine hohe Performance ohne den Aufwand von Skalierung und Wartung und das zu geringen Kosten. Der gr\u00f6\u00dfte Nachteil d\u00fcrfte sicherlich sein, dass nicht alle Anwendungen in einer Edge-Umgebung ausgef\u00fchrt werden k\u00f6nnen. Viele ORMs und Frameworks sind daf\u00fcr zu gro\u00df. Dank neuer, schlanker Frameworks wie Hono.js, die vergleichbare Features wie popul\u00e4re Frameworks wie Express bieten, wird die Entwicklung in einer Edge-Umgebung jedoch zunehmend einfacher und angenehmer.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>Das Repository kann unter dem folgendem Link gefunden werden: <a href=\"https:\/\/github.com\/j3sch\/language-trainer\" title=\"https:\/\/github.com\/j3sch\/language-trainer\">https:\/\/github.com\/j3sch\/language-trainer<\/a><\/p>\n\n\n\n<p><br><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Einleitung Englisch spielt eine gro\u00dfe Rolle in meinem Beruf und Alltag, doch immer noch passieren mir Grammatikfehler. Um meine Englischkenntnisse zu verbessern, habe ich eine kleine Webseite entwickelt, auf der das Schreiben von englischen S\u00e4tzen ge\u00fcbt werden kann. Dem Nutzer wird ein Satz pr\u00e4sentiert, der dann in die festgelegte Sprache \u00fcbersetzt werden muss. Satzteile, die [&hellip;]<\/p>\n","protected":false},"author":1145,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1,120],"tags":[7,247,202],"ppma_author":[673],"class_list":["post-25893","post","type-post","status-publish","format-standard","hentry","category-allgemein","category-cloud-technologies","tag-cloud","tag-edge","tag-serverless"],"aioseo_notices":[],"jetpack_featured_media_url":"","jetpack-related-posts":[{"id":27939,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2025\/09\/11\/entwickeln-eines-ki-tools-zum-generieren-von-strukturierten-lerninhalten\/","url_meta":{"origin":25893,"position":0},"title":"Entwickeln eines KI-Tools zum Generieren von strukturierten Lerninhalten","author":"Jonathan Aupperle","date":"11. September 2025","format":false,"excerpt":"F\u00fcr den Kurs \"Software Development for Cloud Computing\" wollte ich eine Anwendung entwickeln, mit der konkrete Aufgaben f\u00fcr ein gegebenes Lernziel generiert werden k\u00f6nnen. Der Nutzer stellt dabei Informationen zum Lernziel, sowie seines aktuellen Niveaus und der geplanten Lerndauer zur Verf\u00fcgung. Auf dieser Basis k\u00f6nnen dann die konkreten Aufgaben, die\u2026","rel":"","context":"In &quot;Allgemein&quot;","block_context":{"text":"Allgemein","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2025\/08\/grafik.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2025\/08\/grafik.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2025\/08\/grafik.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2025\/08\/grafik.png?resize=700%2C400&ssl=1 2x"},"classes":[]},{"id":28461,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2026\/02\/20\/das-alltagliche-automatisieren-das-menschliche-perfektionieren-die-transformation-des-scrum-masters-und-der-team-kommunikation-im-ki-gestutzten-agilen-umfeld\/","url_meta":{"origin":25893,"position":1},"title":"Das Allt\u00e4gliche automatisieren, das Menschliche perfektionieren: Die Transformation des Scrum-Masters und der Team-Kommunikation im KI-gest\u00fctzten agilen Umfeld","author":"Alice Ginnen","date":"20. February 2026","format":false,"excerpt":"Abstract Die zunehmende Nutzung von KI in der Softwareentwicklung ver\u00e4ndert grundlegend die bew\u00e4hrten agilen Methoden. In der vorliegenden Arbeit wird untersucht, inwiefern sich die Rolle des Scrum Masters und die Teamkommunikation durch den Einsatz von Large Language Models (LLMs) und autonomen Agenten ver\u00e4ndern. Die Analyse ergibt, dass ein zunehmender Anteil\u2026","rel":"","context":"In &quot;Allgemein&quot;","block_context":{"text":"Allgemein","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":22460,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/28\/himbeer-tarte-und-harte-fakten-im-interview-mit-ansible-k3s-infrastructure-as-code-und-raspberry-pi\/","url_meta":{"origin":25893,"position":2},"title":"\u201cHimbeer Tarte und harte Fakten\u201d: Im Interview mit Ansible, k3s, Infrastructure as Code und Raspberry Pi","author":"Aliena Leonhard","date":"28. February 2022","format":false,"excerpt":"Why so serious? - Ein Artikel von Sarah Schwab und Aliena Leonhard im Rahmen der Vorlesung Systems Engineering and Management. Die Idee, ein fiktives Interview zu erstellen, entstammt daraus komplexe Sachverhalte unterhaltsam und verst\u00e4ndlich zu machen. Wir sind heute zu Gast in der Tech-Sendung \u201cHimbeer Tarte und harte Fakten\u201d.\u00a0 Heute\u2026","rel":"","context":"In &quot;Allgemein&quot;","block_context":{"text":"Allgemein","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/stan-slade-BM27BzBrhVM-unsplash-2-scaled.jpg?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/stan-slade-BM27BzBrhVM-unsplash-2-scaled.jpg?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/stan-slade-BM27BzBrhVM-unsplash-2-scaled.jpg?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/stan-slade-BM27BzBrhVM-unsplash-2-scaled.jpg?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/stan-slade-BM27BzBrhVM-unsplash-2-scaled.jpg?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/stan-slade-BM27BzBrhVM-unsplash-2-scaled.jpg?resize=1400%2C800&ssl=1 4x"},"classes":[]},{"id":26129,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2024\/02\/27\/ctf-infrastruktur-als-proof-of-concept-in-der-microsoft-azure-cloud\/","url_meta":{"origin":25893,"position":3},"title":"CTF-Infrastruktur als Proof-of-Concept in der Microsoft Azure Cloud","author":"jk233","date":"27. February 2024","format":false,"excerpt":"Einf\u00fchrung Eine eigene Capture-The-Flag (CTF) Plattform zu betreiben bringt besondere Herausforderungen mit sich. Neben umfangreichem Benutzermanagement, dem Bereitstellen und sicherem Hosten von absichtlich verwundbaren Systemen, sowie einer m\u00f6glichst einfachen Methode, spielbare Systeme von externen Quellen einzubinden. So m\u00f6chte man vielleicht der eigenen Community die M\u00f6glichkeit bieten, eigene Szenarien zu entwickeln,\u2026","rel":"","context":"In &quot;Allgemein&quot;","block_context":{"text":"Allgemein","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/02\/HkdUqtOvFEbZdRFQaVd9LpUF6TaU8VoYhJn2kK0A-rzTGuPjvqiNLsNYpHnMuPZpLr5RFCfTW30d46KxetHLb8mYfYqN_70fqpOvYevgh1bfHZFI_UUKHbqRjDrHQ-b8QG2jXXV6E2zh2W35Rr6gpnk.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/02\/HkdUqtOvFEbZdRFQaVd9LpUF6TaU8VoYhJn2kK0A-rzTGuPjvqiNLsNYpHnMuPZpLr5RFCfTW30d46KxetHLb8mYfYqN_70fqpOvYevgh1bfHZFI_UUKHbqRjDrHQ-b8QG2jXXV6E2zh2W35Rr6gpnk.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/02\/HkdUqtOvFEbZdRFQaVd9LpUF6TaU8VoYhJn2kK0A-rzTGuPjvqiNLsNYpHnMuPZpLr5RFCfTW30d46KxetHLb8mYfYqN_70fqpOvYevgh1bfHZFI_UUKHbqRjDrHQ-b8QG2jXXV6E2zh2W35Rr6gpnk.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/02\/HkdUqtOvFEbZdRFQaVd9LpUF6TaU8VoYhJn2kK0A-rzTGuPjvqiNLsNYpHnMuPZpLr5RFCfTW30d46KxetHLb8mYfYqN_70fqpOvYevgh1bfHZFI_UUKHbqRjDrHQ-b8QG2jXXV6E2zh2W35Rr6gpnk.png?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/02\/HkdUqtOvFEbZdRFQaVd9LpUF6TaU8VoYhJn2kK0A-rzTGuPjvqiNLsNYpHnMuPZpLr5RFCfTW30d46KxetHLb8mYfYqN_70fqpOvYevgh1bfHZFI_UUKHbqRjDrHQ-b8QG2jXXV6E2zh2W35Rr6gpnk.png?resize=1050%2C600&ssl=1 3x"},"classes":[]},{"id":28084,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2025\/09\/14\/springboot-zu-serverless-probleme-und-paradigmen\/","url_meta":{"origin":25893,"position":4},"title":"Springboot zu Serverless: Probleme und Paradigmen","author":"Julian Schniepp","date":"14. September 2025","format":false,"excerpt":"Im Rahmen der Vorlesung \u201eSoftware Development for Cloud\u00a0Computing\u201c sollte jedes Team ein eigenes Cloud\u2011Projekt umsetzen. Unser Projekt Taskflow sollte dabei ein serverloses Todo\u2011Management\u2011System werden. Ziel war es dabei, neue und vor allem industrierelevante Cloud\u2011Technologien praktisch zu erlernen. Der Backend\u2011Teil ist mit Spring\u2011Boot realisiert, welcher \u00fcber AWS\u00a0Lambda und API\u00a0Gateway bereitgestellt modular\u2026","rel":"","context":"In &quot;Allgemein&quot;","block_context":{"text":"Allgemein","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":28654,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2026\/02\/28\/multiplayer-arena-dynamische-game-sessions-mit-docker-fastapi\/","url_meta":{"origin":25893,"position":5},"title":"Multiplayer-Arena: Dynamische Game-Sessions mit Docker &amp; FastAPI","author":"Emre Kalkan","date":"28. February 2026","format":false,"excerpt":"Game: https:\/\/46.101.127.20.sslip.io\/ (online bis 31. M\u00e4rz 2026) (beachte Readme im Git!) Git Repo: https:\/\/github.com\/ek101-collab\/ArenaGame-with-ContainerSessions Einleitung und Motivation Im Rahmen der Vorlesung \"System Engineering und Management\" bestand die Kernaufgabe darin, ein Projekt zu konzipieren und umzusetzen, das moderne Web- und Cloud-Technologien nutzt. Zu Beginn der Projektphase lag mein Fokus auf einer\u2026","rel":"","context":"In &quot;Allgemein&quot;","block_context":{"text":"Allgemein","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2026\/02\/mainmenu.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2026\/02\/mainmenu.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2026\/02\/mainmenu.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2026\/02\/mainmenu.png?resize=700%2C400&ssl=1 2x"},"classes":[]}],"jetpack_sharing_enabled":true,"authors":[{"term_id":673,"user_id":1145,"is_guest":0,"slug":"jens_schlegel","display_name":"Jens Schlegel","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/2a731096e8de04226de7cd7b15f81673835fd47b82ed5a151f25ee7614d06017?s=96&d=mm&r=g","0":null,"1":"","2":"","3":"","4":"","5":"","6":"","7":"","8":""}],"_links":{"self":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts\/25893","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/users\/1145"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/comments?post=25893"}],"version-history":[{"count":20,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts\/25893\/revisions"}],"predecessor-version":[{"id":26038,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts\/25893\/revisions\/26038"}],"wp:attachment":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/media?parent=25893"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/categories?post=25893"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/tags?post=25893"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/ppma_author?post=25893"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}