<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>javascript Archives - Nerd Corner</title>
	<atom:link href="https://nerd-corner.com/de/tag/javascript-de/feed/" rel="self" type="application/rss+xml" />
	<link>https://nerd-corner.com/de/tag/javascript-de/</link>
	<description>Craft your dreams!</description>
	<lastBuildDate>Wed, 11 Dec 2024 13:15:29 +0000</lastBuildDate>
	<language>de</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.2</generator>

<image>
	<url>https://nerd-corner.com/wp-content/uploads/2019/10/cropped-LogoNerdCorner-2-32x32.png</url>
	<title>javascript Archives - Nerd Corner</title>
	<link>https://nerd-corner.com/de/tag/javascript-de/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Erfahrungen aus der Praxis: NestJS auf Vercel hosten</title>
		<link>https://nerd-corner.com/de/erfahrungen-aus-der-praxis-nestjs-auf-vercel-hosten/</link>
					<comments>https://nerd-corner.com/de/erfahrungen-aus-der-praxis-nestjs-auf-vercel-hosten/#respond</comments>
		
		<dc:creator><![CDATA[Nerds]]></dc:creator>
		<pubDate>Thu, 14 Nov 2024 07:55:27 +0000</pubDate>
				<category><![CDATA[Software-DE]]></category>
		<category><![CDATA[angular]]></category>
		<category><![CDATA[Anleitung]]></category>
		<category><![CDATA[cookie-sessions]]></category>
		<category><![CDATA[CORS Fehler]]></category>
		<category><![CDATA[Cors Fehler nest]]></category>
		<category><![CDATA[Datenbank]]></category>
		<category><![CDATA[Datenbank hosten]]></category>
		<category><![CDATA[Datenbank konfigurieren]]></category>
		<category><![CDATA[Einstellungen]]></category>
		<category><![CDATA[Erfahrungsbericht]]></category>
		<category><![CDATA[express]]></category>
		<category><![CDATA[express-sessions]]></category>
		<category><![CDATA[express.js]]></category>
		<category><![CDATA[google cloud]]></category>
		<category><![CDATA[google cloud mysql]]></category>
		<category><![CDATA[Hosten]]></category>
		<category><![CDATA[internal Server Fehler Vercel]]></category>
		<category><![CDATA[Internal Server Fehler Vercel nest]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[JSON]]></category>
		<category><![CDATA[mySql]]></category>
		<category><![CDATA[Nest]]></category>
		<category><![CDATA[NestJs]]></category>
		<category><![CDATA[Redis]]></category>
		<category><![CDATA[redis session management]]></category>
		<category><![CDATA[redis session management nest.js]]></category>
		<category><![CDATA[Schritt für Schritt Anweisung]]></category>
		<category><![CDATA[Server]]></category>
		<category><![CDATA[Session Mamagement]]></category>
		<category><![CDATA[session management]]></category>
		<category><![CDATA[typescript]]></category>
		<category><![CDATA[Vercel]]></category>
		<category><![CDATA[vercel.json]]></category>
		<category><![CDATA[Visual Studio]]></category>
		<guid isPermaLink="false">https://nerd-corner.com/?p=1632</guid>

					<description><![CDATA[<p>Nachdem ich Stunden damit verbracht habe, meine NestJS-App auf Vercel zu hosten und es nicht zum Laufen brachte, dachte ich mir, dass es an der &#8230; </p>
<p>The post <a href="https://nerd-corner.com/de/erfahrungen-aus-der-praxis-nestjs-auf-vercel-hosten/">Erfahrungen aus der Praxis: NestJS auf Vercel hosten</a> appeared first on <a href="https://nerd-corner.com/de">Nerd Corner</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Nachdem ich Stunden damit verbracht habe, meine NestJS-App auf Vercel zu hosten und es nicht zum Laufen brachte, dachte ich mir, dass es an der Zeit ist, zu dokumentieren, was ich gelernt habe &#8211; nicht nur, um mir in Zukunft Zeit zu sparen, sondern hoffentlich auch, um anderen zu helfen, einige der Fallstricke zu vermeiden, in die ich geraten bin. Hier ist eine Aufschlüsselung dessen, was funktioniert hat, was nicht, und wie ich es schließlich geschafft habe, dass alles reibungslos läuft.</p>
<p><em><strong>Das könnte dich ebenfalls interessieren:</strong> <a href="https://nerd-corner.com/de/swagger-api-doku-zu-node-server-hinzufuegen/" target="_blank" rel="noopener">Swagger zu Node Server hinzufügen</a></em></p>
<h3>Schritt 1: NestJS auf Vercel hosten</h3>
<p>Das Wichtigste zuerst: Die Grundeinstellungen für die Bereitstellung auf Vercel. Vercel ist großartig für Serverless, aber die Arbeit mit NestJS benötigt ein paar Anpassungen. Die Hauptsache ist, eine vercel.json-Konfigurationsdatei einzurichten, die Vercel genau sagt, wie die App zu behandeln ist.</p>
<p>Hier ist die Endkonfiguration:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="json" data-enlighter-theme="beyond">{
  "version": 2,
  "builds": [
    {
      "src": "src/main.ts",
      "use": "@vercel/node"
    }
  ],
  "routes": [
    {
      "src": "/(.*)",
      "dest": "src/main.ts",
      "methods": [
        "GET",
        "POST",
        "PUT",
        "PATCH",
        "OPTIONS",
        "DELETE",
        "HEAD",
        "CONNECT",
        "TRACE"
      ]
    }
  ]
}
</pre>
<p>Ich habe daraufhin in Vercel folgende Fehlermeldung erhalten:</p>
<pre>This Serverless Function has crashed. Your connection is working correctly. 
Vercel is working correctly. 500: INTERNAL_SERVER_ERROR 
Code: FUNCTION_INVOCATION_FAILED ID: bom1::sgk4v-1711014022883-1e9ed54f4c37</pre>
<p>Bei einem Blick in die Logs stellte ich fest, dass die Datenbankverbindung ein Problem darstellte und außerdem die folgende Logmeldung angezeigt wurde:</p>
<pre>No exports found in module "/var/task/app-name/src/main.js".
Did you forget to export a function or a server?</pre>
<p>Es stellte sich heraus, dass ich den zweiten Teil der Fehlermeldung ignorieren und mich nur auf die Datenbankverbindung konzentrieren musste.</p>
<h3>Schritt 2: Konfigurieren der Datenbank</h3>
<p>Für meine Anwendung habe ich eine mysql-Datenbank mit mehreren Schemata verwendet. Ich habe mehrere kostenlose Angebote ausprobiert, aber sie waren nicht mit dem Ansatz mehrerer Schemata kompatibel. Daher habe ich mich für das Hosting bei Google Cloud entschieden. Ich habe es auf einen Preis von 0,01 $ pro Stunde heruntergeschraubt und das 300 $-Einsteigerangebot genutzt.</p>
<p>Um Vercel eine Verbindung zu ermöglichen, musste in der Konfiguration von Google Cloud die IP-Adresse auf <strong>0.0.0.0/0</strong> gesetzt werden, so dass die Datenbank von jeder IP-Adresse aus zugänglich war.</p>
<h3>Schritt 3: Umgang mit CORS</h3>
<p>Die CORS Fehler haben auch für einige Kopfschmerzen gesorgt. Stelle sicher, dass OPTIONS für CORS Preflight-Anfragen zugelassen wird, da Vercel eine explizite Erlaubnis für Cross-Origin-Anfragen benötigt. Am Ende musste ich eine Menge Header hinzufügen, um sicherzustellen, dass die Anfragen erlaubt waren:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="beyond">app.enableCors({
    origin: 'domain-name',
    credentials: true,
    methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS',
    allowedHeaders: [
      'Origin',
      'X-Requested-With',
      'Content-Type',
      'Accept',
      'Authorization',
    ],
  });</pre>
<h3>Schritt 4: Umstellung auf express-session und Redis für die Sitzungsverwaltung</h3>
<p>Einer der kniffligsten Teile war die Sessions zum Laufen zu bringen. Ich begann mit der <em>cookie-session</em> Bibliothek, aber Vercel ignoriert sie komplett. Nach einem Blick in die Dokumentation und einigem Ausprobieren wechselte ich zur <em>express-session</em> Bibliothek, da sie populärer ist und besser mit Vercels serverloser Umgebung funktioniert.</p>
<p>Aus irgendeinem Grund muss die Import-Syntax genau so aussehen:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="beyond">import session = require('express-session');</pre>
<p>Außerdem musste ich die Sitzungsmiddleware mit aktiviertem Vertrauensproxy konfigurieren, da Vercel Anfragen proxifiziert. So sieht die Einstellung aus:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="beyond">const expressApp = app.getHttpAdapter().getInstance();
expressApp.set('trust proxy', true);</pre>
<p>Auch die Einstellung secure: true und sameSite: &#8217;none&#8216; war wichtig, um sicherzustellen, dass Cookies über HTTPS und verschiedene Domainanfragen funktionieren!</p>
<p>Beachten, dass mit Vercel mehrere serverlose Instanzen Anfragen bearbeiten können, was bei parallelen Anfragen leider zu Sitzungskonflikten führt. Um dies zu beheben, habe ich meinen Sitzungsspeicher mit einer Redis-Instanz verbunden. Glücklicherweise war das super einfach.</p>
<p><a href="https://redis.io/">Redis</a> hält die Sitzungsdaten konsistent und vermeidet Konflikte zwischen Anfragen, insbesondere unter Last. Hier der Code:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="beyond">const expressApp = app.getHttpAdapter().getInstance();
expressApp.set('trust proxy', true);

const redisClient = createClient({
    password: process.env.REDIS_PASSWORD,
    socket: {
      host: process.env.REDIS_HOST,
      port: parseInt(process.env.REDIS_PORT, 10),
    },
  });

  redisClient
    .connect()
    .catch((err) =&gt;
      console.log('Could not establish a connection with Redis: ' + err),
    );

  redisClient.on('error', (err) =&gt; console.log('Redis error: ' + err));
  redisClient.on('connect', () =&gt;
    console.log('Connected to Redis successfully'),
  );

  app.use(
    session({
      store: new RedisStore({ client: redisClient }),
      secret: process.env.COOKIE_SECRET,
      cookie: {
        secure: process.env.NODE_ENV !== 'development',
        sameSite: process.env.NODE_ENV === 'development' ? 'lax' : 'none',
        maxAge: 86400000,
      },
    }),
  );</pre>
<h3>Schritt 5: Hinzufügen von withCredentials im Frontend</h3>
<p>Dieser Schritt ist nur eine Randnotiz: Damit Session-Cookies zwischen Frontend und Backend funktionieren, muss withCredentials in den HTTP-Anfragen meines Frontends auf true gesetzt werden. Dies ermöglicht die Einbeziehung von Cookies in herkunftsübergreifende Anfragen, was wichtig ist, wenn das Frontend und das Backend getrennt gehostet werden. Ich musste sicherstellen, dass der HTTP-Client von Angular diese Einstellung aktiviert hat.</p>
<h3>Schritt 6: Schriftart hinzufügen</h3>
<p>Um Schriftdateien in Ihr NestJS-Projekt einzubinden, muss man die CompilerOptions in der nest-cli.json-Datei verwenden, um Assets für die Build-Ausgabe zu definieren, z. B. durch die Angabe von „include“: „**/*.ttf“ und ‚outDir‘: „dist/src“.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="json" data-enlighter-title="nest-cli.json" data-enlighter-theme="beyond">{
  "$schema": "https://json.schemastore.org/nest-cli",
  "collection": "@nestjs/schematics",
  "sourceRoot": "src",
  "compilerOptions": {
    "assets": [
      {
        "include": "**/*.ttf",
        "outDir": "dist/src"
      }
    ],
    "deleteOutDir": true
  }
}</pre>
<p>Anschließend kann man nach dem Build die Schriftarten im Code mit path.resolve referenzieren, z. B. path.resolve(__dirname, &#8218;../fonts/Roboto-Regular.ttf&#8216;). Auf diese Weise wird sichergestellt, dass die Schriftartendateien mit dem Build gebündelt werden und während der Laufzeit zugänglich sind.</p>
<h3>Schlussgedanken zum Thema NestJS auf Vercel hosten</h3>
<p>Das Deployment meiner NestJS-App auf Vercel war eine wahre Achterbahnfahrt. Manchmal hatte ich das Gefühl, dass ich kurz davor war, alles perfekt zum Laufen zu bringen, nur um dann mit neuen Fehlern konfrontiert zu werden, die mich zurück in den Fehlersuch-Modus schickten. Es gab Momente der Frustration &#8211; vor allem im Zusammenhang mit der Sessionverarbeitung und CORS-Problemen. Aber jede Lösung brachte ein neues Hoch, und jeder behobene Fehler fühlte sich wie ein kleiner Sieg an.</p>
<p>Jetzt, wo endlich alles reibungslos funktioniert, kann ich sagen, dass es ein großartiges Gefühl ist. Wenn ich sehe, dass meine Anwendung so funktioniert, wie ich es mir vorgestellt habe, ist das all die Kopfschmerzen wert. Es ist eine große Erleichterung, aber noch mehr freut es mich, zu wissen, dass ich jede Hürde überwunden habe und auf das, was ich gelernt habe, zurückblicken kann. Ich hoffe, dieser Leitfaden kann anderen einige dieser Schwierigkeiten ersparen und helfen, diesen „Es funktioniert einfach“-Moment etwas schneller zu erreichen!</p>
<p>The post <a href="https://nerd-corner.com/de/erfahrungen-aus-der-praxis-nestjs-auf-vercel-hosten/">Erfahrungen aus der Praxis: NestJS auf Vercel hosten</a> appeared first on <a href="https://nerd-corner.com/de">Nerd Corner</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://nerd-corner.com/de/erfahrungen-aus-der-praxis-nestjs-auf-vercel-hosten/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>WeMos D1 R2 &#8211; Ganze Webseite mit html, css &#038; js hosten</title>
		<link>https://nerd-corner.com/de/wemos-d1-r2-ganze-webseite-mit-html-css-js-hosten/</link>
					<comments>https://nerd-corner.com/de/wemos-d1-r2-ganze-webseite-mit-html-css-js-hosten/#respond</comments>
		
		<dc:creator><![CDATA[Nerds]]></dc:creator>
		<pubDate>Sat, 02 Mar 2024 23:11:48 +0000</pubDate>
				<category><![CDATA[Arduino Projekte]]></category>
		<category><![CDATA[DIY]]></category>
		<category><![CDATA[Software-DE]]></category>
		<category><![CDATA[Arduino]]></category>
		<category><![CDATA[Arduino mit WLAN]]></category>
		<category><![CDATA[C Programmierung]]></category>
		<category><![CDATA[Datenübertragung]]></category>
		<category><![CDATA[ESP8266]]></category>
		<category><![CDATA[ESP8266 mit mDNS]]></category>
		<category><![CDATA[ganze Webseite]]></category>
		<category><![CDATA[Hardware]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[JS]]></category>
		<category><![CDATA[Klemmsystem]]></category>
		<category><![CDATA[Kommunikationstechnik]]></category>
		<category><![CDATA[led]]></category>
		<category><![CDATA[Lokales Netzwerk]]></category>
		<category><![CDATA[Netzwerk]]></category>
		<category><![CDATA[Schritt für Schritt Anweisung]]></category>
		<category><![CDATA[Webseite hosten]]></category>
		<category><![CDATA[Webserver]]></category>
		<category><![CDATA[WeMos]]></category>
		<category><![CDATA[WeMos D1]]></category>
		<category><![CDATA[WeMos D1 in Arduino IDE]]></category>
		<category><![CDATA[WeMos D1 mini]]></category>
		<category><![CDATA[WeMos D1 R2]]></category>
		<category><![CDATA[WeMOS D1 Webserver]]></category>
		<category><![CDATA[WIFI]]></category>
		<guid isPermaLink="false">https://nerd-corner.com/?p=1526</guid>

					<description><![CDATA[<p>Im ersten Teil der WeMos Reihe wurde die Einrichtung und Wlan Integration erklärt. Darauf aufbauend wird in diesem Artikel Schritt für Schritt beschrieben wie man &#8230; </p>
<p>The post <a href="https://nerd-corner.com/de/wemos-d1-r2-ganze-webseite-mit-html-css-js-hosten/">WeMos D1 R2 &#8211; Ganze Webseite mit html, css &#038; js hosten</a> appeared first on <a href="https://nerd-corner.com/de">Nerd Corner</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Im <a href="https://nerd-corner.com/de/wemos-d1-r2-erste-schritte-und-wlan-integration/">ersten Teil</a> der WeMos Reihe wurde die Einrichtung und Wlan Integration erklärt. Darauf aufbauend wird in diesem Artikel Schritt für Schritt beschrieben wie man eine komplette Webseite mit HTML Seiten, CSS Styling und JavaScript Funktionen auf einem WeMos hosten kann. Das ganze ist ziemlich einfach und unglaublich hilfreich! Ich kann es selbst kaum fassen wie schwer es ist eine vernünftige Anleitung für diese wichtige Funktion im Internet zu finden.</p>
<p>Ich sehe leider sehr oft Blogartikel in denen der HTML Code in der Arduino Datei eingebettet wird. Sowas kann man für eine Mini Demonstration zwar machen, ist aber im Alltag völliger Schwachsinn. Es ist viel zu unübersichtlich und sobald das Projekt wächst nicht mehr nutzbar.</p>
<p>Die ordentliche Alternative ist einen Ordner namens &#8222;data&#8220; einzurichten und in diesem die Webseiten als html Dateien abzulegen. Zusätzlich wird das Styling als CSS Datei gespeichert und es können sogar Funktionen per JavaScript Datei ausgeführt werden. Also alles 1:1 wie auf einem gewöhnlichem Webserver!</p>
<p><em><strong>Das könnte dich auch interessieren: </strong><a href="https://nerd-corner.com/de/wemos-d1-r2-erste-schritte-und-wlan-integration/">WeMos D1 R2 erste Schritte und Wlan Integration</a></em></p>
<h2>Liste der Komponenten</h2>
<ul>
<li>Arduino IDE (Entwicklungsumgebung)</li>
<li><a href="https://amzn.to/3tQPCjC" target="_blank" rel="noopener">WeMos D1 R2</a></li>
</ul>
<h3>Das Einrichten des Dateisystems (offiziell SPIFFS) geschieht einmalig und ist kinderleicht:</h3>
<p><a href="https://nerd-corner.com/de/wemos-d1-r2-erste-schritte-und-wlan-integration/">(Zunächst sollte man die grundlegende Einrichtung aus dem ersten Teil abgeschlossen haben!)</a></p>
<ol>
<li>Auf <a href="https://github.com/esp8266/arduino-esp8266fs-plugin/releases/tag/0.2.0">GitHub</a> eine Kopie der Datei &#8222;ESP8266FS-0.2.0.zip&#8220; herunterladen und entpacken</li>
<li>Die Datei esp8266fs.jar im Arduino-Tool-Verzeichnis ablegen. Der Pfad sieht etwa so aus: [home_dir]\Arduino\tools\ESP8266FS\tool\esp8266fs.jar (Siehe Bild) Ich musste den Pfadteil tools\ESP8266FS\tool\ im Arduino Ordner selbst erstellen. <img fetchpriority="high" decoding="async" class="zoooom aligncenter wp-image-1531" src="https://nerd-corner.com/wp-content/uploads/2024/03/Screenshot-file-path-esp8266fs.jar-.png" alt="Screenshot file path esp8266fs.jar" width="1080" height="335" srcset="https://nerd-corner.com/wp-content/uploads/2024/03/Screenshot-file-path-esp8266fs.jar-.png 1190w, https://nerd-corner.com/wp-content/uploads/2024/03/Screenshot-file-path-esp8266fs.jar--300x93.png 300w, https://nerd-corner.com/wp-content/uploads/2024/03/Screenshot-file-path-esp8266fs.jar--1024x318.png 1024w, https://nerd-corner.com/wp-content/uploads/2024/03/Screenshot-file-path-esp8266fs.jar--768x238.png 768w" sizes="(max-width: 1080px) 100vw, 1080px" /></li>
<li>Die Arduino IDE neustarten.</li>
</ol>
<p>Das wars auch schon! Man kann jetzt in der Arduino DIE unter Tools den neuen Punkt „ESP8266 Sketch Data Upload“ sehen.</p>
<h2>Wie kann man das neue Dateisystem jetzt nutzen?</h2>
<ol>
<li>Erstellen Sie in ihrem aktuellen WeMos Projekt Ordner einen zusätzlichen Ordner mit dem Namen „data“. So wie in dem nachfolgendem Bild</li>
</ol>
<p><img decoding="async" class="zoooom aligncenter wp-image-1528" src="https://nerd-corner.com/wp-content/uploads/2024/03/Wemos-data-folder.png" alt="Wemos data folder" width="1040" height="324" srcset="https://nerd-corner.com/wp-content/uploads/2024/03/Wemos-data-folder.png 1146w, https://nerd-corner.com/wp-content/uploads/2024/03/Wemos-data-folder-300x93.png 300w, https://nerd-corner.com/wp-content/uploads/2024/03/Wemos-data-folder-1024x319.png 1024w, https://nerd-corner.com/wp-content/uploads/2024/03/Wemos-data-folder-768x239.png 768w" sizes="(max-width: 1040px) 100vw, 1040px" /></p>
<ol start="2">
<li>Legen Sie die Dateien, die Sie hochladen möchten, in das &#8218;data&#8216;-Verzeichnis</li>
<li>Wählen Sie in der Arduino IDE im Menü &#8218;Tools&#8216; den WeMos aus und wählen Sie eine Größe bei &#8218;Flash Size&#8216;</li>
<li>Das Dialogfeld für den seriellen Monitor schließen!</li>
<li>Wählen Sie aus dem Menü &#8218;Tools&#8216; die Option &#8218;ESP8266 Sketch Data Upload&#8216;.</li>
</ol>
<p><img decoding="async" class="zoooom aligncenter wp-image-1529" src="https://nerd-corner.com/wp-content/uploads/2024/03/data-folder-upload.png" alt="Data folder upload" width="805" height="518" srcset="https://nerd-corner.com/wp-content/uploads/2024/03/data-folder-upload.png 851w, https://nerd-corner.com/wp-content/uploads/2024/03/data-folder-upload-300x193.png 300w, https://nerd-corner.com/wp-content/uploads/2024/03/data-folder-upload-768x495.png 768w" sizes="(max-width: 805px) 100vw, 805px" /></p>
<p>Sobald der Upload abgeschlossen ist zeigt das Nachrichtenfenster der Arduino IDE 100% Upload an.</p>
<h2>WeMos Beispielprogramm zum Ein- und Ausschalten der OnBoard LED</h2>
<p>Ähnlich wie im ersten Teil wird der Webserver die OnBoard LED steuern. Als Basis dient ebenfalls der Code aus dem ersten Teil. Der überarbeitete Code sieht so aus:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="cpp" data-enlighter-theme="atomic" data-enlighter-title="D1_webserver_filesystem">#include &lt;ESP8266WiFi.h&gt;
#include &lt;ESP8266WebServer.h&gt;
#include &lt;ESP8266mDNS.h&gt;

ESP8266WebServer server(80);

void setup() {
  Serial.begin(115200); //Baudrate
  Serial.println("ESP starts");

  WiFi.begin("NerdCornerWiFi","NerdCornerPassword");


  Serial.print("Connecting...");

  while(WiFi.status()!=WL_CONNECTED){ //Loop which makes a point every 500ms until the connection process has finished

    delay(500);
    Serial.print(".");
  }
  Serial.println();

  Serial.print("Connected! IP-Address: ");
  Serial.println(WiFi.localIP()); //Displaying the IP Address

  if (MDNS.begin("nerd-corner")) {
    Serial.println("DNS started, available with: ");
    Serial.println("http://nerd-corner.local/");
  }

  server.serveStatic("/", SPIFFS, "/", "max-age=86400");
  SPIFFS.begin();

  server.onNotFound([](){ 
    server.send(404, "text/plain", "Landing page not found! Don't forget to name your landing page 'index.html'!");  
  });
 
  server.on("/led", HTTP_POST, []() {    
     
    const String ledState = server.arg("ledstate");
    if(ledState=="on"){
      switchLedOn();
    }
    else if(ledState=="off"){
      switchLedOff();
    }
    server.send(200, "text/json", "{\"result\":\"ok\"}");
  });

  server.begin();
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  server.handleClient();
  MDNS.update();

}

void switchLedOff(){ 
  digitalWrite(LED_BUILTIN, HIGH);   // turn the D1 LED off 
}

void switchLedOn(){ 
  digitalWrite(LED_BUILTIN, LOW);    // turn the LED on 
}</pre>
<p>Auf ein paar Besonderheiten möchte ich dabei hinweisen. Wir haben beispielsweise folgendes hinzugefügt:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="cpp" data-enlighter-theme="atomic">server.serveStatic("/", SPIFFS, "/", "max-age=86400"); 
SPIFFS.begin();</pre>
<p>Ohne die beiden Zeilen wäre der Zugriff auf die Dateien im &#8222;data&#8220; Ordner nicht möglich. Bitte beachten, dass der Name &#8222;index.html&#8220; als default für die Landing Page eingestellt ist. Wenn man unbedingt möchte kann man das aber auch ändern.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="cpp" data-enlighter-theme="atomic">server.on("/led", HTTP_POST, []() {    
     
    const String ledState = server.arg("ledstate");
    if(ledState=="on"){
      switchLedOn();
    }
    else if(ledState=="off"){
      switchLedOff();
      }
      server.send(200, "text/json", "{\"result\":\"ok\"}");
  });</pre>
<p>Der &#8222;/led&#8220; Endpoint empfängt die Befehle vom Webserver. Lautet der Befehl &#8222;on&#8220; wird die LED eingeschaltet und bei &#8222;off&#8220; wird die LED ausgeschaltet.</p>
<h2>Wemos Webseite zum Ein- und Ausschalten der WeMos OnBoard LED</h2>
<p>Die Beispielwebseite ist ganz einfach aufgebaut. Sie besteht in erster Linie aus 2 Buttons zum Ein- und Ausschalten der LED.</p>
<p><img loading="lazy" decoding="async" class="zoooom aligncenter wp-image-1536" src="https://nerd-corner.com/wp-content/uploads/2024/03/Webseite-Wemos-Control.jpeg" alt="Website Wemos control LED" width="875" height="1946" srcset="https://nerd-corner.com/wp-content/uploads/2024/03/Webseite-Wemos-Control.jpeg 921w, https://nerd-corner.com/wp-content/uploads/2024/03/Webseite-Wemos-Control-135x300.jpeg 135w, https://nerd-corner.com/wp-content/uploads/2024/03/Webseite-Wemos-Control-461x1024.jpeg 461w, https://nerd-corner.com/wp-content/uploads/2024/03/Webseite-Wemos-Control-768x1708.jpeg 768w, https://nerd-corner.com/wp-content/uploads/2024/03/Webseite-Wemos-Control-691x1536.jpeg 691w" sizes="auto, (max-width: 875px) 100vw, 875px" /></p>
<p>Die Ordnerstruktur der Webseite ist sehr übersichtlich gehalten. Es gibt eine Hauptseite mit dem Namen &#8222;index.html&#8220; Dieser Name ist weltweit üblich für die Hauptseiten und wird auch automatisch vom WeMos entsprechend erkannt. Darüber hinaus einen &#8222;CSS&#8220; Ordner fürs Styling und einen &#8222;JS&#8220; Ordner für Funktionen.</p>
<p><img loading="lazy" decoding="async" class="zoooom aligncenter wp-image-1537" src="https://nerd-corner.com/wp-content/uploads/2024/03/Screenshot-data-file-system.png" alt="Screenshot esp8266 data file system" width="1045" height="347" srcset="https://nerd-corner.com/wp-content/uploads/2024/03/Screenshot-data-file-system.png 1152w, https://nerd-corner.com/wp-content/uploads/2024/03/Screenshot-data-file-system-300x100.png 300w, https://nerd-corner.com/wp-content/uploads/2024/03/Screenshot-data-file-system-1024x340.png 1024w, https://nerd-corner.com/wp-content/uploads/2024/03/Screenshot-data-file-system-768x255.png 768w" sizes="auto, (max-width: 1045px) 100vw, 1045px" /></p>
<p>Im Header Bereich der Webseite verlinken wir die Styles. Da wäre zum einen ein Standard Bootstrap, der alles automatisch bischen schöner macht und zusätzlich eine custom Styles Datei mit meinen eigenen Anpassungen. Außerdem werden im Header Bereich auch die Funktionen der Webseite verlinkt. Ich benutze den jQuery Standard um von der Webseite Requests an den WeMos zu senden. Meine eigenen custom Funktionen liegen in der &#8222;index.js&#8220;.</p>
<p>Bitte beachten, dass die jQuery Datei VOR der eigenen Datei eingebunden werden muss, sonst können keine jQuery Befehle im eigenen Code benutzt werden! Die eigenen Funktionen werden anschließend von den Buttons verwendet. Der HTML Code der Seite sieht insgesamt folgendermaßen aus:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="html" data-enlighter-theme="atomic">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;&lt;/html&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;meta charset="utf-8" /&gt;
    &lt;meta
      name="viewport"
      content="width=device-width, initial-scale=1, shrink-to-fit=no"
    /&gt;
    &lt;script type="text/javascript" src="js/jquery-3.5.1.min.js"&gt;&lt;/script&gt;
    &lt;script type="text/javascript" src="./js/index.js"&gt;&lt;/script&gt;
    &lt;link rel="stylesheet" href="css/bootstrap.min.css" /&gt;
    &lt;link rel="stylesheet" href="css/custom-style.css" /&gt;
    &lt;title&gt;D1 Webserver&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;D1 Webserver with filesystem&lt;/h1&gt;
    &lt;p&gt;
      This is an example for a WeMos Webserver with a filesystem. You can easily
      create webpages with html, css and js!
    &lt;/p&gt;

    &lt;h3&gt;Example to turn on and off the built in LED&lt;/h3&gt;
    &lt;button class="button-style" onclick="changeLEDState('on')"&gt;Turn on&lt;/button&gt;
    &lt;button class="button-style" onclick="changeLEDState('off')"&gt;
      Turn off
    &lt;/button&gt;

    &lt;h3&gt;Example to demo a JS function&lt;/h3&gt;
    &lt;button class="button-style" onclick="showAlert()"&gt;Show alert&lt;/button&gt;
  &lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Besonderes Augenmerk gilt der JavaScript Funktion &#8222;changeLEDState(value)&#8220;</p>
<pre class="EnlighterJSRAW" data-enlighter-language="js" data-enlighter-theme="atomic">function changeLEDState(value) {
  $.post("/led", { ledstate: value });
}</pre>
<p>Dadurch, dass jQuery genutzt wird für die Kommunikation mit dem WeMos, reicht ein einfaches Dollarzeichen mit dem entsprechendem Request Befehl. Bei diesem POST Request wird ebenfalls ein Wert mit geschickt, welcher entweder &#8222;on&#8220; oder &#8222;off&#8220; ist zum ein- und ausschalten der LED.</p>
<p>Die Webseite kann nachfolgend als zip Datei heruntergeladen werden.</p>
<h2>Dateien zum Herunterladen</h2>
<ul>
<li><a  data-e-Disable-Page-Transition="true" class="download-link" title="" href="https://nerd-corner.com/de/download/1540/?tmstv=1756254041" rel="nofollow" id="download-link-1540" data-redirect="false" >
	Wemos example webserver to control OnBoard LED</a>
</li>
</ul>
<p>The post <a href="https://nerd-corner.com/de/wemos-d1-r2-ganze-webseite-mit-html-css-js-hosten/">WeMos D1 R2 &#8211; Ganze Webseite mit html, css &#038; js hosten</a> appeared first on <a href="https://nerd-corner.com/de">Nerd Corner</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://nerd-corner.com/de/wemos-d1-r2-ganze-webseite-mit-html-css-js-hosten/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Swagger Api Doku zu Node Server hinzufügen</title>
		<link>https://nerd-corner.com/de/swagger-api-doku-zu-node-server-hinzufuegen/</link>
					<comments>https://nerd-corner.com/de/swagger-api-doku-zu-node-server-hinzufuegen/#comments</comments>
		
		<dc:creator><![CDATA[Nerds]]></dc:creator>
		<pubDate>Wed, 26 Oct 2022 17:07:59 +0000</pubDate>
				<category><![CDATA[Software-DE]]></category>
		<category><![CDATA[angular]]></category>
		<category><![CDATA[Api]]></category>
		<category><![CDATA[Backend]]></category>
		<category><![CDATA[Backend mit Swagger]]></category>
		<category><![CDATA[Backend Server]]></category>
		<category><![CDATA[CRUD]]></category>
		<category><![CDATA[Endpoints]]></category>
		<category><![CDATA[Endpunkte]]></category>
		<category><![CDATA[express.js]]></category>
		<category><![CDATA[Github]]></category>
		<category><![CDATA[Informatik]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[JSON]]></category>
		<category><![CDATA[Node]]></category>
		<category><![CDATA[Node mit Swagger und Typescript]]></category>
		<category><![CDATA[Node.js]]></category>
		<category><![CDATA[Programmierung]]></category>
		<category><![CDATA[REST]]></category>
		<category><![CDATA[Schritt für Schritt Anweisung]]></category>
		<category><![CDATA[Server]]></category>
		<category><![CDATA[Swagger]]></category>
		<category><![CDATA[Swagger dokumentation]]></category>
		<category><![CDATA[Swagger express]]></category>
		<category><![CDATA[Swagger.json]]></category>
		<category><![CDATA[typescript]]></category>
		<guid isPermaLink="false">https://nerd-corner.com/?p=1266</guid>

					<description><![CDATA[<p>Ich habe vor kurzem einen Node Server mit express.js in Typescript programmiert. Das ist eine typische Kombination für Backend Entwicklung. Das ist besonders vorteilhaft wenn &#8230; </p>
<p>The post <a href="https://nerd-corner.com/de/swagger-api-doku-zu-node-server-hinzufuegen/">Swagger Api Doku zu Node Server hinzufügen</a> appeared first on <a href="https://nerd-corner.com/de">Nerd Corner</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Ich habe vor kurzem einen Node Server mit express.js in Typescript programmiert. Das ist eine typische Kombination für Backend Entwicklung. Das ist besonders vorteilhaft wenn bereits Erfahrung mit Frontend Entwicklung in Typescript vorhanden ist. Generell würde ich sowohl im Frontend als auch im Backend empfehlen lieber Typescript anstelle von Javascript zu nutzen. Typescript nutzt Javascript ist aber typbasiert und daher nicht so fehleranfällig!</p>
<p>Auch sollte der Fokus stehts auf clean Code legen und dieser ordentlich dokumentiert sein, besonders wenn ein Projekt größer wird zahlt sich ordentlich dokumentierte Arbeit aus. Um beispielsweise REST Endpunkte zu dokumentieren empfiehlt es sich Swagger zu benutzen. Die Swagger API Dokumentation bietet eine Übersicht über alle Endpunkte und sogar die Möglichkeit mit diesen zu interagieren.</p>
<p><strong><em>Das könnte Sie auch interessieren: </em></strong><em><a href="https://nerd-corner.com/de/wie-man-eine-reactive-angular-form-svg-mit-klickbaren-elementen-erstellt/" target="_blank" rel="noopener">Angular Form SVG mit clickbaren Elementen</a></em></p>
<h2>Liste der Komponenten</h2>
<ul>
<li>Entwicklungsumgebung (z.B. VS Code)</li>
<li>Node.js</li>
</ul>
<h2>Dokumentation mit Swagger</h2>
<p>Obwohl Swagger recht bekannt ist, konnte ich keine detaillierte Anleitung für die Implementation finden. Ich möchte daher 2 Wege zur Implementierung von Swagger in einen bestehenden Node Server erklären. Die erste Möglichkeit besteht darin direkt zu jedem Endpunkt Swagger Parameter hinzuzufügen. Das ist wohl die schnellere Variante, kann allerdings abhängig von der Anzahl der Endpunkte unübersichtlich werden. In der zweiten Variante wird eine „swagger.json“ Datei erstellt, die die  Parameter der Endpunkte zusammenfasst.</p>
<p>Um Swagger nutzen zu können wird folgende Bibliothek bzw. deren Typerweiterung genutzt:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">npm install --save  swagger-ui-express
npm install --save-dev @types/swagger-ui-express</pre>
<p>Swagger erstellt eine Dokumentation der Endpunkte:</p>
<p><img loading="lazy" decoding="async" class="zoooom aligncenter wp-image-1271" src="https://nerd-corner.com/wp-content/uploads/2022/10/exampleSwaggerDocu.jpg" alt="Example Swagger Docu" width="1647" height="853" srcset="https://nerd-corner.com/wp-content/uploads/2022/10/exampleSwaggerDocu.jpg 1847w, https://nerd-corner.com/wp-content/uploads/2022/10/exampleSwaggerDocu-300x155.jpg 300w, https://nerd-corner.com/wp-content/uploads/2022/10/exampleSwaggerDocu-1024x531.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2022/10/exampleSwaggerDocu-768x398.jpg 768w, https://nerd-corner.com/wp-content/uploads/2022/10/exampleSwaggerDocu-1536x796.jpg 1536w" sizes="auto, (max-width: 1647px) 100vw, 1647px" /></p>
<p>Diese können geöffnet werden und man sieht einen beispielhaften Request und einen beispielhaften Response:</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1273 zoooom" style="font-size: 1.125rem;" src="https://nerd-corner.com/wp-content/uploads/2022/10/exampleSwaggerEndpoint.jpg" alt="Example Swagger Endpoint 1" width="1657" height="840" srcset="https://nerd-corner.com/wp-content/uploads/2022/10/exampleSwaggerEndpoint.jpg 1757w, https://nerd-corner.com/wp-content/uploads/2022/10/exampleSwaggerEndpoint-300x152.jpg 300w, https://nerd-corner.com/wp-content/uploads/2022/10/exampleSwaggerEndpoint-1024x519.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2022/10/exampleSwaggerEndpoint-768x389.jpg 768w, https://nerd-corner.com/wp-content/uploads/2022/10/exampleSwaggerEndpoint-1536x779.jpg 1536w" sizes="auto, (max-width: 1657px) 100vw, 1657px" /><img loading="lazy" decoding="async" class="aligncenter wp-image-1272 zoooom" src="https://nerd-corner.com/wp-content/uploads/2022/10/exampleSwaggerEndpoint2.png" alt="Example Swagger Endpoint 2" width="1657" height="803" srcset="https://nerd-corner.com/wp-content/uploads/2022/10/exampleSwaggerEndpoint2.png 1757w, https://nerd-corner.com/wp-content/uploads/2022/10/exampleSwaggerEndpoint2-300x145.png 300w, https://nerd-corner.com/wp-content/uploads/2022/10/exampleSwaggerEndpoint2-1024x496.png 1024w, https://nerd-corner.com/wp-content/uploads/2022/10/exampleSwaggerEndpoint2-768x372.png 768w, https://nerd-corner.com/wp-content/uploads/2022/10/exampleSwaggerEndpoint2-1536x744.png 1536w" sizes="auto, (max-width: 1657px) 100vw, 1657px" /></p>
<p>Über den Button &#8222;Try it out&#8220; kann direkt mit dem Endpunkt interagiert werden.</p>
<h2>Swagger API Dokumentation mit Parametern</h2>
<p>Diese Variante empfiehlt sich eher für kleinere Projekte. Zunächst müssen diese beiden Swagger Bibliotheken in das Projekt eingebunden werden:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">npm install --save-dev @types/swagger-jsdoc 
npm install --save swagger-jsdoc</pre>
<p>Die Bibliotheken werden in app.ts bzw. für nicht Typescript Nutzer app.js konfiguriert. Aufgrund der hier festgelegten Konfiguration befindet sich die Dokumentation unter „/api-docs“:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-title="app.ts">import express from "express";
import bodyParser from "body-parser";
import exampleRoutes from "./routes/example-route";
import swaggerJSDoc from "swagger-jsdoc";
import swaggerUi from "swagger-ui-express";

const app = express();

const options = {
  definition: {
    openapi: "3.0.1",
    info: {
      title: "REST API for Swagger Documentation",
      version: "1.0.0",
    },
    schemes: ["http", "https"],
    servers: [{ url: "http://localhost:3000/" }],
  },
  apis: [
    `${__dirname}/routes/example-route.ts`,
    "./dist/routes/example-route.js",
  ],
};

const swaggerSpec = swaggerJSDoc(options);

app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerSpec));
app.use(bodyParser.json());
app.use(exampleRoutes);

app.listen(3000);</pre>
<p>Der Endpunkt wird mit den Parametern folgendermaßen beschrieben:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-title="example-route.ts">import { Router } from "express";
import { exampleFunction } from "../controller/example";

const router = Router();

/**
 * @swagger
 * /example:
 *      post:
 *          summary: Send the text to the server
 *          tags:
 *              - ExampleEndpoints
 *          description: Send a message to the server and get a response added to the original text.
 *          requestBody:
 *              required: true
 *              content:
 *                  application/json:
 *                      schema:
 *                          type: object
 *                          properties:
 *                              responseText:
 *                                  type: string
 *                                  example: This is some example string! This is an endpoint
 *          responses:
 *              201:
 *                  description: Success
 *                  content:
 *                      application/json:
 *                          schema:
 *                              type: object
 *                              properties:
 *                                  text:
 *                                      type: string
 *                                      example: This is some example string!
 *              404:
 *                  description: Not found
 *              500:
 *                  description: Internal server error
 */
router.post("/example", exampleFunction);

export default router;
</pre>
<h2>Swagger API Dokumentation mit swagger.json</h2>
<p>Um eine bessere Übersicht zu haben und um die Dokumentation nicht mit dem eigentlichen Code zu vermischen empfiehlt sich die Nutzung einer, oder gar mehrerer json Dateien. Bei der Nutzung von TypeScript muss die Swagger.json Datei in die rootDirectory:</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1276 zoooom" src="https://nerd-corner.com/wp-content/uploads/2022/10/foulderStructureOfSwaggerJson.jpg" alt="Foulder Structure with Swagger.json" width="170" height="344" srcset="https://nerd-corner.com/wp-content/uploads/2022/10/foulderStructureOfSwaggerJson.jpg 199w, https://nerd-corner.com/wp-content/uploads/2022/10/foulderStructureOfSwaggerJson-148x300.jpg 148w" sizes="auto, (max-width: 170px) 100vw, 170px" /></p>
<p>Auch hier wirdzunächst die Konfiguration in app.ts bzw. für nicht Typescript Nutzer in app.js festgelegt. Hier wird ebenfalls der Domainpfad „/api-docs“ für die Dokumentation gewählt:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-title="app.ts">import express from "express";
import bodyParser from "body-parser";
import exampleRoutes from "./routes/example-route";
import swaggerUi from "swagger-ui-express";

import * as swaggerDocument from "./swagger.json";

const app = express();

app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerDocument));

