It’s #FrontendFriday – Wie mocke ich ein Backend mit Angular?

09.04.2021

Es ist #FrontendFriday und heute zeige ich euch, wie ihr losgelöst von einem Backend arbeiten könnt…

Das Szenario

Stellt euch vor, ihr seid ein Frontendentwickler und sollt eine Angular App schreiben, die Informationen von Filmen wie Titel, Erscheinungsdatum und Genre in einer Tabelle anzeigen soll. Die Informationen sind in einer Datenbank gespeichert, die ihr dann von einem Backend erhaltet. Der Entwickler, der das Backend schreiben soll, ist leider verhindert. Da ihr Zeit habt, sollt ihr aber schon mal anfangen. Ich nehme an, jeder Frontendentwickler hat das so oder so ähnlich erlebt. Die Frage, die sich dann stellt, ist Folgende: Wie kann ich ohne ein Backend meine Angular App entwickeln und sichergehen, dass diese App dann keine oder wenig Anpassungen braucht, wenn das Backend entwickeln wird?

Angular to the rescue

Zum Glück hat Angular hier schon einen Weg vordefiniert. Es bietet die Möglichkeit http-Anfragen (im weiteren Request genannt) die aus der Angular App ausgehen, über einen Proxy weiterzuleiten (Siehe: Proxying to a backend server)[1]. Was genau bedeutet das? – „Ein Proxy ist eine Kommunikationsschnittstelle in einem Netzwerk aus Rechnern.“ [2] Das bedeutet, anstelle des wirklichen Backends können wir auch den Proxy ansprechen. Diesen Proxy können wir dann so einstellen, dass er uns eine Antwort in der Form eines JSON o. ä. sendet. Somit können wir lokal einen Ansprechpartner für unsere Request definieren, ohne wirklich ein Backend erstellen zu müssen.

Genug Theorie, wie sieht das Ganze aus?

Vorab: Es gibt mehrere Wege den Proxy einzurichten. Ich zeige euch den für das oben genannte Szenario. Weitere Infos können aus der offiziellen Angular Dokumentation entnommen werden: Proxying to a backend server.

Zunächst einmal benötigen wir eine Datei, in der wir unsere Konfigurationen eintragen können. Diese nennen wir „proxy.conf.js“. Um Angular zu sagen, dass wir einen Proxy benutzen, müssen wir in der „angular.json“ den Pfad dieser Datei wie folgt angeben:

Da wir das Ganze nur lokal benutzen wollen, erstellen wir unter „serve“ eine Konfiguration, die wir „development“ nennen und tragen dort unseren Pfad ein. In diesem Beispiel ist die „proxy.conf.js“ auf der gleichen Ebene wie die „angular.json“ , deshalb hier nur der Dateiname. Angular weiß nun, dass wir einen Proxy verwenden möchten. Jetzt stellen wir den Proxy ein, damit er uns Daten liefert bei bestimmten Anfragen.

Dies kann wie folgt aussehen:

const PROXY_CONFIG = [
  {
    context: [
      "/movies**"
    ],
    // Has to be set to physical file otherwise it gets an endless loop and we can't read body of request
    "target": "http://localhost:4200/assets/mocks/emptyFile.json",
    "secure": false,
    "onProxyReq": (proxyReq, req, res) => {
      return res.json([{
		title:"Super Awesome Movie",
		releaseDate: "2020-12-12",
		genre: "Action"
	  }, {
		title:"Super Awesome Movie 2 - More Awesomeness",
		releaseDate: "2021-03-03",
		genre: "Action, Drama"
	  }]);
    }
  }
]

module.exports = PROXY_CONFIG;

Was genau habe ich hier eingestellt?

context: Hier werden die Pfade reingeschrieben, bei denen der Proxy greift. Wenn ich einen Request aus meiner Angular stelle, sucht der Proxy in dem context Array nach einer Übereinstimmung und führt dann die jeweiligen Konfigurationen aus. Mit „**“ kann man eine Wildcard setzen, was bedeutet, dass der Proxy auch bei „/movies/1337“greifen würde. [3]

target: Ich habe hier festgestellt, dass der Proxy einen validen Endpunkt benötigt. Deshalb habe ich hier eine Datei eingetragen, die wirklich in der Angular App existiert. Die Datei befindet sich im Ordner /assets/mocks meines Projektes und beinhaltet ein leeres JSON (= {}). [3]

secure: Muss angegeben werden, da der Proxy wissen muss, ob der Request über http oder https aufgerufen wird. [3]

onProxyReq: Hier kann man eine Antwort definieren, die man auf die Anfrage erhält. Die 2 wichtigen Parameter sind hier req oder res. Mit dem req Parameter lassen sich Informationen aus der Anfrage auslesen, wie z. B. welche HTTP Methode benutzt wird. Das res Objekt wird benötigt, um eine Antwort zu schicken. [3] [4] [5]

Der Proxy benutzt die ExpressJS (Dokumentation zu ExpressJS). Das obere Beispiel sendet immer ein JSON. Wir können dies aber auch so verändern, dass für einen HTTP DELETE Request ein anderer Statuscode und ein leeres JSON zurückerhält:

const PROXY_CONFIG = [
  {
    context: [
      "/movies**"
    ],
    // Has to be set to physical file otherwise it gets an endless loop and we can't read body of request
    "target": "http://localhost:4200/assets/mocks/emptyFile.json",
    "onProxyReq": (proxyReq, req, res) => {
	if(req.method === "DELETE") {
		return res.status(204).json({});
	}
	
      return res.json([{
		title:"Super Awesome Movie",
		releaseDate: "2020-12-12",
		genre: "Action"
	  }, {
		title:"Super Awesome Movie 2 - More Awesomeness",
		releaseDate: "2021-03-03",
		genre: "Action, Drama"
	  }]);
    }
  }
]

module.exports = PROXY_CONFIG;

In diesem Beispiel wird für den Request DELETE „/movies/1337“ eine leere Antwort mit http Statuscode 204 an die Angular App geschickt.

Fazit

Mit diesem Proxy können wir als Frontendentwickler losgelöst vom Backend arbeiten, weil wir das Backend so simulieren können. Noch eine Erwähnung: Da wir in der „angular.json“ eine Konfiguration definiert haben, diedevelopmentheißt, müssen wir das beim lokalen Starten der Angular App das auch mitgeben. Der Befehl hierfür wäre dann ng serveconfiguration=development.

Anbei noch ein Beispielprojekt zum besseren Verständnis.

 

Mehr über Frontend Entwicklung erfahren


Quellen:
[1]: Proxying to a backend server – https://angular.io/guide/build#proxying-to-a-backend-server
[2]: Proxy (Rechnernetz) – https://de.wikipedia.org/wiki/Proxy_(Rechnernetz)
[3]: ExpressJS – Req – http://expressjs.com/de/api.html#req
[4]: ExpressJS – Res – http://expressjs.com/de/api.html#res

Zurück zur Übersicht

Kommentar verfassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

*Pflichtfelder

*