{"id":17797219,"url":"https://github.com/timoscheuermann/gofeed-backend","last_synced_at":"2025-04-02T03:19:08.449Z","repository":{"id":99711175,"uuid":"380939645","full_name":"TimoScheuermann/GoFeed-Backend","owner":"TimoScheuermann","description":null,"archived":false,"fork":false,"pushed_at":"2021-07-07T12:41:47.000Z","size":169,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-07T17:47:57.293Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TimoScheuermann.png","metadata":{"files":{"readme":"Readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-06-28T07:12:02.000Z","updated_at":"2021-07-07T12:41:50.000Z","dependencies_parsed_at":"2023-07-31T14:15:12.828Z","dependency_job_id":null,"html_url":"https://github.com/TimoScheuermann/GoFeed-Backend","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimoScheuermann%2FGoFeed-Backend","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimoScheuermann%2FGoFeed-Backend/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimoScheuermann%2FGoFeed-Backend/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimoScheuermann%2FGoFeed-Backend/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TimoScheuermann","download_url":"https://codeload.github.com/TimoScheuermann/GoFeed-Backend/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246747054,"owners_count":20827080,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-27T11:51:42.473Z","updated_at":"2025-04-02T03:19:08.430Z","avatar_url":"https://github.com/TimoScheuermann.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GoFeed\n\n## Topics\n\n* Was macht die Anwendung\n* Hintergrundwissen\n* Ein einfaches Beispiel\n* Wie funktioniert die Anwendung\n* Herausforderungen\n* Weitere persönliche Erfahrungen mit Go\n* Finale Bewertung von Go als Sprache für Webservices\n\n---\n\n## Was macht die Anwendung\n\nDieses Projekt ist eine Art \"Playground\", um Webservices mit Hilfe von Go zu realisieren. Hierfür wurde ein einfacher Feed entwickelt. Der Fokus lag hier weniger auf dem Frontend (Vue) und mehr auf dem Backend (Go).\n\nNutzer können sich mittels oAuth (Google \u0026 GitHub) anmelden und Beiträge verfassen, die von anderen gelesen werden können. Eigene Beiträge können bearbeitet und gelöscht werden. Hierdurch deckt die Anwendung typische Request an eine REST-Schnittstelle ab. GET, POST, DELETE und PATCH. Durch die oAuth-Integration ist auch ein kleiner Teil der Authentisierung, Authentifizierung und Autorisierung abgedeckt.\n\n---\n\n## Hintergrundwissen\n\nGo ist für mich eine komplett neue Sprache gewesen. Ich hatte zuvor keine Erfahrungen mit C oder anderen Go-ähnlichen Sprachen. Mit meinem Hintergrund als Webentwickler war ich mit Sprachen wie PHP, JavaScript und Typescript sehr vertraut. Neben diesen Sprachen beherrschte ich auch Java.\n\nNun ging es also los mit Go. Ich nutzte hierzu \"[A Tour of Go](https://tour.golang.org/welcome/1)\". Dies ist eine interaktive Tour, bei der Go-Code im Browser geschrieben und getestet werden kann. Es ist eine Tutorial-Reihe, welche Schritt für Schritt den Syntax von Go erklärt.\n\nDie Tour ist in folgende [Themenblöcke](https://tour.golang.org/list) aufgeteilt:\n\n* Basics\n  * Packages\n  * Imports\n  * Exported names\n  * Functions\n  * Multiple results\n  * Named return values\n  * Variables\n  * Basic types\n* Flow Control\n  * For\n  * If (else)\n  * Switch\n  * Defer\n* More types\n  * Pointers\n  * Structs\n  * Arrays\n  * Map\n* Methods\n  * Erros\n  * Readers\n  * Images\n* Concurrency\n  * Goroutines\n  * Channels\n  * Range and Close\n  * Select\n\nNachdem ich einschließlich das Kapitel Flow-Control durchgearbeitet hatte, fühlte ich mich bereits sicher genug meine erste Anwendung zu schreiben. Durch meine eigenen Erfahrungen wusste ich auch, dass ich es besser lerne, wenn ich es selbst schreibe und nicht halbe Lösungen vorgelegt bekomme. Gerade die Problemlösung hilft mir enorm eine neue Sprache schneller und besser zu verstehen als mich durch 20 Bücher zu arbeiten.\n\n---\n\n## Ein einfaches Beispiel\n\nDieses Beispiel zeigt, wie einfach es ist, eine Datenbank (MongoDB) mit Go zu verknüpfen und Anfragen über eine REST-Schnittstelle zu verarbeiten.\n\nZunächst muss Go [installiert](https://golang.org/doc/install) werden.\n\nIst dies erledigt kann die Entwicklung auch schon starten.\n\nWir erstellen ein Verzeichnis, in welchem wir unsere Anwendung schreiben möchten.\n\n```bash\ncd %HOMEPATH%\n\nmkdir go-rest\n\ncd go-rest\n```\n\nAls nächstes initialisieren wir unser Projekt und geben ihm einen Namen.\n\n```bash\ngo mod init go-rest\n```\n\nAls nächstes erstellen wir unsere erste Go-Datei, in welcher wir unsreren Go-Code schreiben. Beispielsweise *app.go*.\n\nHier kann nun zunächst folgender Code eingefügt werden:\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n    fmt.Println(\"Hallo zusammen!\")\n}\n```\n\nJede Datei beginnt **immer** mit \"package \\\u003cpackagename\u003e\". Java Entwickler kennen das Konzept von Packages. Hierdurch können wir den Code in Funktionsbereiche aufteilen und gegenseitig importieren. In Code ist dies ebenfalls möglich. In dem oben gezeigten Code importieren wir sogar das fmt-Paket von Go. Jede Anwendung verfügt über ein package main und eine Funktion main. Hier startet Go auch die Anwendung.\n\nUm den Code auszuführen, kann folgender Befehl ausgeführt werden:\n\n```bash\n# Wir befinden uns im gleichen Verzeichnis wie die Dateien go.mod und app.go\n\ngo run .\n\nHallo zusammen!\n```\n\nWebentwickler von node.js Anwendung sind vertraut mit NPM. Go bietet eine ähnliche Möglichkeit externe Pakete zu installieren oder eigene zu veröffentlichen. Mit dem Befehl *go get XY* können Pakete installiert werden. Das Pendant zu NPM für Go ist [pkg.go.dev](pkg.go.dev).\n\nFür unser kleines Beispiel benötigen wir ein zwei zusätzliche Pakete, um uns die Arbeit zu erleichtern.\n\n```bash\ngo get github.com/gorilla/mux\ngo get go.mongodb.org/mongo-driver/mongo\n```\n\nGorilla/Mux ist ein Router, um REST-Anfragen einfach zu verarbeiten, Mongo-Driver/mongo ist unser Driver für die Datenbank Anbindung.\n\nBeginnen wir also zunöchst mit dem Verbindungsaufbau zur Datenbank.\nHier zu erstellen wir eine init Funktion, welche bei Programmstart autom. ausgeführt wird.\n\n```go\nvar database *mongo.Database\n\nfunc init() {\n\tclientOptions := options.Client().ApplyURI(\"MongoDB Verbindungs URL\")\n\tctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n\n\tdefer cancel()\n\n\tc, err := mongo.Connect(ctx, clientOptions)\n\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tfmt.Println(\"Successfully connected to MongoDB\")\n\n\tdatabase = c.Database(\"go-rest\")\n}\n```\n\nWir speichern die Database ab, damit wir sie später bei Anfragen wiederverwenden können.\n\nIm nächsten Schritt kümmern wir uns um das Routing. Hierzu haben wir das Gorilla/Mux Paket installiert. Wir möchten zunächst einfache Anfragen wie POST und GET realisieren. Hierzu erstellen wir zunächst ein struct (Objekt/Klasse), in welchem wir die Datenstruktur vorgeben.\n\n```go\ntype Message struct {\n\tMessageID primitive.ObjectID `json:\"id,omitempty\" bson:\"_id,omitempty\"`\n\tMessage   string             `json:\"message\" bson:\"message\"`\n}\n```\n\nStructs in go sind sehr vielfälltig, einerseits definieren sie Attribute und deren Typen, andererseits können auch Zusätze mitgegeben werden. In unserem Beispiel haben wir jeweils ein json und bson Zusatz hinzugefügt. Da wir eine Webanwendung entwickeln und die Daten als json zurückgeben werden, kann mit dem json-Zusatz definiert werden, wie die Variable umbenannt werden soll, sobald sie nach außen geschickt wird, bzw. wie sie heißen muss, wenn sie von außen kommt.\n*Im Vgl. hierzu ist der bson-Zusatz für MongoDb und das dortige Mapping der Namen*.\n\nDas omitempty gibt lediglich an, dass das Attribut auch weggelassen werden kann, wenn der Wert nil ist. Neben bson und json gibt es auch Zusatzpakete welche von diesem Syntax gebrauch machen. So kann ein validator hinzugefügt werden, welche die einzelnen Attribute validiert. Mehr zum Thema Validierung kann [hier](https://github.com/go-playground/validator) gefunden werden.\n\nNun bauen wir unsere Endpunkte ein\n\n```go\nfunc main() {\n\tfmt.Println(\"Hallo zusammen!\")\n\n\trouter := mux.NewRouter()\n\trouter.HandleFunc(\"/\", postMessage).Methods(\"POST\")\n\trouter.HandleFunc(\"/{id}\", getMessage).Methods(\"GET\")\n\trouter.HandleFunc(\"/\", getMessages).Methods(\"GET\")\n\n\tlog.Fatal(http.ListenAndServe(\":3000\", router))\n}\n```\n\nIm obigen Beispiel erstellen wir zunächst unseren Router. Das defer führt den Code erst am Ende des Code-Blocks aus. Die HandleFunc-Methode nimmt 2 Parameter entgegen, zum einen den Pfad zum anderen eine Funktion, welche bei einem Aufruf ausgeführt wird.\n\nIm nächsten Schritt müssen wir nun unsere Methoden schreiben.\n\n```go\nfunc postMessage(w http.ResponseWriter, req *http.Request) {\n\tvar message Message\n\tjson.NewDecoder(req.Body).Decode(\u0026message)\n\n\tresult, _ := database.Collection(\"messages\").InsertOne(context.Background(), bson.M{\n\t\t\"message\": message.Message,\n\t})\n\n\tw.Header().Set(\"content-type\", \"application/json\")\n\tjson.NewEncoder(w).Encode(result)\n}\n```\n\nWir definieren zunächst eine Message Variable in welches wir den Request Body parsen wollen. Im folgenden decoden wir den Body und parsen ihn in unsere Variable. Als nächstes speichern wie die Nachricht in MongoDB und geben das Ergebnis an den Aufrufer zurück.\n\n---\n\n```go\nfunc getMessage(w http.ResponseWriter, req *http.Request) {\n\tparams := mux.Vars(req)\n\toid, err := primitive.ObjectIDFromHex(params[\"id\"])\n\n\tif err != nil {\n\t\tw.WriteHeader(http.StatusUnprocessableEntity)\n\t\tjson.NewEncoder(w).Encode(`{\"error\": \"Ungültige ObjectID\"}`)\n\t\treturn\n\t}\n\n\tvar message Message\n\terr = database.Collection(\"messages\").FindOne(context.Background(), bson.M{\"_id\": oid}).Decode(\u0026message)\n\n\tif err != nil {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t\tjson.NewEncoder(w).Encode(`{\"error\": \"Message not found\"}`)\n\t\treturn\n\t}\n\n\tw.Header().Set(\"content-type\", \"application/json\")\n\tjson.NewEncoder(w).Encode(message)\n}\n```\n\nWir lesen zunächst die ID aus unseren Paremtern. Im Anschluss konvertieren wir diese zu einer ObjectID, hierbei wird auch gleichzeitig geprüft, ob es sich um eine ObjectID handelt. Falls nicht gibt es einen Error und wir geben dies an den Aufrufer zurück.\n\nAls nächstes erstellen wir wieder unsere Messsage Variable, führen den FindOne Befehl auf der Datenbank aus, filtern nach der id und dekodieren das Ergebnis in unsere Message Variable. Sollte es hierbei zu einem Fehler kommen geben wir diesen zurück, ansonsten die Nachricht.\n\n---\n\n```go\nfunc getMessages(w http.ResponseWriter, req *http.Request) {\n\tcursor, err := database.Collection(\"messages\").Find(context.Background(), bson.M{})\n\n\tif err != nil {\n\t\tw.WriteHeader(http.StatusUnprocessableEntity)\n\t\tjson.NewEncoder(w).Encode(`{\"error\": \"Ein Fehler beim Aufrufen ist aufgetreten\"}`)\n\t\treturn\n\t}\n\n\tdefer cursor.Close(context.Background())\n\n\tmessages := []Message{}\n\tfor cursor.Next(context.Background()) {\n\t\tvar message Message\n\t\tcursor.Decode(\u0026message)\n\t\tmessages = append(messages, message)\n\t}\n\n\tif err := cursor.Err(); err != nil {\n\t\tw.WriteHeader(http.StatusUnprocessableEntity)\n\t\tjson.NewEncoder(w).Encode(`{\"error\": \"Ein Fehler beim Iterieren ist aufgetreten\"}`)\n\t\treturn\n\t}\n\n\tw.Header().Set(\"content-type\", \"application/json\")\n\tjson.NewEncoder(w).Encode(messages)\n}\n```\n\nFür das Auslesen mehrerer Datensätze in MongoDB wird ein cursor benötigt. Wir lesen alle Datensätze aus der Datenbank ohne zu Filtern (einfaches *bson.M{}*). Sollte bereits hier ein Fehler auftreten, informieren wir den Aufrufer. Bevor wir vergessen den cursor am Ende zu schließen, schließen wir ihn direkt nach Erstellung mittels defer.\n\nAls nächstes definieren wir wieder unsere Messages variable, diesmal ist sie allerdings ein leeres Array. Folglich iterieren wir durch die Ergebnisse des Cursors, dekodieren die einzelne Nachricht und fügen diese dem Array hinzu. Sollte es hierbei zu einem Fehler kommen informieren wir den Client.\n\n---\n\nUnser kompletter Code sollte nun so aussehen:\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/gorilla/mux\"\n\t\"go.mongodb.org/mongo-driver/bson\"\n\t\"go.mongodb.org/mongo-driver/bson/primitive\"\n\t\"go.mongodb.org/mongo-driver/mongo\"\n\t\"go.mongodb.org/mongo-driver/mongo/options\"\n)\n\nvar database *mongo.Database\n\ntype Message struct {\n\tMessageID primitive.ObjectID `json:\"id,omitempty\" bson:\"_id,omitempty\"`\n\tMessage   string             `json:\"message\" bson:\"message\"`\n}\n\nfunc init() {\n\tclientOptions := options.Client().ApplyURI(\"MongoDB Verbindungs URL\")\n\tctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n\n\tdefer cancel()\n\n\tc, err := mongo.Connect(ctx, clientOptions)\n\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tfmt.Println(\"Successfully connected to MongoDB\")\n\n\tdatabase = c.Database(\"go-rest\")\n}\n\nfunc main() {\n\tfmt.Println(\"Hallo zusammen!\")\n\n\trouter := mux.NewRouter()\n\trouter.HandleFunc(\"/\", postMessage).Methods(\"POST\")\n\trouter.HandleFunc(\"/{id}\", getMessage).Methods(\"GET\")\n\trouter.HandleFunc(\"/\", getMessages).Methods(\"GET\")\n\n\tlog.Fatal(http.ListenAndServe(\":3000\", router))\n}\n\nfunc postMessage(w http.ResponseWriter, req *http.Request) {\n\tvar message Message\n\tjson.NewDecoder(req.Body).Decode(\u0026message)\n\n\tresult, _ := database.Collection(\"messages\").InsertOne(context.Background(), bson.M{\n\t\t\"message\": message.Message,\n\t})\n\n\tw.Header().Set(\"content-type\", \"application/json\")\n\tjson.NewEncoder(w).Encode(result)\n}\n\nfunc getMessage(w http.ResponseWriter, req *http.Request) {\n\tparams := mux.Vars(req)\n\toid, err := primitive.ObjectIDFromHex(params[\"id\"])\n\n\tif err != nil {\n\t\tw.WriteHeader(http.StatusUnprocessableEntity)\n\t\tjson.NewEncoder(w).Encode(`{\"error\": \"Ungültige ObjectID\"}`)\n\t\treturn\n\t}\n\n\tvar message Message\n\terr = database.Collection(\"messages\").FindOne(context.Background(), bson.M{\"_id\": oid}).Decode(\u0026message)\n\n\tif err != nil {\n\t\tw.WriteHeader(http.StatusNotFound)\n\t\tjson.NewEncoder(w).Encode(`{\"error\": \"Message not found\"}`)\n\t\treturn\n\t}\n\n\tw.Header().Set(\"content-type\", \"application/json\")\n\tjson.NewEncoder(w).Encode(message)\n}\n\nfunc getMessages(w http.ResponseWriter, req *http.Request) {\n\tcursor, err := database.Collection(\"messages\").Find(context.Background(), bson.M{})\n\n\tif err != nil {\n\t\tw.WriteHeader(http.StatusUnprocessableEntity)\n\t\tjson.NewEncoder(w).Encode(`{\"error\": \"Ein Fehler beim Aufrufen ist aufgetreten\"}`)\n\t\treturn\n\t}\n\n\tdefer cursor.Close(context.Background())\n\n\tmessages := []Message{}\n\tfor cursor.Next(context.Background()) {\n\t\tvar message Message\n\t\tcursor.Decode(\u0026message)\n\t\tmessages = append(messages, message)\n\t}\n\n\tif err := cursor.Err(); err != nil {\n\t\tw.WriteHeader(http.StatusUnprocessableEntity)\n\t\tjson.NewEncoder(w).Encode(`{\"error\": \"Ein Fehler beim Iterieren ist aufgetreten\"}`)\n\t\treturn\n\t}\n\n\tw.Header().Set(\"content-type\", \"application/json\")\n\tjson.NewEncoder(w).Encode(messages)\n}\n```\n\nWenn wir diesen nun mit\n```bash\ngo run .\n```\nausführen und zu [http://localhost:3000](http://localhost:3000) navigieren, sollten wir ein leeres Array als Antwort erhalten, da wir noch keine Daten in der Datenbank haben.\n\n----\n\n## Wie funktioniert die Anwendung?\n\nIm Prinzip ist sie ähnlich aufgebaut wie in unserem Beispiel. Allerdings wurde für eine bessere Lesbarkeit auf eine einzelne Go-Datei verzichtet und dafür einzelne Pakete für die Datenbank, Nachrichten und Authentisierung erstellt.\n\nDas Nachrichten Paket hat hier noch zusätzliche Validierer und Filter optionen wie limit und skip bei GET All Anfragen.\n\nMöchte ein Nutzer sich anmelden so offnet sich im Frontend ein neues Fenster mit der URL \".../auth/PROVIDER\" Provider kann hier Google oder GitHub sein.\n\nDas Lesen, Löschen und Ändern der Nachrichten gelingt mittels REST-Schnittstelle\n\n---\n\n## Herausforderungen\nDie größte Herausforderung war bereits zu Beginn klar. Ich muss eine neue Sprache lernen und mich mit der Syntax von C vertraut machen. Da ich bereits Sprachen wie PHP, Java, JavaScript und TypeScript behersche, war dies jedoch recht einfach.\n\nWeitere Probleme kamen immer wieder während der Entwicklung auf. So gab es Probleme mit den CORs wodurch ich die Schnittstellen lokal zwar super aufrufen und nutzen konnte, gehostet jedoch nie durchkam. Dieses Problem konnte ich jedoch mit Hilfe eines externen Pakets ([github.com/rs/cors](github.com/rs/cors)) lösen.\n\nEin weiteres Problem hatte ich beim import lokaler Pakete. Dies war mehr ein Verständnis Problem und weniger ein technisches Problem. Beim Import ist zu beachten, dass immer der komplette Pfad angegeben werden muss. Im Vergleich hierzu versuchte ich zunächst den herkömmlichen Weg von JavaScript zu verwenden, indem ich als Pfad lediglich ./\\\u003cpackageName\u003e angab. In Go muss jedoch der komplette Pfad angegeben werden: \\\u003cmoduleName\u003e/\\\u003cpackageName\u003e\n\nFür das Deployment nutzte ich schließlich Docker. Docker kannte ich nur aus der Uni und auch nur flüchtig. Selbst damit gearbeitet hatte ich bis zu diesem Zeitpunkt damit noch nicht. Um die Anwendung zu Dockerizen musste ein Dockerfile geschrieben werden. Dieses baut einerseits die Anwendung, installiert hierfür die benötigten Module und kopiert im Anschluss alle notwedigen Dateien. Wenn ich die Bundlesize vergleiche, ist mir aufgefallen, dass diese sehr viel kleiner ist und ich somit auch Speicherplatz auf meinem Server einspeichern konnte (Vgl. Node.js)\n\nDas Bauen, Publishen, Fetchen und Reloaden wurde alles von einer einzelnen GitHub Action gelöst. Hierin bestand dann auch das nächste Problem: Wie schreibe ich eine GitHub Action?\nHierzu gabs einige nützlichen Ressourcen im Internet, auch speziell für Go.\n\nNun blieb noch ein einziges Problem offen: TLS/SSL.\nWenn die Anwendung nur lokal oder im Firmennetz laufen würde, wäre dies nicht besonders kritisch. Da dies aber nicht der Fall war und die Anwendung für die Anmeldung oAuth verwendet (SSL notwendig), musste dieses Problem schließlich auch noch gelößt werden.\nIm Endeffekt ging es so aus, dass ich nginx und Docker auf dem Server installiert habe. Nginx war hierbei mein Proxy, welcher sich um SSL kümmert und die Anfragen auf einen bestimmten Pfad oder eine bestimmte subdomain mittels reverse proxy intern umleitet. Dadurch lief Go seöbst intern nur mit HTTP nach außen mittles nginx jedoch über HTTPS. Auch hierfür gab es auf digitalocean einige hilfreiche Anleitungen um nginx mit ssl und docker zu verwenden.\n\nUm die Anwendung schließlich auf dem Server zu starten war eine docker-compose.yml notwendig. Diese beschreibt den Prozess (Name, Port) und gibt an welches Dockerimage verwendet werden soll. Hierdurch konnte in der GitHub Action zum Fetchen und Neustarten einfach ein update gepullt werden und der Prozess neugestartet werden.\n\nDa ich bis hier aber allerdings schon sehr viel neues gelernt hatte und zufrieden mit dem Gesamtprozess war, habe ich auf einen Einsatz von Kubernetes verzichtet. Beim persönlichen Testen der Downtime habe ich keine großen Zeitspannen festgestellt, was allerdings auch an der Schlankheit der Testanwendung liegen kann.\n\n---\n\n## Weitere persönliche Erfahrungen mit Go\nNachdem ich den ersten fertigen prototypen inklusive CI/CD Donnerstags fertiggestellt hatte und Freitags ausgiebig testen und bewerten konnte, habe ich angefangen für private Projekte im Backend auf Go zu setzen. Ich war erstaunt wie viel ich in den 2 Wochen mit Go bereits gelernt hatte und ohne Hilfe erneut wiedergeben konnte. Ich hab mich also rangesetzt und für jeden Service eine eigene kleine Go Anwendung, inkl. Dockerfile und CI/CD geschrieben.\n\nNachdem ich mir eine einheitliche Grundstruktur ausgedacht hatte, war der Rest super fix fertig. Ich bin hier mal gespannt wo mich das ganze hinbringt und was ich dabei noch alles lernen werde. Aber zum jetzigen Stand scheint es so, als würde ich nun statt auf NestJS auf Go setzen. Dies hat mehrer Gründe. Es ist super einfach eine Go-Anwendung zu Dockerizen und publishen, die CI/CD Pipeline ist extrem simpel und leicht zu verstehen. Gerade die Schnelligkeit von Go (kompilierte Sprache) und Nebenläufigkeiten machen Go zu einer idealen Sprache für Webservices.\n\nPersönlich war ich überrascht wie viele Community-Packages es bereits gibt und wie einfach und schnell diese integriert werden können. Wenn ich die Zeit vergleiche, in der ich oAuth in meine NestJS Anwendungen eingebaut habe und wie lange das gedauert hat, war es in Go eine Sache von zehn Minuten.\n\n---\n\n## Finale Bewertung von Go als Sprache für Webservices\n\nNach meinen ausgiebigen Wochen mit Go muss ich sagen, dass ich echt überrascht bin, wie schnell man es lernen kann und wie schnell man damit Erfolge erzielt. Es ist für mich definitiv zu einer alternative für Webservices geworden, wenn nicht sogar to Go-To-Sprachen für Webservices.\n\nIn Anbetracht für einen späteren Einsatz in der SV Informatik muss ich sagen, dass es irgendwann in IV zum Einsatz für COR kommen kann! Hierfür sprechen viel zu viele positive Aspekte. Spannend wäre hier auch zu untersuchen, wie viel des bisherigen Codes möglicherweise ganz einfach in Go umgeschrieben werden kann, da der Syntax zu C sehr ähnlich (wenn nicht sogar gleich) ist.\n\nGleichzeitig bietet dies auch die Möglichkeit SVI-Interne Go-Packages zu schreiben, welche von anderen Services genutzt werden können. Beispielsweise ein Formelmodul, o.ä.. Dies könnten dann über go get gitea.svi (o.ä.) eingebunden und genutzt werden.\n\nDa ich für den Prototyen Docker und GitHub Actions verwendet habe, bleibt die Untersuchung des Deployments mittels OpenShift offen. Hierzu gibt es allerdings auch schon ein offizielles Repo von OpenShift wie man Go-Anwendungen deployen kann. Dieses ist [hier](https://github.com/openshift/golang-ex) zu finden.\n\n---\n\nBei Rückfragen stehe ich (Timo Scheuermann) gerne und jederzeit zur Verfügung.\n\nDas verwendete Dockerfile und der verwendete Workflow befinden sich ebenfalls in diesem Repo.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimoscheuermann%2Fgofeed-backend","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftimoscheuermann%2Fgofeed-backend","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimoscheuermann%2Fgofeed-backend/lists"}