app.use(bodyParser.json());

app.use(exampleRoutes);

app.listen(3000);</pre>
<p>Wichtiger Hinweis: Um JSON Dateien in ein Typescript Projekt zu importieren muss dies in der tsconfig.json erlaubt werden. Außerdem muss die JSON Datei sich in der root directory befinden:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="json" data-enlighter-title="tsconfig.json">{
  "compilerOptions": {
    "target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
    "module": "commonjs" /* Specify what module code is generated. */,
    "rootDir": "./src" /* Specify the root folder within your source files. */,
    "moduleResolution": "node" /* Specify how TypeScript looks up a file from a given module specifier. */,
    "resolveJsonModule": true /* Enable importing .json files. */,
    "outDir": "./dist" /* Specify an output folder for all emitted files. */,
    "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
    "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
    "strict": true /* Enable all strict type-checking options. */,
    "skipLibCheck": true /* Skip type checking all .d.ts files. */
  }
}</pre>
<p>Die vorherige Swagger Dokumentation mit Parametern würde somit in der swagger.json folgendermaßen aussehen:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="json">{
  "openapi": "3.0.1",
  "info": {
    "title": "REST API for Swagger Documentation",
    "version": "1.0.0"
  },
  "schemes": ["http"],
  "servers": [{ "url": "http://localhost:3000/" }],
  "paths": {
    "/example": {
      "post": {
        "tags": ["ExampleEndpoints"],
        "summary": "Send a text to the server",
        "description": "Send a message to the server and get a response added to the original text.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ExampleSchemaHeader"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExampleSchemaBody"
                }
              }
            }
          },
          "404": { "description": "Not found" },
          "500": { "description": "Internal server error" }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "ExampleSchemaBody": {
        "properties": {
          "responseText": {
            "type": "string",
            "example": "This is some example string! This is an endpoint"
          }
        }
      },
      "ExampleSchemaHeader": {
        "required": ["text"],
        "properties": {
          "text": {
            "type": "string",
            "example": "This is some example string!"
          }
        }
      }
    }
  }
}
</pre>
<h2>Dateien zum Herunterladen</h2>
<ul>
<li><a href="https://github.com/hanneslim/Node-with-swagger-params" target="_blank" rel="noopener">Github Beispielprojekt für swagger Parameter</a></li>
<li><a href="https://github.com/hanneslim/node-with-swagger-json" target="_blank" rel="noopener">Github Beispielprojekt für swagger.json</a></li>
</ul>
<p>The post <a href="https://nerd-corner.com/de/swagger-api-doku-zu-node-server-hinzufuegen/">Swagger Api Doku zu Node Server hinzufügen</a> appeared first on <a href="https://nerd-corner.com/de">Nerd Corner</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://nerd-corner.com/de/swagger-api-doku-zu-node-server-hinzufuegen/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Wie man eine Angular Fassade mit PUSH Architektur programmiert</title>
		<link>https://nerd-corner.com/de/wie-man-eine-angular-fassade-mit-push-architektur-programmiert/</link>
					<comments>https://nerd-corner.com/de/wie-man-eine-angular-fassade-mit-push-architektur-programmiert/#comments</comments>
		
		<dc:creator><![CDATA[Nerds]]></dc:creator>
		<pubDate>Sat, 27 Nov 2021 11:04:22 +0000</pubDate>
				<category><![CDATA[Angular-DE]]></category>
		<category><![CDATA[Software-DE]]></category>
		<category><![CDATA[angular]]></category>
		<category><![CDATA[Angular Fassade]]></category>
		<category><![CDATA[Angular PUSH Architektur]]></category>
		<category><![CDATA[angularjs]]></category>
		<category><![CDATA[BehaviourSubject]]></category>
		<category><![CDATA[Facade]]></category>
		<category><![CDATA[Facade Pattern]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[Observable]]></category>
		<category><![CDATA[PULL]]></category>
		<category><![CDATA[PUSH]]></category>
		<category><![CDATA[PUSH Architektur]]></category>
		<category><![CDATA[rxjs]]></category>
		<category><![CDATA[rxjs observables]]></category>
		<category><![CDATA[typescript]]></category>
		<guid isPermaLink="false">https://nerd-corner.com/?p=1100</guid>

					<description><![CDATA[<p>Typischerweise wird in der Softwareentwicklung eine PULL basierte Architektur verwendet und nur selten eine PUSH Architektur. Um Daten von einem Server anzufragen wird ein Service &#8230; </p>
<p>The post <a href="https://nerd-corner.com/de/wie-man-eine-angular-fassade-mit-push-architektur-programmiert/">Wie man eine Angular Fassade mit PUSH Architektur programmiert</a> appeared first on <a href="https://nerd-corner.com/de">Nerd Corner</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Typischerweise wird in der Softwareentwicklung eine PULL basierte Architektur verwendet und nur selten eine PUSH Architektur. Um Daten von einem Server anzufragen wird ein Service aufgerufen, welcher anschließend einen Request an den Server schickt und die benötigten Daten erhält. Während dieses Prozesses ist der Main UI Thread aber blockiert.</p>
<p>Angular selbst überprüft regelmäßig das View Model auf Änderungen. Sollte sich aufgrund einer State Änderung das View Model verändern, rendert Angular die View neu, weil Angular automatisch das UI anpasst, wenn sich das Model verändert. Durch diese kontinuierlichen Beobachtungen wird die ganze Anwendung langsamer.</p>
<p>Zudem steigt mit dem traditionellen Pull Ansatz die Komplexität. Besonders, wenn mehrere Views dieselben Daten benötigen. Was passiert, wenn sich die Daten ändern? Wie werden die Views darüber informiert, dass neue Daten verfügbar sind? Callback Funktionen könnten helfen, aber dann wird die Anwendung schnell unübersichtlich. Außerdem wirken sie sich besonders bei größeren Anwendungen negativ auf die Handhabung und Wartung aus. Schließlich müssen die Objekte einzeln initialisiert werden und die Abhängigkeiten bzw. die Reihenfolge der Methoden beachtet werden.</p>
<p>Um dieses Problem zu umgehen kann eine Push basierte Architektur genutzt werden. Angular bietet hier mit RxJS und dem Fassaden Entwurfsmuster eine hervorragende Möglichkeit zur Implementierung! Die Implementierung wird anhand eines Angular Beispiels in diesem Blog Artikel erläutert und ist für alle Plattformen und Frameworks (Angular, React, Vue.js, usw.) übertragbar.</p>
<h2>Liste der Komponenten:</h2>
<ul>
<li>Entwicklungsumgebung</li>
<li>Angular CLI</li>
</ul>
<p>&nbsp;</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1110 zoooom" src="https://nerd-corner.com/wp-content/uploads/2021/11/RandomBeerPUSH.png" alt="Push based Architecture Facade Service example" width="650" height="443" srcset="https://nerd-corner.com/wp-content/uploads/2021/11/RandomBeerPUSH.png 659w, https://nerd-corner.com/wp-content/uploads/2021/11/RandomBeerPUSH-300x204.png 300w" sizes="auto, (max-width: 650px) 100vw, 650px" /></p>
<h2>Was ist eine Fassade eigentlich?</h2>
<p>Das Fassaden Entwurfsmuster ist ein zentrales Interface, welches mit verschiedenen Schnittstellen eines oder mehrerer untergeordneter Systeme kommuniziert. Es kann je nach Bedarf zusätzliche Funktionen sowohl vor als auch nach einer Client Anfrage ausführen.</p>
<p>Das Fassaden Objekt bietet eine einheitliche und meist vereinfachte Schnittstelle zu einer Menge von Schnittstellen eines Subsystems.</p>
<p>Es sorgt als Vermittler dafür, dass die Kommunikation bzw. der Zugriff auf die einzelnen Komponenten eines Subsystems vereinfacht und damit auch die direkte Abhängigkeit von diesen Komponenten minimiert wird. Es delegiert die Clientaufrufe so, dass Clients weder die Klassen noch ihre Beziehungen und Abhängigkeiten kennen müssen.</p>
<p>Das ist besonders in großen Systemen hilfreich, wenn ein Subsystem viele technisch orientierte Klassen enthält, die selten von außen verwendet werden. Die Verwendung einer Fassade senkt die Komplexität, da mehrere Schnittstellen zu einer zusammengefasst werden. Außerdem kann das Subsystem durch die lose Kopplung leichter erweitert werden.</p>
<h2>Die PUSH Architektur</h2>
<p>Für eine Push basierte Architektur können fortgeschrittene Entwurfsmuster wie Redux oder NgRx verwendet werden. Allerdings kann auch eine sehr elegante und performante push basierte Lösung mit RxJS erreicht werden.</p>
<p>Mit Hilfe der lang lebigen RxJS Observable streams können Veränderungen der Daten an alle Subscriber gepusht werden. Dazu subscriben die Views dem gewünschten Daten Stream. Verändern sich dann die Daten, werden die Änderungen über den Stream direkt an alle Subscriber gepusht ohne das der UI Thread geblockt wird.</p>
<p>Auf diese Art und Weise wird ein direkter Datenzugang verhindert und die Daten sind read-only. Die eigentliche Datenquelle wird ähnlich wie bei einer API angesprochen, die von den Views verwendet wird. Als zentrale Schnittstelle dient hier die eingangs beschriebene Fassade. Diese besteht aus Streams, die Daten liefern, wenn sich die Daten ändern und Methoden, um Änderungen an den Daten anzufordern oder bestimmte benutzerdefinierte Streams anzufordern.</p>
<p>Die eigentlichen Rohdaten sind erst verfügbar, nachdem sie durch den/die Stream(s) gepusht worden sind. Diese Abschirmung zentralisiert die gesamte Logik und zwingt die Views passiv auf die eingehenden Daten zu reagieren. Mit den Push basierten Diensten werden Angular View Komponenten hoch performant und nutzen dabei sowohl ChangeDetectionStrategy.OnPush, als auch die async Pipe für die gelieferten Stream Daten.</p>
<p>Daher ist das System lazy loading. Das UI-Framework braucht nicht nach Zustandsänderungen des View-Modells suchen und wartet stattdessen lazy darauf, dass ein neuer Zustand gepusht wird.</p>
<h2>Praxisbeispiel</h2>
<p>Als Beispiel für eine Push basierte Architektur dient eine einfache Anwendung, welche über den HTTP Request <a href="https://random-data-api.com/api/beer/random_beer?size=1">https://random-data-api.com/api/beer/random_beer?size=1</a> eine zufällige Bier Sorte erhält. Eine View zeigt die Biersorte an. Über ein kleines Input Feld kann die Anzahl der anzuzeigenden Biere variiert werden. Eine Fassade als zentrales Element pusht automatisiert die neuen Biersorten an die View, sobald diese verfügbar sind.</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1120 zoooom" src="https://nerd-corner.com/wp-content/uploads/2021/11/facade_explanation-1.png" alt="PUSH vs PULL based architecture facade design Angular" width="1275" height="717" srcset="https://nerd-corner.com/wp-content/uploads/2021/11/facade_explanation-1.png 1280w, https://nerd-corner.com/wp-content/uploads/2021/11/facade_explanation-1-300x169.png 300w, https://nerd-corner.com/wp-content/uploads/2021/11/facade_explanation-1-1024x576.png 1024w, https://nerd-corner.com/wp-content/uploads/2021/11/facade_explanation-1-768x432.png 768w" sizes="auto, (max-width: 1275px) 100vw, 1275px" /></p>
<p>&nbsp;</p>
<p>Als Vorbild für das Entwurfsmuster dient der Artikel von Thomas Burleson: <a href="https://thomasburlesonia.medium.com/push-based-architectures-with-rxjs-81b327d7c32d">https://thomasburlesonia.medium.com/push-based-architectures-with-rxjs-81b327d7c32d</a></p>
<h3>Als erstes muss das State Management aufgesetzt werden:</h3>
<pre class="EnlighterJSRAW" data-enlighter-language="js" data-enlighter-theme="atomic">export interface Beer {
  brand: string;
  name: string;
  style: string;
  hop: string;
  alcohol: string;
}

export interface BeerState {
  beerArray: Beer[];
  size: number;
}</pre>
<h3>Als nächstes initialisieren wir die Werte des Statemanagements der Fassade:</h3>
<pre class="EnlighterJSRAW" data-enlighter-language="js" data-enlighter-theme="atomic">export interface BeerState {
  beerArray: Beer[];
  size: number;
}

let _state: BeerState = {
  beerArray: [],
  size: 4
};</pre>
<h3>Die Fassade nutzt RxJS Streams um Daten direkt an die Views zu pushen. Diese Streams können auch automatisiert bei einer Änderung der States einen Rest API call ausführen:</h3>
<pre class="EnlighterJSRAW" data-enlighter-language="js" data-enlighter-theme="atomic">combineLatest(this.size$)
      .pipe(
        //switchMap: Maps values to observable. Cancels the previous inner observable.
        switchMap(([size]) =&gt; {
          return this.findBeerArray(size);
        })
      )
      .subscribe((beerArray) =&gt; {
        this.updateState({ ..._state, beerArray });
      });</pre>
<h3>Die Streams werden so aufgebaut, dass sie dauerhaft erhalten bleiben und nur bei einer Datenänderung aktiv werden:</h3>
<pre class="EnlighterJSRAW" data-enlighter-language="js" data-enlighter-theme="atomic">export class BeerFacade {

  beerArray$ = this.state$.pipe(
    map((state) =&gt; state.beerArray),
    distinctUntilChanged()
  );

  size$ = this.state$.pipe(
    map((state) =&gt; state.size),
    distinctUntilChanged()
  );

  private updateState(state: BeerState) {
    this.store.next((_state = state));
  }
}
</pre>
<h3>Für die bessere Handhabung werden die Streams zu einem Einzigen zusammengefasst:</h3>
<pre class="EnlighterJSRAW" data-enlighter-language="js" data-enlighter-theme="atomic">vm$: Observable&lt;BeerState&gt; = combineLatest(this.beerArray$, this.size$).pipe(
    map(([beerArray, size]) =&gt; {
      return { beerArray, size };
    })
  );</pre>
<h5>Der HTTP Request kann in einen seperaten Data Service ausgelagert werden. Da es sich hierbei aber nur um ein kleines Beispielprogramm handelt, wird der Restcall innerhalb der Facade implementiert und ausgeführt:</h5>
<pre class="EnlighterJSRAW" data-enlighter-language="js" data-enlighter-theme="atomic">/** RandomBeer REST call */
  private findBeerArray(size: number): Observable&lt;Beer[]&gt; {
    const url = `https://random-data-api.com/api/beer/random_beer?size=${size}`;
    return this.http.get&lt;Beer[]&gt;(url);
  }</pre>
<h3>Die Fassade und der View Model Stream können nun einfach initialisiert und genutzt werden. Es ist unglaublich wenig Code notwendig:</h3>
<pre class="EnlighterJSRAW" data-enlighter-language="js" data-enlighter-theme="atomic">export class AppComponent {
  vm$: Observable&lt;BeerState&gt; = this.facadeService.vm$;

  //facadeService is public for direct usage in html
  constructor(public facadeService: RandomBeerFacadeService) {} 
  title = 'RandomBeerApp';
}</pre>
<h3>Die Daten des View Model Streams werden mit folgendem Codeschnippsel angezeigt:</h3>
<pre class="EnlighterJSRAW" data-enlighter-language="js" data-enlighter-theme="atomic">&lt;div *ngIf="vm$ | async as vm"&gt;  
  &lt;ul&gt;
    &lt;li id="random-beer-list" *ngFor="let u of vm.beerArray"&gt;
      Brand: {{ u.brand }}, Alcohol: {{ u.alcohol }}, Hop: {{ u.hop }}
    &lt;/li&gt;
  &lt;/ul&gt;
  &lt;input id="input-rand-beer" type="number" (change)="facadeService.updateSize($event)" /&gt;
&lt;/div&gt;</pre>
<p>Dabei sorgt die Async Pipe von Angular dafür, dass immer die aktuellsten Daten angezeigt werden. <a href="https://angular.io/api/common/AsyncPipe" target="_blank" rel="noopener">Hier findet sich eine genaue Erklärung der Async Pipe.</a></p>
<h5>Da der Fassaden Service als public deklariert wurde, werden die Inputs direkt an die updateSize Funktion der Fassade weitergegeben:</h5>
<pre class="EnlighterJSRAW" data-enlighter-language="js" data-enlighter-theme="atomic">updateSize(selectedSize: any) {
    const size = selectedSize.target.value;
    this.updateState({ ..._state, size });
  }</pre>
<p>Als Resultat zeigt die Applikation zunächst 4 zufällige Biere an. Anschließend kann durch das Input Feld die Anzahl auf 7 erhöht werden. Dadurch wird die Funktion updateSize() aufgerufen, welche mittels updateState() den State des size$ streams aktualisiert. Als Folge dessen wird automatisiert ein neuer REST call ausgeführt und das Ergebnis an das View Model gepusht. Mit Hilfe der async Pipe werden somit die neusten Daten angezeigt. In unserem Fall die 7 zufälligen Biersorten.</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1110 zoooom" src="https://nerd-corner.com/wp-content/uploads/2021/11/RandomBeerPUSH.png" alt="Push based Architecture Facade Service example" width="650" height="443" srcset="https://nerd-corner.com/wp-content/uploads/2021/11/RandomBeerPUSH.png 659w, https://nerd-corner.com/wp-content/uploads/2021/11/RandomBeerPUSH-300x204.png 300w" sizes="auto, (max-width: 650px) 100vw, 650px" /></p>
<h3>Abschließend macht es Sinn die Funktionalität der einzelnen Fassaden Bestandteile zu testen:</h3>
<pre class="EnlighterJSRAW" data-enlighter-language="js" data-enlighter-theme="atomic">it('should get individual Observable "stream" of vm data', (done) =&gt; {
    testFacade.vm$.subscribe((vm) =&gt; {
      expect(vm.size).toEqual(initStateMock.size);
      done();
    });
  });

  it('should update state values', (done) =&gt; {
    const updatedStateMock: TestBeerState = {
      beerArray: [
        {
          brand: 'Pabst Blue Ribbon',
          name: 'Two Hearted Ale',
          style: 'Merican Ale',
          hop: 'Sorachi Ace',
          alcohol: '2,9%',
        },
        {
          brand: 'Bud Light',
          name: 'La fin Du Monde',
          style: 'Stout',
          hop: 'Bullion',
          alcohol: '2,7%',
        },
      ],
      size: 2,
    };
    testFacade['updateState'](updatedStateMock);
    testFacade.vm$.subscribe((vm) =&gt; {
      expect(vm).toEqual(updatedStateMock);
      done();
    });
  });

  it('should update the size value', (done) =&gt; {
    const newSize = 9;
    const mockEvent = {
      target: {
        value: newSize,
      },
    };
    testFacade['updateSize'](mockEvent);
    testFacade.vm$.subscribe((vm) =&gt; {
      expect(vm.size).toEqual(newSize);
      done();
    });
  });

  it('should perform a mocked http request', (done) =&gt; {
    const httpMock: HttpTestingController = TestBed.inject(
      HttpTestingController
    );

    const mockResponse = {
      brand: 'Pabst Blue Ribbon',
      name: 'Two Hearted Ale',
      style: 'Merican Ale',
      hop: 'Sorachi Ace',
      alcohol: '2,9%',
    };

    testFacade['findBeerArray'](1);
    testFacade.vm$.subscribe((tb) =&gt; {
      expect(tb.beerArray).toBeTruthy();
      expect(tb.beerArray[0].brand).toBe(mockResponse.brand);
      expect(tb.beerArray[0].name).toBe(mockResponse.name);
      expect(tb.beerArray[0].style).toBe(mockResponse.style);
      expect(tb.beerArray[0].hop).toBe(mockResponse.hop);
      expect(tb.beerArray[0].alcohol).toBe(mockResponse.alcohol);

      done();
    });

    const mockRequest = httpMock.expectOne(
      'https://random-data-api.com/api/beer/random_beer?size=1'
    );
    mockRequest.flush(mockResponse);
  });</pre>
<h2>Dateien zum Herunterladen</h2>
<ul>
<li><a href="https://github.com/hanneslim/RandomBeerApp">Source Code in GitHub</a></li>
</ul>
<p>The post <a href="https://nerd-corner.com/de/wie-man-eine-angular-fassade-mit-push-architektur-programmiert/">Wie man eine Angular Fassade mit PUSH Architektur programmiert</a> appeared first on <a href="https://nerd-corner.com/de">Nerd Corner</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://nerd-corner.com/de/wie-man-eine-angular-fassade-mit-push-architektur-programmiert/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
	</channel>
</rss>
