<?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>Software Archives - Nerd Corner</title>
	<atom:link href="https://nerd-corner.com/category/software/feed/" rel="self" type="application/rss+xml" />
	<link>https://nerd-corner.com/category/software/</link>
	<description>Craft your dreams!</description>
	<lastBuildDate>Sun, 22 Jun 2025 17:30:54 +0000</lastBuildDate>
	<language>en-US</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>Software Archives - Nerd Corner</title>
	<link>https://nerd-corner.com/category/software/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Deployment of a WebApp with Kubernetes and Caddy</title>
		<link>https://nerd-corner.com/deployment-of-a-webapp-with-kubernetes-and-caddy/</link>
					<comments>https://nerd-corner.com/deployment-of-a-webapp-with-kubernetes-and-caddy/#respond</comments>
		
		<dc:creator><![CDATA[Nerds]]></dc:creator>
		<pubDate>Sun, 16 Feb 2025 18:31:48 +0000</pubDate>
				<category><![CDATA[Angular]]></category>
		<category><![CDATA[App development]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[angular]]></category>
		<category><![CDATA[Caddy]]></category>
		<category><![CDATA[Caddy Server]]></category>
		<category><![CDATA[Cluster]]></category>
		<category><![CDATA[ClusterIP]]></category>
		<category><![CDATA[ConfigMap]]></category>
		<category><![CDATA[Container]]></category>
		<category><![CDATA[Deployment]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[Domain]]></category>
		<category><![CDATA[Domain Registration]]></category>
		<category><![CDATA[Hetzner]]></category>
		<category><![CDATA[Images]]></category>
		<category><![CDATA[Ingress]]></category>
		<category><![CDATA[Ingress Controller]]></category>
		<category><![CDATA[ip addres]]></category>
		<category><![CDATA[K3s]]></category>
		<category><![CDATA[Kubectl]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[LoadBalancer]]></category>
		<category><![CDATA[mySql]]></category>
		<category><![CDATA[Nest.js]]></category>
		<category><![CDATA[Node Port]]></category>
		<category><![CDATA[Pods]]></category>
		<category><![CDATA[Redis]]></category>
		<category><![CDATA[Reverse Proxy]]></category>
		<category><![CDATA[Server]]></category>
		<category><![CDATA[Service]]></category>
		<category><![CDATA[Step by step guide]]></category>
		<category><![CDATA[Traefik]]></category>
		<guid isPermaLink="false">https://nerd-corner.com/de/?p=1695</guid>

					<description><![CDATA[<p>After describing how to create production-ready Docker images and upload them to Docker Hub in the last article, it is now time to make these &#8230; </p>
<p>The post <a href="https://nerd-corner.com/deployment-of-a-webapp-with-kubernetes-and-caddy/">Deployment of a WebApp with Kubernetes and Caddy</a> appeared first on <a href="https://nerd-corner.com">Nerd Corner</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>After describing how to create production-ready Docker images and upload them to Docker Hub in the last article, it is now time to make these images available on a server. The aim is to make the web application accessible to everyone via a dedicated domain. To do this, we use a virtual private server (VPS) from Hetzner and deploy Kubernetes (k3s) with Caddy as a reverse proxy.</p>
<p><strong><em>You might also be interested in this: </em></strong><a href="https://nerd-corner.com/create-docker-images-and-upload-them-to-docker-hub/"><em>Create Docker images and upload them to Dockerhub</em></a></p>
<h2>Set up a VPS with Hetzner</h2>
<p>Hetzner often offers referral links with credit benefits for new customers. Of course, you can also use other providers, but Hetzner is attractively priced and offers solid services.</p>
<h3>What is a VPS?</h3>
<p>A Virtual Private Server (VPS) is a virtual server that is operated on a physical machine and acts as an independent server. It offers more control than classic shared hosting and is a cost-effective alternative to dedicated servers. Access is usually via SSH (Secure Shell), which allows us to control the server via the command line.</p>
<h3>SSH access to the VPS</h3>
<p>Once a VPS has been created, it is usually managed via a secure shell (SSH). SSH is a protocol that enables encrypted connections to remote servers. The following command is used to connect to the server:</p>
<p data-start="1137" data-end="1384"><span style="background-color: #e9ebec; color: #222222; font-family: Monaco, Consolas, 'Andale Mono', 'DejaVu Sans Mono', monospace; font-size: 15px;">ssh root@&lt;IP-Server&gt;</span></p>
<p>If an SSH key has been stored, authentication can be carried out using public key authentication, which is more secure than a password.</p>
<h3>Create a server at Hetzner</h3>
<ol>
<li>After logging into the Hetzner Cloud, we navigate to “Projects” and create a new project.</li>
<li>Select “Add server” and can configure an instance.</li>
<li>The cheapest model is often sufficient to start with. However, I recommend activating the option for an IPv4 address, as purely IPv6-based setups often cause compatibility problems.</li>
</ol>
<h3>Setting up a domain</h3>
<p>To access the application later under your own domain, you must register a domain and link it to the server.</p>
<h3>Apply for a domain at Hetzner</h3>
<ol>
<li>Register a new domain or add an existing domain in the Hetzner ConsoleH.</li>
<li>To manage DNS entries, we need to activate DNS access.</li>
</ol>
<h3>Set name servers</h3>
<p>The following name servers should be used:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">helium.ns.hetzner.de. 
hydrogen.ns.hetzner.com. 
oxygen.ns.hetzner.com.</pre>
<p>These new name servers offer better performance and flexibility compared to the old Hetzner name servers:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">ns1.first-ns.de.
robotns2.second-ns.de.
robotns3.second-ns.com.</pre>
<p>However, both nameserver variants are possible! The DNS changes take some time. However, we can use tools such as MXToolbox to check whether the changes have already taken place.</p>
<h3>Connect the domain to the server</h3>
<p>Now the IP address of the server must be linked to the domain:</p>
<ol>
<li>Switch to DNS zones in the Hetzner Cloud.</li>
<li>Select the registered domain.</li>
<li>Create a new A-Record and enter the IPv4 address of the server.</li>
<li>If available, remove the IPv6 record (AAAA) to avoid compatibility problems.</li>
</ol>
<p>You can also use <a href="http://mxtoolbox.com">MXToolbox</a> to check whether the DNS changes have already been applied.</p>
<h2>Set up Kubernetes</h2>
<p>Kubernetes is a powerful orchestration tool for containers. I use k3s, a lean Kubernetes variant that is particularly suitable for smaller environments.</p>
<h3>Install K3s on the server</h3>
<p>Connect to the server via SSH and install k3s with the following command:</p>
<pre><code class="language-sh">curl -sfL https://get.k3s.io | sh - </code></pre>
<p>The script installs k3s and starts the Kubernetes service. After installation, k3s can be checked with the following command:</p>
<pre><code class="language-sh">kubectl get nodes
</code></pre>
<p>k3s comes with its own kubectl version, so that no separate installation is necessary.</p>
<h3>Create YAML files for FE, BE, MySQL and Redis</h3>
<p>To deploy our application, we need YAML files for:</p>
<ul>
<li>Frontend (Angular)</li>
<li>Backend (NestJS)</li>
<li>Database (MySQL)</li>
<li>Session-Management (Redis)</li>
</ul>
<p>A deployment file for the backend could look like this:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
        - name: backend
          image: dockerhub-user/backend:latest
          ports:
            - containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
  name: backend-service
spec:
  selector:
    app: backend
  ports:
    - protocol: TCP
      port: 80
      targetPort: 3000
  type: ClusterIP
</pre>
<h3>What are deployments and services?</h3>
<ul>
<li>Deployments manage the provision and scaling of containers.</li>
<li>Services ensure a stable network connection between containers.</li>
<li>ClusterIP means that the service is only accessible within the Kubernetes cluster.</li>
</ul>
<h3>Set up Caddy as a reverse proxy</h3>
<p>A reverse proxy is required to ensure that incoming traffic is distributed correctly. K3s comes with Traefik by default, but I opted for a simpler solution: Caddy. I was really surprised how little guidance or documentation there is on Caddy in combination with Kubernetes.</p>
<h3>Why Kubernetes with Caddy?</h3>
<ul>
<li>Automatic Let&#8217;s Encrypt SSL certificates</li>
<li>Simple configuration via Caddyfile</li>
<li>Built-in load balancer</li>
</ul>
<h3>Remove Traefik</h3>
<pre><code class="language-sh">kubectl delete helmrelease traefik -n kube-system
</code></pre>
<h3>Create Caddy Deployment</h3>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">apiVersion: apps/v1
kind: Deployment
metadata:
  name: caddy
spec:
  replicas: 1
  selector:
    matchLabels:
      app: caddy
  template:
    metadata:
      labels:
        app: caddy
    spec:
      containers:
        - name: caddy
          image: caddy
          volumeMounts:
            - name: caddy-config
              mountPath: /etc/caddy/Caddyfile
      volumes:
        - name: caddy-config
          configMap:
            name: caddy-config
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: caddy-config
data:
  Caddyfile: |

    example.com {
        reverse_proxy backend-service:3000
    }

---
apiVersion: v1
kind: Service
metadata:
  name: caddy-service
spec:
  type: LoadBalancer
  selector:
    app: caddy
  ports:
    - port: 80
      targetPort: 80
    - port: 443
      targetPort: 443</pre>
<p>Important: Since Let&#8217;s Encrypt has a rate limit, tests should first be carried out with staging certificates!</p>
<h2>Conclusion</h2>
<p>After these steps, the application is now running in a Kubernetes cluster on a Hetzner VPS and can be accessed via its own domain. The next step would be to set up an automatic CI/CD pipeline to deploy new versions without manual effort.</p>
<p>The post <a href="https://nerd-corner.com/deployment-of-a-webapp-with-kubernetes-and-caddy/">Deployment of a WebApp with Kubernetes and Caddy</a> appeared first on <a href="https://nerd-corner.com">Nerd Corner</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://nerd-corner.com/deployment-of-a-webapp-with-kubernetes-and-caddy/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Create Docker images and upload them to Docker Hub</title>
		<link>https://nerd-corner.com/create-docker-images-and-upload-them-to-docker-hub/</link>
					<comments>https://nerd-corner.com/create-docker-images-and-upload-them-to-docker-hub/#respond</comments>
		
		<dc:creator><![CDATA[Nerds]]></dc:creator>
		<pubDate>Thu, 16 Jan 2025 18:16:16 +0000</pubDate>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[angular]]></category>
		<category><![CDATA[Backend]]></category>
		<category><![CDATA[Backend Server]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[Docker Hub]]></category>
		<category><![CDATA[Docker Images]]></category>
		<category><![CDATA[Docker Repository]]></category>
		<category><![CDATA[frontend]]></category>
		<category><![CDATA[google cloud mysql]]></category>
		<category><![CDATA[guide]]></category>
		<category><![CDATA[Images]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[mySql]]></category>
		<category><![CDATA[Nest]]></category>
		<category><![CDATA[Nest.js]]></category>
		<category><![CDATA[Redis]]></category>
		<category><![CDATA[redis session management]]></category>
		<category><![CDATA[redis session management nest.js]]></category>
		<category><![CDATA[Repository]]></category>
		<category><![CDATA[Step by step guide]]></category>
		<guid isPermaLink="false">https://nerd-corner.com/de/?p=1691</guid>

					<description><![CDATA[<p>In this article, I will show you how to create production-ready Docker images for a web application with Angular, NestJS, MySQL and Redis and then &#8230; </p>
<p>The post <a href="https://nerd-corner.com/create-docker-images-and-upload-them-to-docker-hub/">Create Docker images and upload them to Docker Hub</a> appeared first on <a href="https://nerd-corner.com">Nerd Corner</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>In this article, I will show you how to create production-ready Docker images for a web application with Angular, NestJS, MySQL and Redis and then publish them on Docker Hub. The prerequisite is an installed Docker environment.</p>
<p><em><strong>You might also be interested in this: </strong><a href="https://nerd-corner.com/lessons-learned-hosting-nestjs-app-on-vercel/">Hosting NestJS on Vercel</a></em></p>
<h2>Creation of the Docker Compose Yml</h2>
<p>With Docker Compose, all components of an application can be defined via a single configuration file and built or started together.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">version: '3.8'

services:
  frontend:
    build: ./frontend
    ports:
      - "80:80"
    depends_on:
      - backend

  backend:
    build: ./backend
    ports:
      - "3000:3000"
    depends_on:
      - mysql
      - redis
    environment:
      - DATABASE_URL=mysql://user:password@mysql:3306/db
      - SESSION_STORE=redis://redis:6379

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: db
    ports:
      - "3306:3306"

  redis:
    image: redis:latest
    ports:
      - "6379:6379"</pre>
<h2>Creating the env file</h2>
<p>To manage environment variables centrally, we create an .env file:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">DATABASE_URL=mysql://user:password@mysql:3306/db 
SESSION_STORE=redis://redis:6379</pre>
<p>Important: All ENV variables used in the code must also appear in docker-compose.yml!</p>
<h2>Docker image for the frontend</h2>
<p>The Angular frontend must be built for production. Here is an example Dockerfile:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">FROM node:20 AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install
COPY . .
RUN npm run build --prod

FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf</pre>
<p>Since we are dependent on nginx for the build, we also need a corresponding config file:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">server {
  listen 80;
  server_name _;

  location / {
    root /usr/share/nginx/html;
    index index.html;
    try_files $uri $uri/ /index.html;
  }
}</pre>
<h2>Docker image for the backend</h2>
<p>The NestJS backend also needs to be built. Here is an optimized Dockerfile.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic"># Build stage
FROM node:20 AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install
COPY . .
RUN npm run build

# Production stage
FROM node:20-alpine
WORKDIR /app

COPY --from=build /app/dist ./dist
COPY package.json package-lock.json ./
RUN npm install --only=production
CMD ["node", "dist/main.js"]</pre>
<h2>Building the app with Docker Compose</h2>
<p>Once all the Dockerfiles have been configured, the images can now be built. The whole thing is really easy with Docker compose.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">docker compose up -d --build</pre>
<p>The images are then ready, the containers are built and the app can be tested locally! The last step is to upload the images to DockerHub so that they can be used more easily later for deployment on a server.</p>
<h2>Uploading to Docker Hub</h2>
<p>Uploading is explained step by step below:</p>
<ol>
<li>Create an account on <a href="https://www.docker.com/products/docker-hub/">Dockerhub</a></li>
<li>Create a repository for the frontend and backend (1 private repo is currently free)</li>
<li>Build the images and tag them:
<pre class="EnlighterJSRAW" data-enlighter-language="generic">docker tag &lt;image-id&gt; dockerAccountName/frontend:latest
docker tag &lt;image-id&gt; dockerAccountName/backend:latest</pre>
</li>
<li>Sign up and push the images:
<pre class="EnlighterJSRAW" data-enlighter-language="generic">docker login
docker push dockerAccountName/frontend:latest
docker push dockerAccountName/backend:latest</pre>
</li>
</ol>
<h2>Outlook: Deployment with Kubernetes</h2>
<p>Now that the images are on Docker Hub, nothing stands in the way of deployment. I have opted for a Kubernetes cluster on a Hetzner VPS. <a href="https://nerd-corner.com/deployment-of-a-webapp-with-kubernetes-and-caddy/">More information here</a>.</p>
<p>The post <a href="https://nerd-corner.com/create-docker-images-and-upload-them-to-docker-hub/">Create Docker images and upload them to Docker Hub</a> appeared first on <a href="https://nerd-corner.com">Nerd Corner</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://nerd-corner.com/create-docker-images-and-upload-them-to-docker-hub/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>DIY keypad lock &#8211; 3D printing and code</title>
		<link>https://nerd-corner.com/diy-keypad-lock-3d-printing-and-code/</link>
					<comments>https://nerd-corner.com/diy-keypad-lock-3d-printing-and-code/#respond</comments>
		
		<dc:creator><![CDATA[Nerds]]></dc:creator>
		<pubDate>Tue, 17 Dec 2024 20:44:05 +0000</pubDate>
				<category><![CDATA[Arduino projects]]></category>
		<category><![CDATA[Handcrafted]]></category>
		<category><![CDATA[Hardware]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[SolidWorks]]></category>
		<category><![CDATA[3D print]]></category>
		<category><![CDATA[Arduino]]></category>
		<category><![CDATA[barrel jack]]></category>
		<category><![CDATA[CAD]]></category>
		<category><![CDATA[CAD design]]></category>
		<category><![CDATA[case]]></category>
		<category><![CDATA[Design]]></category>
		<category><![CDATA[DIY]]></category>
		<category><![CDATA[housing]]></category>
		<category><![CDATA[jumper cable]]></category>
		<category><![CDATA[keypad]]></category>
		<category><![CDATA[led]]></category>
		<category><![CDATA[lock]]></category>
		<category><![CDATA[precise]]></category>
		<category><![CDATA[Solid Works]]></category>
		<category><![CDATA[user guide]]></category>
		<guid isPermaLink="false">https://nerd-corner.com/de/?p=1677</guid>

					<description><![CDATA[<p>A cupboard that should always be locked and five people who need to access it &#8211; a classic challenge. The obvious solutions? Five keys in &#8230; </p>
<p>The post <a href="https://nerd-corner.com/diy-keypad-lock-3d-printing-and-code/">DIY keypad lock &#8211; 3D printing and code</a> appeared first on <a href="https://nerd-corner.com">Nerd Corner</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>A cupboard that should always be locked and five people who need to access it &#8211; a classic challenge. The obvious solutions? Five keys in circulation or a single person who manages the key so that you have to borrow it every time. But we&#8217;ve all been there: the key ends up lying in the cupboard, under the carpet or behind the flower pot.</p>
<p>But wouldn&#8217;t it be much more practical to do away with physical keys altogether? Nowadays, there are numerous ways to open a lock: Fingerprint scanners, facial recognition, NFC, numeric codes, dials &#8211; or, of course, brute force methods such as explosives and brute force. But if you are looking for an inexpensive, non-violent and simple solution, the keypad lock comes into focus.</p>
<p>Surprisingly, there are hardly any really good DIY solutions for hobbyists on the Internet. So I tackle it myself &#8211; my first keypad lock, which I simply call “Version 1”.</p>
<p><em><strong>This might also be interesting for you: </strong><a href="https://nerd-corner.com/diy-powerbank-with-voltage-regulator-and-voltmeter/">Do it yourself powerbank with voltage regulator and voltmeter</a></em></p>
<h2>Construction of the housing</h2>
<p>The initial focus is on the housing and the keypad holder. The first question that always arises is: How big should it be? The answer depends on several factors:</p>
<ul>
<li>Which components are required? Each component takes up space and influences the design.</li>
<li>How much space do the components take up? A compact design is advantageous, but must not restrict functionality.</li>
<li>What are the haptics and operability like? The keypad should be comfortable to use without being too cramped or impractical.</li>
</ul>
<p>These considerations form the basis for the housing design &#8211; because good planning saves time and nerves later on.</p>
<h3>What will be inside the housing?</h3>
<p>The central component is, of course, the membrane keypad (1.0.1). It has the following dimensions:</p>
<ul>
<li>Width: 69 mm</li>
<li>Length: 76 mm</li>
<li>Thickness: 0.6 mm (or 0.95 mm above the keys)</li>
</ul>
<p>The keypad also has a ribbon cable with DuPont sockets for connection to a microcontroller. The cable itself is 85 mm long and 17.78 mm wide.</p>
<p>The control center of the lock is the Nano (1.0.2). To accommodate it neatly in the housing and to make the cable connections as convenient as possible, I opted for a Nano expansion board with screw terminals (1.0.4).</p>
<p>A hollow socket (5.5 x 2.1 mm, 1.0.4) is used for the emergency power supply so that the lock continues to function even in the event of a power failure.</p>
<p><img fetchpriority="high" decoding="async" class="aligncenter wp-image-1671 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/1-0-1.jpg" alt="tastenfeldschloss Bauteile" width="2310" height="1324" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/1-0-1.jpg 2318w, https://nerd-corner.com/wp-content/uploads/2024/12/1-0-1-300x172.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/1-0-1-1024x587.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/1-0-1-768x440.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/1-0-1-1536x881.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/1-0-1-2048x1174.jpg 2048w" sizes="(max-width: 2310px) 100vw, 2310px" /></p>
<p>The pin and socket connectors (1.1.1) serve as the central power distribution and are later soldered to the breadboard (1.1.2). Jumper cables (1.1.3) are used to ensure that all components are reliably connected. Depending on the position of the components, different lengths are required &#8211; in this case 10 cm and 20 cm.</p>
<p><img decoding="async" class="aligncenter wp-image-1670 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/1-1.jpg" alt="" width="2310" height="1327" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/1-1.jpg 2321w, https://nerd-corner.com/wp-content/uploads/2024/12/1-1-300x172.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/1-1-1024x588.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/1-1-768x441.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/1-1-1536x882.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/1-1-2048x1176.jpg 2048w" sizes="(max-width: 2310px) 100vw, 2310px" /></p>
<p>For the status display of the keypad lock, I use Neo Pixel addressable LEDs of type WS2812b (1.2.1). These can be used to control different colors and effects to visually display the current status of the lock.</p>
<p>I will go into the positioning of the screws in more detail later.</p>
<p><img decoding="async" class="aligncenter wp-image-1669 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/1-2.jpg" alt="" width="2320" height="1324" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/1-2.jpg 2331w, https://nerd-corner.com/wp-content/uploads/2024/12/1-2-300x171.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/1-2-1024x584.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/1-2-768x438.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/1-2-1536x876.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/1-2-2048x1169.jpg 2048w, https://nerd-corner.com/wp-content/uploads/2024/12/1-2-1400x800.jpg 1400w" sizes="(max-width: 2320px) 100vw, 2320px" /></p>
<p>Now that the components have been determined, I can think about the size of the housing. This is not only determined by the installed elements, but above all by the usability and feel.</p>
<p>We encounter keypads every day &#8211; on ATMs, telephones, door lock systems and, of course, smartphones. The decision for the depth of the housing is based on a positive memory of my penultimate workplace: the keypad lock at the entrance was raised and easily accessible from both sides. You could operate it comfortably with your right or left thumb, and the rounded corners provided a pleasant feel when you put your hand on it.</p>
<h2>Keypad lock Housing design</h2>
<p>This results in a depth of 45 mm (2.0.3). For better ergonomics, the corners have a radius of 15 mm (R15) and the surrounding upper edges have a radius of 10 mm (R10). I am aware that these roundings slightly reduce the interior space, but the comfort and appearance outweigh this disadvantage.</p>
<p>The width and height of the housing are determined by the components to be installed. The space for the cabling must also be taken into account. Particularly important: When installed, the Nano should still be accessible with a standard USB mini cable, for example to be able to install new programs.</p>
<h3>Mounting and fastening</h3>
<ul>
<li>Four M3 threads (2.0.1) on the inside allow the support plate to be screwed on.</li>
<li>In addition, there are four mounting points with Ø4.2 mm holes for attaching the housing to a door, cover or wall.</li>
<li>The housing has a window (2.0.2) measuring 60 × 67 mm, which is intended for the keypad. This is later filled with the carrier plate.</li>
<li>Retaining columns with M2 threads and the opening for the hollow socket (1.0.4) are marked with orange ellipses (2.0.1, 2.0.3).</li>
<li>The next picture (2.0.4) shows the external dimensions: 110 mm wide and 117 mm high.</li>
<li>In addition, an aperture or slot 50 mm long is required for the LED cover (2.0.5).</li>
</ul>
<p>For the wall thickness of the housing, I have provided 2 mm throughout &#8211; stable enough for the intended purpose.</p>
<p>With the housing construction completed, we can now continue with the other components.</p>
<h3 data-start="1513" data-end="1546"><img loading="lazy" decoding="async" class="aligncenter wp-image-1668 zoooom" style="font-family: 'Source Sans Pro', Graphik, -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif; font-size: 1.125rem;" src="https://nerd-corner.com/wp-content/uploads/2024/12/2-0-1.jpg" alt="Tastenfeldschloss Gehäuse" width="2310" height="1330" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/2-0-1.jpg 2316w, https://nerd-corner.com/wp-content/uploads/2024/12/2-0-1-300x173.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/2-0-1-1024x589.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/2-0-1-768x442.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/2-0-1-1536x884.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/2-0-1-2048x1179.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></h3>
<h3>The support plate &#8211; the central mounting element</h3>
<p>The next important component is the carrier plate. The name is self-explanatory: With the exception of the hollow bushing, all components are attached here. This system offers several advantages over direct mounting in the housing:</p>
<p><img src="https://s.w.org/images/core/emoji/16.0.1/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Easy mounting outside the housing &#8211; More space and better handling when wiring.<br />
<img src="https://s.w.org/images/core/emoji/16.0.1/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Modularity &#8211; different carrier plates with different components are possible.<br />
<img src="https://s.w.org/images/core/emoji/16.0.1/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Simple enclosure design &#8211; The enclosure design remains simpler and more flexible.<br />
<img src="https://s.w.org/images/core/emoji/16.0.1/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Easy replacement &#8211; Components can be replaced or extended more easily.</p>
<p>But there is another decisive advantage for the keypad lock in particular:</p>
<p>The keypad is glued directly into a designated recess in the carrier plate (2.1.1). The carrier plate and keypad are then inserted into the housing from behind and fixed in place with M3 screws. The frame of the window in the housing completely covers the edge and the cables.</p>
<p><img src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f512.png" alt="🔒" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Safety aspect: The window is dimensioned in such a way that the edge and the cables remain concealed, but the buttons are fully visible and operable. This means that the keypad cannot be removed without destroying it &#8211; an important protective mechanism against tampering.</p>
<h3>Fastening the components to the carrier plate</h3>
<p>There are various mounting options for the electronics on the back (2.1.2) of the carrier plate:</p>
<p><img src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f7e1.png" alt="🟡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> M3 thread for the nano adapter (2.1.3) &#8211; As nano adapters on the Internet often have different hole spacings, there is an additional fastening thread on the right-hand side (2.1.2) for flexible adjustment. If the holes still do not fit exactly, they can be carefully widened &#8211; but without damaging the adapter&#8217;s conductor tracks.</p>
<p><img src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f534.png" alt="🔴" class="wp-smiley" style="height: 1em; max-height: 1em;" /> M2 thread for the Nano R3 ATMEGA168P (2.1.4) &#8211; An alternative, cost-effective solution instead of a Nano R3 with adapter.</p>
<p><img src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f535.png" alt="🔵" class="wp-smiley" style="height: 1em; max-height: 1em;" /> M2 thread for the breadboard (20×80 mm, 1.1.2, 2.1.5) &#8211; This is used for power distribution and connects all power supply lines neatly at a central point.</p>
<h3 data-start="1487" data-end="1545"><img loading="lazy" decoding="async" class="aligncenter wp-image-1667 zoooom" style="font-family: 'Source Sans Pro', Graphik, -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif; font-size: 1.125rem;" src="https://nerd-corner.com/wp-content/uploads/2024/12/2-1-1.jpg" alt="" width="2310" height="1329" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/2-1-1.jpg 2317w, https://nerd-corner.com/wp-content/uploads/2024/12/2-1-1-300x173.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/2-1-1-1024x589.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/2-1-1-768x442.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/2-1-1-1536x884.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/2-1-1-2048x1178.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></h3>
<h3>The LED cover and its attachment</h3>
<p>The LED cover (2.2.1) has been designed so that it is clicked into the slot (2.0.5) of the housing from the rear. The radius on the outside of the LED cover corresponds to the housing radius, creating a smooth transition and allowing the cover to blend in seamlessly.</p>
<p>I don&#8217;t need to redesign the mounting bridge (front 2.2.2, back 2.2.3) as I have already used it successfully in other projects.</p>
<h3>Mounting the LEDs</h3>
<p>Now it remains to mount the three WS2812b LEDs (1.2.1). I will explain why exactly three LEDs are needed later in the programming section of this article.</p>
<p>The development of the LED holder (SMD50, 2.2.4) was more complex than expected. Of course, you could simply glue, clamp or hot glue the LED strips &#8211; but that seemed too unprofessional to me.</p>
<p>I therefore invested a lot of time and effort in designing a perfect holder. The result can be seen in picture 2.2.5.</p>
<p><img src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f4cc.png" alt="📌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Further information:</p>
<p data-start="1181" data-end="1300"><a href="https://nerd-corner.com/click-and-clamp-system-smd5050-bracket/">For details on the construction, there is a separate article on NerdCorner.</a></p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1666 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/2-2-1.jpg" alt="" width="2310" height="1330" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/2-2-1.jpg 2316w, https://nerd-corner.com/wp-content/uploads/2024/12/2-2-1-300x173.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/2-2-1-1024x589.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/2-2-1-768x442.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/2-2-1-1536x884.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/2-2-1-2048x1179.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></p>
<h2>3D printing of the components</h2>
<p>Once the design has been completed, the parts must now be printed.</p>
<p><img src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f4cc.png" alt="📌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Material selection for the individual components:</p>
<ul>
<li>Housing (3.0.1): Printed with ABS filament, consisting of front and back.</li>
<li>Carrier plate (3.0.2): made from PLA filament.</li>
<li>LED cover (3.0.3): produced upright in the printer, printed with PLA+ in the color “natural”.</li>
<li>LED terminals (3.0.4): also made of PLA+, manufactured in the same process as the LED cover.</li>
<li>Bridge for the hollow socket: printed from PLA filament, analogous to the carrier plate.</li>
</ul>
<p>With these materials, the mechanical and thermal properties of the components are optimally matched to their respective applications.</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1665 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/3-0-1.jpg" alt="" width="2310" height="1329" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/3-0-1.jpg 2317w, https://nerd-corner.com/wp-content/uploads/2024/12/3-0-1-300x173.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/3-0-1-1024x589.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/3-0-1-768x442.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/3-0-1-1536x884.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/3-0-1-2048x1178.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></p>
<h2>Post-processing of the components</h2>
<p>After printing, both the printed parts and some purchased parts need to be processed.</p>
<p>1&#x20e3; Cleaning the 3D printed parts</p>
<ul>
<li>Removal of support material and protruding print residues.</li>
</ul>
<p>2&#x20e3; Cutting the thread<br />
Housing (4.0.1):</p>
<ul>
<li>Four M3 threads (Attention: blind holes! Proceed carefully when cutting so as not to push the base outwards).</li>
<li>Two M2 threads (4.0.2).</li>
<li>Support plate (4.0.3):
<ul>
<li>The M2 threads marked in blue must be cut in any case.</li>
<li>When using a nano adapter with screw terminals (2.1.4), the M2 threads marked in red must also be cut.</li>
</ul>
</li>
</ul>
<p>3&#x20e3; Shortening the perforated grid plate</p>
<ul>
<li>The perforated grid plate (4.0.4) must be shortened to 8 to a maximum of 10 perforated grids.<br />
Important: The mounting holes should be retained (see 4.0.5).</li>
</ul>
<p>4&#x20e3; Shortening the pin header</p>
<ul>
<li>Shorten the pin header to eight pins using a side cutter (4.0.6).</li>
</ul>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1664 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/4-0-2.jpg" alt="" width="2310" height="1394" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/4-0-2.jpg 2316w, https://nerd-corner.com/wp-content/uploads/2024/12/4-0-2-300x181.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/4-0-2-1024x618.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/4-0-2-768x464.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/4-0-2-1536x927.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/4-0-2-2048x1236.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></p>
<h3>Soldering the components</h3>
<p>Now it&#8217;s time to solder the parts. First we concentrate on the power supply board:</p>
<p>1&#x20e3; Soldering the power supply board</p>
<ul>
<li>Perforated grid plate (4.0.5): Soldering the base strip (1.1.1) and the pin strip (4.0.6).</li>
<li>Pin strip (4.1.2): A two-row pin and skirting board is soldered on. We connect the two rows on the back with solder.</li>
<li>The rows differ in male and female as well as in the colors: red for plus and black for minus. This makes it easier to connect the power supply.</li>
</ul>
<p>2&#x20e3; Soldering the WS2812B LEDs</p>
<ul>
<li>Soldering the connections of the WS2812B LED strip (4.1.3).</li>
</ul>
<p>3&#x20e3; Soldering the hollow socket</p>
<ul>
<li>Finally, the hollow socket (1.0.4) is soldered (4.1.4).</li>
<li>Detailed instructions <a href="https://nerd-corner.com/using-a-barrel-jack-as-a-switch/">on soldering the hollow socket can be found</a> in a separate article. It is important to know the exact procedure in order to avoid mistakes.</li>
</ul>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1663 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/4-1-2.jpg" alt="" width="2310" height="1329" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/4-1-2.jpg 2317w, https://nerd-corner.com/wp-content/uploads/2024/12/4-1-2-300x173.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/4-1-2-1024x589.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/4-1-2-768x442.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/4-1-2-1536x884.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/4-1-2-2048x1178.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></p>
<h2 data-start="75" data-end="118">Assembling the keypad lock</h2>
<p data-start="75" data-end="118">1&#x20e3; Installing the LED cover</p>
<ul>
<li data-start="75" data-end="118">First click the LED cover (2.2.1) into the housing.</li>
<li data-start="75" data-end="118">Glue the LED cover in the intended places as shown in pictures 5.0.1 and 5.0.2.</li>
</ul>
<p>2&#x20e3; Plug in the Nano R3</p>
<ul>
<li data-start="75" data-end="118">Insert the Nano R3 (1.0.2) into the Nano adapter (5.0.3) of your choice.</li>
</ul>
<p>3&#x20e3; Installing the Nano adapter</p>
<ul>
<li data-start="75" data-end="118">Screw the Nano adapter (1.0.3) with the inserted Nano R3 (1.0.2) to the back of the carrier plate (4.0.3).</li>
<li data-start="75" data-end="118">Use the M3 threads on the carrier plate and the screws (1.2.2), as shown in pictures 5.0.4 and 5.0.5.</li>
</ul>
<p>4&#x20e3; Attaching the power supply</p>
<ul>
<li data-start="75" data-end="118">Attach the power supply (4.1.2) to the back of the carrier plate (4.0.3) using the screws (1.2.4).</li>
<li data-start="75" data-end="118">See figure 5.0.6.</li>
</ul>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1662 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/5-0-1.jpg" alt="" width="2310" height="1330" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/5-0-1.jpg 2316w, https://nerd-corner.com/wp-content/uploads/2024/12/5-0-1-300x173.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/5-0-1-1024x589.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/5-0-1-768x442.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/5-0-1-1536x884.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/5-0-1-2048x1179.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></p>
<h3 data-start="75" data-end="127">Mounting the keypad on the support plate</h3>
<p>1&#x20e3; Selecting the screw length</p>
<ul>
<li data-start="75" data-end="127">Make sure to select the correct screw length as shown in Figure 5.1.1.</li>
<li data-start="75" data-end="127">Screws must not be too long, otherwise they will protrude from the adhesive surface when the keypad is attached and the keypad cannot be glued on cleanly.</li>
</ul>
<p>2&#x20e3; Preparing the keypad</p>
<ul>
<li data-start="75" data-end="127">Remove the protective film from the keypad (1.0.1).</li>
<li data-start="75" data-end="127">To remove the film, use a carpet knife to carefully get between the adhesive layer and the protective film at one corner (see 5.1.2).</li>
<li data-start="75" data-end="127">Once you have reached the corner, peel off the entire protective film (see 5.1.3).</li>
</ul>
<p>3&#x20e3; Stick on the keypad</p>
<ul>
<li data-start="75" data-end="127">Insert the keypad (1.0.1) into the recess on the front of the carrier plate (2.1.1) and press it firmly into place.</li>
<li data-start="75" data-end="127">The keypad must be completely recessed and must not protrude over the edge (see 5.1.4).</li>
<li data-start="75" data-end="127">Make sure that the cables are also in the recess (see 5.1.5).</li>
</ul>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1661 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/5-1-1.jpg" alt="" width="2340" height="1327" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/5-1-1.jpg 2350w, https://nerd-corner.com/wp-content/uploads/2024/12/5-1-1-300x170.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/5-1-1-1024x581.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/5-1-1-768x436.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/5-1-1-1536x871.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/5-1-1-2048x1162.jpg 2048w" sizes="auto, (max-width: 2340px) 100vw, 2340px" /></p>
<h3 data-start="64" data-end="123">Wiring the keypad to the Arduino Nano R3</h3>
<p>1&#x20e3; Using jumper cables</p>
<ul>
<li data-start="64" data-end="123">Jumper cables (10 cm long, male-male) are used to connect the keypad to the Arduino Nano R3 (see 5.2.1).</li>
</ul>
<p>2&#x20e3; Connecting the cables</p>
<ul>
<li data-start="64" data-end="123">Make sure that you do not twist the cables, but only bend them.</li>
<li data-start="64" data-end="123">The left cable is connected to D2 of the Arduino and the right cable to D8.</li>
<li data-start="64" data-end="123">There are seven cables in total, which occupy the connections D2 to D8 (see 5.2.3).</li>
</ul>
<p>3&#x20e3; Use pin headers</p>
<ul>
<li data-start="64" data-end="123">As the pitch of the Arduino adapter and the keypad do not match, you can use pin strips to make the connection.</li>
</ul>
<p>4&#x20e3; Fastening the LED terminals</p>
<ul>
<li data-start="64" data-end="123">The clips (3.0.4) for the WS2812B LEDs are clicked onto the LEDs.</li>
<li data-start="64" data-end="123">It is best to do this on a flat surface (see 5.2.4).</li>
</ul>
<p>5&#x20e3; Attaching the LED strip</p>
<ul>
<li data-start="64" data-end="123">The WS2812B strip is now pushed onto the center of the top of the carrier plate (see 5.2.5 Front and 5.2.6 Rear).</li>
</ul>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1660 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/5-2.jpg" alt="" width="2310" height="1330" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/5-2.jpg 2316w, https://nerd-corner.com/wp-content/uploads/2024/12/5-2-300x173.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/5-2-1024x589.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/5-2-768x442.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/5-2-1536x884.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/5-2-2048x1179.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></p>
<h3>Assembling the carrier plate and housing</h3>
<p>1&#x20e3; Screwing the support plate to the housing (5.3.1)</p>
<ul>
<li>Make sure that you use M3 screws that are not too long. Otherwise, they could push a bump into the front of the housing when tightened.</li>
<li>The alignment of the carrier plate in the housing is important: the LED strip should be on the same side as the LED cover.</li>
<li>After screwing, the keypad should be lightly pressed against the inner frame of the housing (see 5.3.2 and 5.3.3).</li>
</ul>
<p>2&#x20e3; Pre-assembly of the hollow socket for the emergency power supply</p>
<ul>
<li>For the emergency power supply, you must pre-assemble the hollow socket and screw it to the housing.</li>
<li>The hollow socket with bridge and nut is shown in Figure 5.3.4.</li>
<li>Insert the hollow bush into the round recess of the bridge.</li>
<li>Lock the hollow bushing with the nut on the other side of the bridge.</li>
</ul>
<p>3&#x20e3; Fastening the hollow bush in the housing</p>
<ul>
<li>The hollow bush with the bridge is then screwed to the retaining pillars provided in the housing using M2 screws (see 5.3.5 and 5.3.6).</li>
</ul>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1659 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/5-3.jpg" alt="" width="2310" height="1329" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/5-3.jpg 2317w, https://nerd-corner.com/wp-content/uploads/2024/12/5-3-300x173.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/5-3-1024x589.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/5-3-768x442.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/5-3-1536x884.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/5-3-2048x1178.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></p>
<h3>Connecting the cables and completing the power supply</h3>
<p>1&#x20e3; Connecting the control cable for the servo motor</p>
<ul>
<li>The control cable for the servo motor is connected to pin D9 of the Nano R3 (5.4.1).</li>
</ul>
<p>2&#x20e3; Connecting the power supply for the Nano R3</p>
<ul>
<li>The power supply for the Nano R3 is connected to the GND pin and the VIN pin of the Nano R3 (5.4.2).</li>
</ul>
<p>3&#x20e3; Connecting the control cable for the WS2812B</p>
<ul>
<li>The control cable for the WS2812B LED strip is connected to pin D10 of the Nano R3 (5.4.3).</li>
</ul>
<p>4&#x20e3; Wiring of the power distributor</p>
<ul>
<li>All remaining cables must be connected to the power distributor:
<ul>
<li>Servo motor connection</li>
<li>Arduino Nano R3</li>
<li>WS2812B LED strip</li>
<li>Hollow socket All connections are made with plus and minus (see 5.4.4 and 5.4.5).</li>
</ul>
</li>
</ul>
<p>5&#x20e3; Using different colors and connection types for the wiring</p>
<ul>
<li>It helps to use different cable colors:
<ul>
<li>Red for plus</li>
<li>Black for minus</li>
</ul>
</li>
<li>The power distributor has different connection types:
<ul>
<li>Male for plus</li>
<li>Female for minus</li>
</ul>
</li>
</ul>
<p>6&#x20e3; Note on the hollow socket</p>
<ul>
<li><a href="https://nerd-corner.com/using-a-barrel-jack-as-a-switch/">There is a separate article on wiring and connecting the hollow socket, which you should definitely read</a></li>
</ul>
<p>7&#x20e3; Attaching the connector housings for the servo motor and power supply</p>
<ul>
<li>A three-pin plug is required for the servo motor connection.</li>
<li>A two-pin plug is required for the external power supply.</li>
</ul>
<p><a href="https://nerd-corner.com/3d-printed-dupont-connector-for-jumper-cable/">Further details on the construction process and the download links for connector housings can be found here.</a></p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1658 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/5-4.jpg" alt="" width="2310" height="1330" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/5-4.jpg 2316w, https://nerd-corner.com/wp-content/uploads/2024/12/5-4-300x173.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/5-4-1024x589.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/5-4-768x442.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/5-4-1536x884.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/5-4-2048x1179.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></p>
<p>Figure 5.5.1 shows the entire cabling again.</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1657 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/5-5.jpg" alt="fritzing keypad lock" width="2310" height="1330" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/5-5.jpg 2316w, https://nerd-corner.com/wp-content/uploads/2024/12/5-5-300x173.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/5-5-1024x589.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/5-5-768x442.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/5-5-1536x884.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/5-5-2048x1179.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></p>
<h2>Arduino code</h2>
<p>After the intensive work with the hardware and the 3D printer, we now turn our attention to the strategy and programming of the keypad lock. Why is a strategy important for a keypad lock? A well thought-out sequence of actions &#8211; i.e. the order in which the keypad lock is operated, which events result from certain actions and which goal is being pursued &#8211; forms the basis for functional control. This guideline is therefore also crucial for programming the keypad lock: it determines what should happen when and which hardware is used. The aim is to create a logical and comprehensible sequence of events, which in this case can also be understood visually.</p>
<p>Example 1:<br />
The keypad lock should display how many digits have already been entered. (See Figures 7.0.1 to 7.0.4)</p>
<p>Example 2:<br />
The keypad lock should indicate whether the password entered is correct after pressing a specific key. (See figure 7.0.5)</p>
<p>Example 3:<br />
The keypad lock should indicate if something has been entered incorrectly, such as an incorrect password or too many keystrokes. (See figure 7.0.8)</p>
<p>Example 4:<br />
The keypad lock should display the current status. (See Figures 7.0.5 to 7.0.7)</p>
<p>Now let&#8217;s move on to programming the keypad lock, which differs from conventional programs on the Internet in a few respects. To begin with, the three necessary libraries are included: &lt;Keypad.h&gt; for the keypad, &lt;Adafruit_NeoPixel.h&gt; for the WS2812B LEDs and &lt;Servo.h&gt; for the servo motor (Figure 6.0.1). In the following section, the pin assignment for the LEDs is defined, whereby pin D10 is used and the number of LEDs and the color scheme are determined. The brightness of the LEDs is also defined &#8211; this value can be adjusted depending on the location, with higher values providing more brightness (values from 0 to 255). (See figure 6.0.2) The third section is dedicated to the description of the keypad used, including the number of rows and columns and the assignment of the buttons. (See figure 6.0.3).</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1656 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/6-0-1.jpg" alt="" width="2310" height="1330" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/6-0-1.jpg 2316w, https://nerd-corner.com/wp-content/uploads/2024/12/6-0-1-300x173.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/6-0-1-1024x589.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/6-0-1-768x442.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/6-0-1-1536x884.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/6-0-1-2048x1179.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></p>
<p>In the fourth section, the servomotor is configured by defining the degree range it can cover and the speed at which it should move (see Figure 6.1.1). This is followed by the section for entering the password. Here you have the option of changing the default password 1516 to set a new four-digit password. The program will only work correctly if a four-digit code is entered. In this section, the control pin for the servo motor is also set to D9 (see Figure 6.1.2). The following section is dedicated to defining the colors for the various events.</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1655 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/6-1-1.jpg" alt="" width="2310" height="1330" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/6-1-1.jpg 2316w, https://nerd-corner.com/wp-content/uploads/2024/12/6-1-1-300x173.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/6-1-1-1024x589.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/6-1-1-768x442.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/6-1-1-1536x884.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/6-1-1-2048x1179.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></p>
<p>The last two sections explain the behavior of the keypad lock during certain actions. This description is of course only a rough overview of the program. In a future article for keypad lock version 2, we will explain the program in more detail and more comprehensively.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">//==========================================Librarys==============================================================
#include &lt;Keypad.h&gt;
#include &lt;Adafruit_NeoPixel.h&gt;
#include &lt;Servo.h&gt;
//========================================Neo-Pixel==============================================================
#define LED_PIN 10
#define LED_COUNT 3
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
int led_strength = 75; //controlls Brighttness (0 - 255)
//========================================Keypad=================================================================
const byte rows = 4;
const byte cols = 3;
char keys[rows][cols] = {
 {'1', '2', '3'},
 {'4', '5', '6'},
 {'7', '8', '9'},
 {'*', '0', '#'}
};
byte rowPins[rows] = {8, 7, 6, 5};
byte colPins[cols] = {4, 3, 2};
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, rows, cols );
//=========================================Servo============================================================
Servo lock;
int pos = 0;
int servo_angle = 180;
int servo_speed = 15;
//======================================Password===============================================================
String input;
const String password = "1516"; //Set Password
int n = 1;

 

void setup() {
 input.reserve(password.length() +2);
 strip.begin();
 strip.show();
 lock.attach(9); //motor pin
}

void loop() {
//-------------------colours------------------------------------------------------------------- 
 uint32_t blue = strip.Color(0, 0, led_strength);
 uint32_t green = strip.Color(0, led_strength, 0);
 uint32_t red = strip.Color(led_strength, 0, 0);
 uint32_t orange = strip.Color(led_strength, led_strength/2, 0);

 char key = keypad.getKey();

if (key != NO_KEY) {
//-------------------------End conditions------------------------------------------------------
 if (key == '#') {
 if (input == password) {
 //unlock
 strip.clear();
 strip.fill(green, 0, LED_COUNT);
 strip.show();
 for (pos = 0; pos &lt;= servo_angle; pos += 1) {
 lock.write(pos);
 delay(servo_speed );
 }
 while (1 == 1)
 { char key = keypad.getKey();
 strip.clear();
 strip.fill(orange, 0, LED_COUNT);
 strip.show();
 if (key == '*')
 {
 strip.clear();
 strip.fill(green, 0, LED_COUNT);
 strip.show();
 break;
 }
 }

 for (pos = servo_angle; pos &gt;= 0; pos -= 1) {
 lock.write(pos);
 delay(servo_speed );
 }
 n = 1;
 input = "";
 delay (1000);
 strip.clear();
 strip.show();
 } else {
 //wrong password
 strip.clear();
 strip.fill(red, 0, LED_COUNT);
 strip.show();
 n = 1;
 input = "";
 delay (1000);
 strip.clear();
 strip.show();
 }
 }
 else if (n == password.length() + 1) {
 //Input too long
 strip.clear();
 strip.fill(red, 0, LED_COUNT);
 strip.show();
 n = 1;
 input = "";
 delay (1000);
 strip.clear();
 strip.show();
 }
//----------------------------------Buttons------------------------------------------
 else {
 input += key;
 if (n == password.length() ) {
 strip.clear();
 strip.fill(blue, 0, LED_COUNT); 
 strip.show();
 n++;
 }
 else {

 strip.clear();
 strip.setPixelColor(n-1, blue);
 strip.show();
 n++;
 }
 }
 }

}</pre>
<h2>Function</h2>
<p>Operating the keypad lock is very intuitive. As soon as the first button is pressed, the right-hand LED lights up blue (7.0.1). When the second button is pressed, the middle LED turns blue (7.0.2). After the third button is pressed, the left-hand LED lights up blue (7.0.3). Finally, with the fourth button, all three LEDs light up blue (7.0.4). When these three LEDs light up blue, the user knows that the # button must be pressed.</p>
<p>If the # button is pressed, the password is checked. If the password is correct, all three LEDs light up green at the same time and the servomotor is activated (7.0.5). The green light remains on as long as the servomotor has not yet reached its end position (open). As soon as the servomotor reaches the end position (open), the LEDs change from green to orange (7.0.6). The orange color remains until the user presses the * button.</p>
<p>After pressing the * button, the LEDs change back to green (7.0.7) and remain in this color until the servomotor has reached the end position (closed). If this is the case, the LEDs go out and the keypad lock is ready for new entries.</p>
<p>However, if the password is incorrect after pressing the # button in step 7.0.4, all three LEDs light up red (7.0.8). The red light is also displayed if more than four buttons, apart from the # button, are pressed.</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1654 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/7-0-1.jpg" alt="keypad lock step by step guide" width="2310" height="1329" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/7-0-1.jpg 2317w, https://nerd-corner.com/wp-content/uploads/2024/12/7-0-1-300x173.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/7-0-1-1024x589.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/7-0-1-768x442.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/7-0-1-1536x884.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/7-0-1-2048x1178.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></p>
<h2>Door mounting</h2>
<p>Of course, this variant is not intended for use on an ordinary door, where you simply walk through and the door closes by itself. The reason for this is that the lock remains open until the * button is pressed. But how can you press the * button when you are on the other side of the keypad lock, behind the wall? A delay in the program could help, but who knows how long it takes to pass through the door and close it behind you? A much more sensible solution would be to implement an additional switch that is placed on the other side of the door. When pressed, this switch would lead directly to point 7.0.6 and open the lock so that the door can be opened from the inside without having to enter a password.</p>
<p>But that&#8217;s for the future. Now we come to mounting the keypad lock on a door. I have provided four holes on the back of the housing for this purpose. These holes have a recess on the inside for conventional hexagon nuts M4 DIN 934. The corresponding holes can be seen in Figure 8.0.1 (rear view) and Figure 8.0.2 (front view), whereby the housing is shown slightly transparent for better visualization. Installing the nuts is very simple: After ensuring that all support material has been removed, the nut is pressed into the recess provided from behind. Press-in nuts should not be used as they are unnecessarily expensive.</p>
<p>I have developed a drilling template for fixing to the wall or door (Fig. 8.0.3). This template contains a hole with a diameter of 4 mm at each of the four corners. The distance between the holes corresponds to the distance between the holes on the housing. The template can be easily fixed to the surface of the door, for example with adhesive tape (Fig. 8.0.4). This saves you the tedious task of marking out the drill holes and avoids errors.</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1653 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/8-0-1.jpg" alt="" width="2310" height="1330" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/8-0-1.jpg 2316w, https://nerd-corner.com/wp-content/uploads/2024/12/8-0-1-300x173.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/8-0-1-1024x589.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/8-0-1-768x442.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/8-0-1-1536x884.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/8-0-1-2048x1179.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></p>
<p>Figure 8.1.1 shows the drilled mounting holes from the front, marked by magenta-colored circles. Thanks to the drilling template, these holes are the correct distance apart. If the position slips slightly during drilling or is not exactly correct, this is not a problem. In this case, you can simply drill the mounting holes slightly larger to align the keypad lock precisely when screwing it on. In addition, two holes can be seen in yellow circles in this picture, which are unfortunately slightly broken out at the edge. These holes are intended for the servo cable and the power supply. Once the holes had been drilled, I fitted the keypad lock and fed the cables through the holes provided on the back of the door (Fig. 8.1.2).</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1652 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/8-1.jpg" alt="" width="2310" height="1330" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/8-1.jpg 2316w, https://nerd-corner.com/wp-content/uploads/2024/12/8-1-300x173.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/8-1-1024x589.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/8-1-768x442.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/8-1-1536x884.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/8-1-2048x1179.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></p>
<p>The entire assembly of the keypad lock is shown on the front side of the door in Image 8.2.1. If you are not satisfied with this mounting solution, don&#8217;t worry: As a final step, I have designed a back cover that allows the keypad lock to be mounted on a wall or frame (Image 8.2.2). To do this, simply place the housing of the keypad lock onto the back cover and secure it from behind using M4 countersunk screws (marked by magenta circles in Image 8.2.2). After that, the entire construction is mounted on the wall, for which two tabs with two holes each are available (yellow circles in Image 8.2.2).</p>
<p>It is also important to remember to add one or more openings in the housing for the servo and power supply cables. Additionally, the security risk should be considered, as an unauthorized person could potentially unscrew the keypad lock using the accessible screws.</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1651 zoooom" src="https://nerd-corner.com/wp-content/uploads/2024/12/8-2.jpg" alt="" width="2310" height="1330" srcset="https://nerd-corner.com/wp-content/uploads/2024/12/8-2.jpg 2316w, https://nerd-corner.com/wp-content/uploads/2024/12/8-2-300x173.jpg 300w, https://nerd-corner.com/wp-content/uploads/2024/12/8-2-1024x589.jpg 1024w, https://nerd-corner.com/wp-content/uploads/2024/12/8-2-768x442.jpg 768w, https://nerd-corner.com/wp-content/uploads/2024/12/8-2-1536x884.jpg 1536w, https://nerd-corner.com/wp-content/uploads/2024/12/8-2-2048x1179.jpg 2048w" sizes="auto, (max-width: 2310px) 100vw, 2310px" /></p>
<h3>Locking Mechanism</h3>
<p>If you&#8217;re still missing a real locking mechanism that goes beyond just a servo connection, you can look forward to the &#8220;Locking Unit,&#8221; which is already in development and will be featured in future versions of the keypad lock.</p>
<p>The development of the keypad lock was an exciting yet challenging task. Some key features of <strong>VERSION 1</strong> of the keypad lock are particularly noteworthy:</p>
<ul>
<li><strong>Compatibility with various Arduino Nano models and adapters</strong> – Ensures flexibility in hardware selection.</li>
<li><strong>Visual feedback</strong> – Users are directly informed about number input and the status of the keypad lock.</li>
<li><strong>Emergency power supply via barrel jack</strong> – Guarantees reliable operation in case of power failures.</li>
<li><strong>Stable software</strong> – Developed to ensure reliable functionality.</li>
<li><strong>Optimized ergonomics for both left- and right-handed users</strong> – Designed for comfortable use by all users.</li>
</ul>
<h2>Files for Download</h2>
<ul>
<li><a href="https://www.thingiverse.com/thing:6840254">Keypad lock housing</a></li>
</ul>
<p>The post <a href="https://nerd-corner.com/diy-keypad-lock-3d-printing-and-code/">DIY keypad lock &#8211; 3D printing and code</a> appeared first on <a href="https://nerd-corner.com">Nerd Corner</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://nerd-corner.com/diy-keypad-lock-3d-printing-and-code/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Lessons Learned: Hosting NestJS App on Vercel</title>
		<link>https://nerd-corner.com/lessons-learned-hosting-nestjs-app-on-vercel/</link>
					<comments>https://nerd-corner.com/lessons-learned-hosting-nestjs-app-on-vercel/#respond</comments>
		
		<dc:creator><![CDATA[Nerds]]></dc:creator>
		<pubDate>Thu, 14 Nov 2024 07:19:52 +0000</pubDate>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[angular]]></category>
		<category><![CDATA[config]]></category>
		<category><![CDATA[cookie-sessions]]></category>
		<category><![CDATA[CORS error]]></category>
		<category><![CDATA[Cors error nest]]></category>
		<category><![CDATA[database config]]></category>
		<category><![CDATA[database hosting]]></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[Hosting]]></category>
		<category><![CDATA[internal server error vercel]]></category>
		<category><![CDATA[internal server error vercel nest]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[JSON]]></category>
		<category><![CDATA[Lessons Learned]]></category>
		<category><![CDATA[mySql]]></category>
		<category><![CDATA[Nest]]></category>
		<category><![CDATA[Nest hosting issues]]></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[session management]]></category>
		<category><![CDATA[Step by step guide]]></category>
		<category><![CDATA[typescript]]></category>
		<category><![CDATA[Vercel]]></category>
		<category><![CDATA[vercel.json]]></category>
		<category><![CDATA[Visual Studio]]></category>
		<category><![CDATA[with credentials]]></category>
		<guid isPermaLink="false">https://nerd-corner.com/?p=1621</guid>

					<description><![CDATA[<p>After spending hours getting my NestJS app up and running on Vercel, I figured it was time to document what I learned—not only to save &#8230; </p>
<p>The post <a href="https://nerd-corner.com/lessons-learned-hosting-nestjs-app-on-vercel/">Lessons Learned: Hosting NestJS App on Vercel</a> appeared first on <a href="https://nerd-corner.com">Nerd Corner</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>After spending hours getting my NestJS app up and running on Vercel, I figured it was time to document what I learned—not only to save myself time in the future, but hopefully to help others avoid some of the pitfalls I ran into. Here’s a breakdown of what worked, what didn’t, and how I finally got everything running smoothly.</p>
<p><em><strong>This might also be interesting for you:</strong> <a href="https://nerd-corner.com/import-swagger-in-node-typescript-project/" target="_blank" rel="noopener">Adding Swagger to Node Server</a></em></p>
<h3>Step 1: Setting Up NestJS Vercel hosting</h3>
<p>First things first, getting the basic setup to deploy on Vercel. Vercel is awesome for serverless, but working with NestJS needed a few tweaks. The main thing is to set up a <code>vercel.json</code> configuration file, which tells Vercel exactly how to handle your app.</p>
<p>Here’s the configuration I ended up with:</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>I deployed it to Vercel and got the following error:</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>Looking in the logs, I noticed the database connection was an issue and in addition got the following log message:</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>Turned out I could ignore the second part of the error message and just focus on the database connection.</p>
<h3>Step 2: Configuring the Database</h3>
<p>For my app, I used a mysql database with multiple schemas. I tried several free offers, but they were not compatible with the multiple schemas approach. Therefore I ended up with hosting it on Google Cloud. I scaled it down to a price of 0.01$ per hour and used the 300$ newbie offer.</p>
<p>Allowing Vercel to connect required setting the IP to <code>0.0.0.0/0</code> in Google Cloud’s configuration, making the database accessible from any IP address. <strong>Important note</strong>: make sure you test locally before deploying to Vercel, or you’ll be dealing with errors like these:</p>
<h3>Step 3: Dealing with CORS</h3>
<p>CORS caused also some headaches. Make sure you allow <code>OPTIONS</code> for CORS preflight requests, as Vercel needed explicit permission for cross-origin requests. I ended up adding a lot of headers to make sure requests were allowed:</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>Step 4: Switching to <code>express-session</code> and Redis for Session Management</h3>
<p>One of the trickiest parts was getting sessions to work. I started with the <code>cookie-session</code> library, but Vercel completely ignore it. After digging into the docs and some trial and error, I switched to <code>express-session</code>, which is more popular and works nicer with Vercel’s serverless environment.</p>
<p>For some reason the import syntax has to be exactly like this:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="beyond">import session = require('express-session');</pre>
<p>I also had to configure the session middleware with <code>trust proxy</code> enabled, since Vercel proxies requests. Here’s what the final setup looked like:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="beyond">const expressApp = app.getHttpAdapter().getInstance();
expressApp.set('trust proxy', true);</pre>
<p><span style="font-size: 1.125rem;">Also s</span>etting <code>secure: true</code> and <code>sameSite: 'none'</code> was essential to ensure cookies work across HTTPS and cross-origin requests!</p>
<p>Keep in mind, with Vercel, multiple serverless instances can handle requests simultaneously, which caused session conflicts. To fix this, I connected my session storage to a Redis instance. Luckily this was super easy.</p>
<p><a href="https://redis.io/">Redis</a> keeps session data consistent, avoiding conflicts across requests, especially under load. The code I ended up with:</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>
<p><span style="font-family: 'Roboto Condensed', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif; font-size: 1.75rem;">Step 5: Add </span><code>withCredentials</code><span style="font-family: 'Roboto Condensed', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif; font-size: 1.75rem;"> in the Frontend</span></p>
<p>This step is just a side note: For session cookies to work between the frontend and backend, <code>withCredentials</code> need to be set to <code>true</code> on my frontend’s HTTP requests. This allows cookies to be included in cross-origin requests, which is important when the frontend and backend are hosted separately. I had to make sure Angular’s HTTP client had this setting enabled.</p>
<h3>Step 6: Include font</h3>
<p>To include font files in your NestJS project, you can use the <code>compilerOptions</code> in the <code>nest-cli.json</code> file to define assets for build output, like specifying <code>"include": "**/*.ttf"</code> and <code>"outDir": "dist/src"</code>.</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>After the build, ensure the fonts are correctly referenced in your code using <code>path.resolve</code>, e.g., <code>path.resolve(__dirname, '../fonts/Roboto-Regular.ttf')</code>. This approach ensures the font files are bundled with the build and accessible during runtime.</p>
<h3>Final Thoughts to NestJS Vercel hosting</h3>
<p>Deploying my NestJS app on Vercel was a true roller coaster. Sometimes, I felt like I was on the verge of getting everything working perfectly, only to be hit with new errors that sent me back to troubleshooting mode. There were moments of frustration—especially around the session handling and CORS issues. But each solution brought a new high, and every error fixed felt like a little victory.</p>
<p>Now, with everything finally working smoothly, I can say it feels awesome. Seeing my app live and functioning the way I envisioned is worth all the headaches. It’s a huge relief, but even more, it’s deeply satisfying to know I’ve overcome each hurdle and can look back on what I learned. I hope this guide can save others some of those low points and help them reach that “it just works” moment a little faster!</p>
<p>The post <a href="https://nerd-corner.com/lessons-learned-hosting-nestjs-app-on-vercel/">Lessons Learned: Hosting NestJS App on Vercel</a> appeared first on <a href="https://nerd-corner.com">Nerd Corner</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://nerd-corner.com/lessons-learned-hosting-nestjs-app-on-vercel/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>WeMos D1 R2 &#8211; Host entire website with html, css &#038; js</title>
		<link>https://nerd-corner.com/wemos-d1-r2-host-entire-website-with-html-css-js/</link>
					<comments>https://nerd-corner.com/wemos-d1-r2-host-entire-website-with-html-css-js/#respond</comments>
		
		<dc:creator><![CDATA[Nerds]]></dc:creator>
		<pubDate>Wed, 06 Mar 2024 12:18:52 +0000</pubDate>
				<category><![CDATA[Arduino projects]]></category>
		<category><![CDATA[Hardware]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[Arduino]]></category>
		<category><![CDATA[Arduino with Wifi]]></category>
		<category><![CDATA[C programming]]></category>
		<category><![CDATA[communication technique]]></category>
		<category><![CDATA[control Arduino with browser]]></category>
		<category><![CDATA[data transfer]]></category>
		<category><![CDATA[entire website]]></category>
		<category><![CDATA[ESP8266]]></category>
		<category><![CDATA[ESP8266 with mDNS]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[JS]]></category>
		<category><![CDATA[led]]></category>
		<category><![CDATA[local network]]></category>
		<category><![CDATA[network]]></category>
		<category><![CDATA[Step by step guide]]></category>
		<category><![CDATA[Webserver]]></category>
		<category><![CDATA[website hosting]]></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/de/?p=1547</guid>

					<description><![CDATA[<p>The first part of the WeMos series explained the setup and Wi-Fi integration. Building on this, this article describes step by step how to host &#8230; </p>
<p>The post <a href="https://nerd-corner.com/wemos-d1-r2-host-entire-website-with-html-css-js/">WeMos D1 R2 &#8211; Host entire website with html, css &#038; js</a> appeared first on <a href="https://nerd-corner.com">Nerd Corner</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>The first part of the WeMos series explained the setup and Wi-Fi integration. Building on this, this article describes step by step how to host a complete website with HTML pages, CSS styling and JavaScript functions on a WeMos. The whole thing is pretty simple and incredibly helpful! I can&#8217;t believe how hard it is to find proper instructions for this important function anywhere on the internet.</p>
<p>I often see blog articles where the HTML code is embedded in the Arduino file. You can do this for a mini demonstration, but it&#8217;s complete rubbish for everyday use. It is far too confusing and as soon as the project grows it is no longer usable.</p>
<p>The proper alternative is to set up a folder called &#8220;data&#8221; and store the web pages in this folder as html files. In addition, the styling is saved as a CSS file and functions can even be executed via a JavaScript file. So everything is 1:1 like on a normal web server!</p>
<p><em><strong>This might also be interesting for you:</strong> <a href="https://nerd-corner.com/wemos-d1-r2-setup-and-wifi-integration/">WeMos D1 R2 first steps and Wifi integration</a></em></p>
<h2>List of components</h2>
<ul>
<li>Arduino IDE (development environment)</li>
<li>WeMos D1 R2</li>
</ul>
<h2>The setup of the file system (officially SPIFFS) needs to be done once and is very easy:</h2>
<p><a href="https://nerd-corner.com/wemos-d1-r2-setup-and-wifi-integration/">(First you should have completed the basic setup from the first part!)</a></p>
<ol>
<li>Download a copy of the file &#8220;ESP8266FS-0.2.0.zip&#8221; from GitHub and unzip it</li>
<li>Place the file esp8266fs.jar in the Arduino tool directory. The path looks like this: [home_dir]\Arduino\tools\ESP8266FS\tool\esp8266fs.jar (See picture) I had to create the path part tools\ESP8266FS\tool\ in the Arduino folder myself.<img loading="lazy" 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="auto, (max-width: 1080px) 100vw, 1080px" /></li>
<li>Restart the Arduino IDE.</li>
</ol>
<p>That&#8217;s it already! You can now see the new item &#8220;ESP8266 Sketch Data Upload&#8221; in the Arduino IDE under Tools.</p>
<h2>How can I use the new file system now?</h2>
<ol>
<li>Create an additional folder with the name &#8220;data&#8221; in your current WeMos project folder. As shown in the following image</li>
</ol>
<p><img loading="lazy" 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="auto, (max-width: 1040px) 100vw, 1040px" /></p>
<ol start="2">
<li>Place the files you want to upload in the &#8216;data&#8217; directory</li>
<li>In the Arduino IDE, select the WeMos in the &#8216;Tools&#8217; menu and select a size for &#8216;Flash Size&#8217;</li>
<li>Close the dialogue box for the serial monitor!</li>
<li>Select the &#8216;ESP8266 Sketch Data Upload&#8217; option from the &#8216;Tools&#8217; menu.</li>
</ol>
<p><img loading="lazy" 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="auto, (max-width: 805px) 100vw, 805px" /></p>
<p>As soon as the upload is complete, the message window of the Arduino IDE shows 100% upload.</p>
<h2>WeMos example programme for switching the OnBoard LED on and off</h2>
<p>Similar to the first part, the web server will control the OnBoard LED. The code from the first part also serves as the basis. The revised code looks like this:</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>I would like to point out a few special features. For example, we have added the following:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="cpp" data-enlighter-theme="atomic">server.serveStatic("/", SPIFFS, "/", "max-age=86400"); 
SPIFFS.begin();</pre>
<p>Without these two lines, access to the files in the &#8220;data&#8221; folder would not be possible. Please note that the name &#8220;index.html&#8221; is set as the default for the landing page. However, you can change this if you really want to.</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>The &#8220;/led&#8221; endpoint receives the commands from the web server. If the command is &#8220;on&#8221;, the LED is switched on and if &#8220;off&#8221;, the LED is switched off.</p>
<h2>Wemos website for switching the WeMos OnBoard LED on and off</h2>
<p>The example website has a very simple structure. It primarily consists of 2 buttons for switching the LED on and off.</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>The folder structure of the website is very clearly organised. There is a main page with the name &#8220;index.html&#8221;. This name is common worldwide for the main pages and is also automatically recognised by WeMos. There is also a &#8220;CSS&#8221; folder for styling and a &#8220;JS&#8221; folder for functions.</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>We link the styles in the header area of the website. There is a standard bootstrap, which automatically makes everything a bit nicer, and a custom styles file with my own customisations. The functions of the website are also linked in the header area. I use the jQuery standard to send requests from the website to the WeMos. My own custom functions are in the &#8220;index.js&#8221;.</p>
<p>Please note that the jQuery file must be included BEFORE your own file, otherwise no jQuery commands can be used in your own code! The custom functions are then used by the buttons. The HTML code of the page looks as follows:</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>Special attention is paid to the JavaScript function &#8220;changeLEDState(value)&#8221;</p>
<pre class="EnlighterJSRAW" data-enlighter-language="js" data-enlighter-theme="atomic">function changeLEDState(value) {
  $.post("/led", { ledstate: value });
}</pre>
<p>Because jQuery is used for communication with the WeMos, a simple dollar sign with the corresponding request command is sufficient. A value is also sent with this POST request, which is either &#8220;on&#8221; or &#8220;off&#8221; to switch the LED on and off.</p>
<p>The web page can be downloaded as a zip file below.</p>
<h2>Files to download</h2>
<ul>
<li><a  data-e-Disable-Page-Transition="true" class="download-link" title="" href="https://nerd-corner.com/download/1540/?tmstv=1755847702" 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/wemos-d1-r2-host-entire-website-with-html-css-js/">WeMos D1 R2 &#8211; Host entire website with html, css &#038; js</a> appeared first on <a href="https://nerd-corner.com">Nerd Corner</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://nerd-corner.com/wemos-d1-r2-host-entire-website-with-html-css-js/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>NLP Application: Tensorflow.js vs Tensorflow Python &#8211; Part 2</title>
		<link>https://nerd-corner.com/nlp-application-tensorflow-js-vs-tensorflow-python-part-2/</link>
					<comments>https://nerd-corner.com/nlp-application-tensorflow-js-vs-tensorflow-python-part-2/#respond</comments>
		
		<dc:creator><![CDATA[Nerds]]></dc:creator>
		<pubDate>Wed, 31 May 2023 21:47:58 +0000</pubDate>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[adam]]></category>
		<category><![CDATA[Decoder]]></category>
		<category><![CDATA[Encoder]]></category>
		<category><![CDATA[GPU]]></category>
		<category><![CDATA[hidden state]]></category>
		<category><![CDATA[JSON]]></category>
		<category><![CDATA[Keras]]></category>
		<category><![CDATA[Long short term memory]]></category>
		<category><![CDATA[loss function]]></category>
		<category><![CDATA[LSTM]]></category>
		<category><![CDATA[Map]]></category>
		<category><![CDATA[Natural Language Processing]]></category>
		<category><![CDATA[NLP]]></category>
		<category><![CDATA[OOV]]></category>
		<category><![CDATA[OOV Token]]></category>
		<category><![CDATA[optimizer]]></category>
		<category><![CDATA[Pad sequences]]></category>
		<category><![CDATA[Padding]]></category>
		<category><![CDATA[rmsprop]]></category>
		<category><![CDATA[Tensorflow]]></category>
		<category><![CDATA[Tensorflow Anleitung]]></category>
		<category><![CDATA[TensorFlow GPU]]></category>
		<category><![CDATA[Tensorflow python]]></category>
		<category><![CDATA[Tensorflow.js]]></category>
		<category><![CDATA[Tensorflow.js vs Tensorflow]]></category>
		<category><![CDATA[Tfjs]]></category>
		<category><![CDATA[Tokenisierung]]></category>
		<category><![CDATA[Tokenizer]]></category>
		<category><![CDATA[Txt Datensatz]]></category>
		<category><![CDATA[WordTokenizer]]></category>
		<guid isPermaLink="false">https://nerd-corner.com/de/?p=1441</guid>

					<description><![CDATA[<p>The first part demonstrated how to read in and prepare datasets. In addition, the tokenisation of a dataset was discussed in detail. The points were &#8230; </p>
<p>The post <a href="https://nerd-corner.com/nlp-application-tensorflow-js-vs-tensorflow-python-part-2/">NLP Application: Tensorflow.js vs Tensorflow Python &#8211; Part 2</a> appeared first on <a href="https://nerd-corner.com">Nerd Corner</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>The first part demonstrated how to read in and prepare datasets. In addition, the tokenisation of a dataset was discussed in detail. The points were illustrated using an example in Tensorflow (Python) and Tensorflow.js (Tfjs). In both the Python example and the JavaScript example, the model is only able to recognise words that have occurred at least once in the data set. This model has issues with new words, because we didn&#8217;t take an OOV token into account for our NLP tensorflow application.</p>
<p><em><strong>You might also be interested in:</strong> <a href="https://nerd-corner.com/nlp-application-tensorflow-js-vs-tensorflow-python-part-1/">NLP application part 1 (reading data, preparing data and tokenisation)</a></em></p>
<h2>OOV Token</h2>
<p>With a translator, it is only a matter of time before an unknown word is entered. This can be a personal name, a spelling mistake or something similar. It is therefore advisable to train the model with regard to unknown words. An OOV token is needed for this. OOV stands for &#8220;out of vocabulary&#8221;. During training, the model learns to generate this token or to handle it accordingly. In this case, an unknown word can be replaced by the token &#8220;&lt;oov&gt;&#8221; before it is passed to the model. The model will then treat it like any other token and generate a response based on its learned behaviour.</p>
<p>Now you might ask how do you include this OOV token in the training data? I do this by automatically searching my dataset at the beginning for words that occur only 1 time. Then I replace these rare words with &#8220;&lt;oov&gt;&#8221; so that my model can also learn to react to unknown words.</p>
<h2>Padding</h2>
<p>In many machine learning models, including neural networks, the inputs are expected to have a fixed size or shape. This requirement arises from the structure and operation of the underlying computational graph. Inputs of the same length simplify the data processing pipeline and enable efficient batch processing.</p>
<h4>Why the inputs should have equal length:</h4>
<ol>
<li>Matrix operations: Neural networks typically process inputs in batches, and batch processing is most efficient when the input data has a uniform shape. The data is organised into matrices, with each row representing an input instance. To perform matrix operations efficiently, all input instances must have the same shape.</li>
<li>Sharing of parameters: In many neural network architectures, model parameters (weights) are shared across different parts of the input sequence. For example, in recurrent neural networks (RNNs), the same weights are used to process each time step. To enable sharing of parameters, all input sequences must have the same length.</li>
<li>Memory allocation: Neural networks often allocate memory based on the maximum length of the input sequences. If the sequences have different lengths, dynamic memory allocation is required, which can be more complex and less efficient.</li>
</ol>
<p>While it is possible to process variable length inputs using techniques such as padding and masking, this increases the complexity of the model and may require additional processing steps. For simplicity and efficiency, it is therefore common to pad or truncate sequences to a fixed length before feeding them into a neural network model.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="atomic">from keras.utils import pad_sequences

# pad sequences
encoder_seq = pad_sequences(encoder, maxlen=max_encoder_sequence_len, padding="post")
decoder_inp = pad_sequences([arr[:-1] for arr in decoder], maxlen=max_decoder_sequence_len, padding="post")
decoder_output = pad_sequences([arr[1:] for arr in decoder], maxlen=max_decoder_sequence_len, padding="post")
print(encoder_seq)
print([idx_2_txt_encoder[i] for i in encoder_seq[0]])
print([idx_2_txt_decoder[i] for i in decoder_inp[0]])
print([idx_2_txt_decoder[i] for i in decoder_output[0]])</pre>
<p>I have added the 4 print commands for better illustration. Initially, I thought that the longest record in the data set would set the length for input data and output data. So the padding length for input and output would be the same. But that is not the case! Input data and output data are normalised to different lengths!</p>
<p>In the example here, I have used a tiny data set where the longest English sentence consists of 3 words and the longest French sentence consists of 10 words. Accordingly, each training set is padded with &#8220;&lt;pad&gt;&#8221; or 0 until the input reaches 3 words and the output 10 words.</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1398 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/sampleOutputPadding.png" alt="Sample output padding tensorflow" width="1230" height="351" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/sampleOutputPadding.png 1243w, https://nerd-corner.com/wp-content/uploads/2023/05/sampleOutputPadding-300x86.png 300w, https://nerd-corner.com/wp-content/uploads/2023/05/sampleOutputPadding-1024x292.png 1024w, https://nerd-corner.com/wp-content/uploads/2023/05/sampleOutputPadding-768x219.png 768w" sizes="auto, (max-width: 1230px) 100vw, 1230px" /></p>
<p>The decoder output with <strong>[arr[1:] for arr in decoder]</strong> removes the &#8220;start&#8221; token and the decoder input with <strong>[arr[:-1] for arr in decoder]</strong> removes the &#8220;end&#8221; token.</p>
<p>In sequence-to-sequence models, the decoder is trained to generate the output sequence based on the input sequence and the previously generated tokens. During training, the input sequence of the decoder contains the &#8220;start&#8221; token, which serves as the initialisation token for the decoder. However, when training the decoder, it is supposed to predict the next token based on the previously generated tokens, except for the &#8220;start&#8221; token. Therefore, when preparing the decoder output sequence, the &#8220;start&#8221; token is removed from each sequence. This is done to correctly match the decoder input and output sequences. The decoder input sequence contains the &#8220;Start&#8221; token and excludes the &#8220;End&#8221; token, while the decoder output sequence contains the &#8220;End&#8221; token and excludes the &#8220;Start&#8221; token. In this way, we ensure that the decoder learns to generate the correct output sequence based on the input.</p>
<p>During inference (model application after training) or when using the trained model to generate translations, we can start with the &#8220;start&#8221; token and iteratively generate tokens until we encounter the &#8220;end&#8221; token or reach a maximum sequence length.</p>
<p>When padding for Tensorflow.js, we adopt Python&#8217;s approach 1:1. Unfortunately, we again have more work and more code lines, as there is no padSequences function in Tfjs. I have therefore written my own padSequences function:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="js" data-enlighter-theme="atomic">function padSequences(sequences) {
  const paddedSequences = [];
  const maxlen = findMaxLength(sequences);

  for (const sequence of sequences) {
    if (sequence.length &gt;= maxlen) {
      paddedSequences.push(sequence.slice(0, maxlen));
    } else {
      const paddingLength = maxlen - sequence.length;
      const paddingArray = new Array(paddingLength).fill(0);
      const paddedSequence = sequence.concat(paddingArray);
      paddedSequences.push(paddedSequence);
    }
  }

  return paddedSequences;
}</pre>
<p>We can then use this function to determine our encoder, decoder input and decoder output:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="js" data-enlighter-theme="atomic">function pad(data) {
  const encoderSeq = padSequences(data.en);
  const decoderInp = padSequences(data.de.map((arr) =&gt; arr.slice(0, -1))); // Has startToken
  const decoderOutput = padSequences(data.de.map((arr) =&gt; arr.slice(1))); // Has endToken
  console.log(decoderInp);
}</pre>
<p>In my case, the &#8220;1&#8221; is the &#8220;startToken&#8221;, so the decoder input looks like this, for example:</p>
<p><img loading="lazy" decoding="async" class="zoooom aligncenter wp-image-1420" src="https://nerd-corner.com/wp-content/uploads/2023/06/decoder-Input-example-JS.png" alt="Decoder Input Example JS NLP tensorflow" width="490" height="376" srcset="https://nerd-corner.com/wp-content/uploads/2023/06/decoder-Input-example-JS.png 499w, https://nerd-corner.com/wp-content/uploads/2023/06/decoder-Input-example-JS-300x230.png 300w" sizes="auto, (max-width: 490px) 100vw, 490px" /></p>
<h2>Create a model</h2>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="atomic"># Design LSTM NN (Encoder &amp; Decoder)
# encoder model
encoder_input = Input(shape=(None,), name="encoder_input_layer")
encoder_embedding = Embedding(num_encoder_tokens, 300, input_length=max_encoder_sequence_len, name="encoder_embedding_layer")(encoder_input)
encoder_lstm = LSTM(256, activation="tanh", return_sequences=True, return_state=True, name="encoder_lstm_1_layer")(encoder_embedding)
encoder_lstm2 = LSTM(256, activation="tanh", return_state=True, name="encoder_lstm_2_layer")(encoder_lstm)
_, state_h, state_c = encoder_lstm2
encoder_states = [state_h, state_c]

# decoder model
decoder_input = Input(shape=(None,), name="decoder_input_layer")
decoder_embedding = Embedding(num_decoder_tokens, 300, input_length=max_decoder_sequence_len, name="decoder_embedding_layer")(decoder_input)
decoder_lstm = LSTM(256, activation="tanh", return_state=True, return_sequences=True, name="decoder_lstm_layer")
decoder_outputs, _, _ = decoder_lstm(decoder_embedding, initial_state=encoder_states)
decoder_dense = Dense(num_decoder_tokens+1, activation="softmax", name="decoder_final_layer")
outputs = decoder_dense(decoder_outputs)

model = Model([encoder_input, decoder_input], outputs)</pre>
<p>The code example shows the design of a neural network with Long Short-Term Memory (LSTM) for sequence-to-sequence learning (Seq2Seq), which is typically used for tasks such as machine translation. The code defines two main parts: Encoder Model and Decoder Model.</p>
<h3>Encoder Model:</h3>
<ul>
<li style="list-style-type: none;">
<ul>
<li>The encoder input layer (<strong>encoder_input</strong>) represents the input sequence of the encoder model.</li>
<li>The input sequence is embedded using an embedding layer (<strong>encoder_embedding</strong>) that converts each token into a dense vector representation.</li>
<li>The embedded sequence is then passed through the first LSTM layer (<strong>encoder_lstm_1_layer</strong>) to capture sequential information. The LSTM layer returns the output sequence and the final hidden state.</li>
<li>The output sequence of the first LSTM layer is further processed by the second LSTM layer (<strong>encoder_lstm_2_layer</strong>). The second LSTM layer only returns the final hidden state, which is the summarised information of the input sequence.</li>
<li>The final hidden state of the second LSTM layer is split into the final hidden state (<strong>state_h</strong>) and the final cell state (<strong>state_c</strong>), which are used as initial states for the decoder model.</li>
<li>The states of the encoder model are defined as <strong>encoder_states</strong> and are passed on to the decoder model.</li>
</ul>
</li>
</ul>
<h3>Decoder Model:</h3>
<ul>
<li>The decoder input layer (<strong>decoder_input</strong>) represents the input sequence of the decoder model, which consists of the target sequence shifted by one position.</li>
<li>Similar to the encoder, the input sequence is embedded using an embedding layer (<strong>decoder_embedding</strong>).</li>
<li>The embedded sequence is then passed through an LSTM layer (<strong>decoder_lstm_layer</strong>), where the initial states are set to the final states of the encoder model. This allows the decoder to take into account the relevant information from the encoder.</li>
<li>The LSTM layer provides the output sequence and the end states.</li>
<li>The output sequence from the LSTM layer is passed through a dense layer (<strong>decoder_final_layer</strong>) with a softmax activation function that predicts the probability distribution over the output tokens.</li>
</ul>
<p>The <strong>Model</strong> class is used to create the overall model by specifying the input layers (<strong>[encoder_input, decoder_input]</strong>) and the output layer (<strong>outputs</strong>). This model architecture follows the basic structure of an encoder-decoder model using LSTMs, where the encoder processes the input sequence and generates the context vector (final hidden state), which is then used by the decoder to generate the output sequence.</p>
<p>The same model can also be implemented in JS:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="js" data-enlighter-theme="atomic">function createModell(
  numEncoderTokens,
  numDecoderTokens,
  maxEncoderSequenceLen,
  maxDecoderSequenceLen
) {
  // Encoder model
  const encoderInput = tf.input({ shape: [null], name: "encoderInputLayer" });
  const encoderEmbedding = tf.layers
    .embedding({
      inputDim: numEncoderTokens,
      outputDim: 300,
      inputLength: maxEncoderSequenceLen,
      name: "encoderEmbeddingLayer",
    })
    .apply(encoderInput);
  const encoderLstm = tf.layers
    .lstm({
      units: 256,
      activation: "tanh",
      returnSequences: true,
      returnState: true,
      name: "encoderLstm1Layer",
    })
    .apply(encoderEmbedding);
  const [_, state_h, state_c] = tf.layers
    .lstm({
      units: 256,
      activation: "tanh",
      returnState: true,
      name: "encoderLstm2Layer",
    })
    .apply(encoderLstm);
  const encoderStates = [state_h, state_c];

  // Decoder model
  const decoderInput = tf.input({ shape: [null], name: "decoderInputLayer" });
  const decoderEmbedding = tf.layers
    .embedding({
      inputDim: numDecoderTokens,
      outputDim: 300,
      inputLength: maxDecoderSequenceLen,
      name: "decoderEmbeddingLayer",
    })
    .apply(decoderInput);
  const decoderLstm = tf.layers.lstm({
    units: 256,
    activation: "tanh",
    returnState: true,
    returnSequences: true,
    name: "decoderLstmLayer",
  });
  const [decoderOutputs, ,] = decoderLstm.apply(decoderEmbedding, {
    initialState: encoderStates,
  });
  const decoderDense = tf.layers.dense({
    units: numDecoderTokens + 1,
    activation: "softmax",
    name: "decoderFinalLayer",
  });
  const outputs = decoderDense.apply(decoderOutputs);

  const model = tf.model({ inputs: [encoderInput, decoderInput], outputs });
  return model;
}</pre>
<h2>Train and save a model</h2>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="atomic"># train model
loss = tf.losses.SparseCategoricalCrossentropy()
model.compile(optimizer='rmsprop', loss=loss, metrics=['accuracy'])
callback = tf.keras.callbacks.EarlyStopping(monitor='loss', patience=3)
history = model.fit(
   [encoder_seq, decoder_inp],
   decoder_output,
   epochs=80,  # 80
   batch_size=450,  # 450
   # callbacks=[callback]
)</pre>
<p>The model.fit() function is used to train the model. The training data consists of the encoder input sequences (<strong>encoder_seq</strong>), the decoder input sequences (<strong>decoder_inp</strong>) and the decoder output sequences (<strong>decoder_output</strong>). The training is carried out for a certain number of epochs and a batch size of 450. The training progress can be monitored with the EarlyStopping callback, which stops the training if the loss has not improved after a certain number of epochs. The training progress is stored in the variable history.</p>
<p>The model in Tensorflow can handle both tensors and numpy arrays as inputs. If you pass numpy arrays as inputs to the <strong>fit</strong> function in TensorFlow, it will automatically convert them to tensors internally before training is performed. In the code, the <strong>encoder_seq</strong>, <strong>decoder_inp</strong> and <strong>decoder_output</strong> arrays are automatically converted to tensors when passed to the <strong>fit</strong> function. This allows TensorFlow to perform the necessary calculations during the training process.</p>
<p>Similarly, the <strong>fit</strong> function in TensorFlow.js can handle both tensors and arrays. So you can directly pass your 2D array (<strong>encoderSeq</strong>) as the first input and TensorFlow.js will internally convert them into tensors for training. Although you pass arrays instead of tensors, TensorFlow and TensorFlow.js are able to handle the conversion internally and perform the training accordingly.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="atomic"># save model
model.save("./model-experimental/Translate_Eng_FR.h5")
model.save_weights("./model-experimental/model_NMT")</pre>
<p>It is common to store the weights of a trained model separately from the model architecture. Storing the weights and the architecture separately allows more flexibility when loading and using the model. For example, one can load only the weights if the model architecture has been defined elsewhere or if the weights are to be used in another model with a similar architecture.</p>
<p>Finally, also the code in JavaScript:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="js" data-enlighter-theme="atomic">async function trainModel(data) {
  const encoderSeq = padSequences(data.en);
  const decoderInp = padSequences(data.de.map((arr) =&gt; arr.slice(0, -1))); // Has startToken
  const decoderOutput = padSequences(data.de.map((arr) =&gt; arr.slice(1))); // Has endToken

  data.model.compile({
    optimizer: "rmsprop",
    loss: "sparseCategoricalCrossentropy",
    metrics: ["accuracy"],
  });
  const history = await data.model.fit(
    [encoderSeq, decoderInp],
    decoderOutput,
    {
      epochs: 80,
      batch_size: 450,
    }
  );
}</pre>
<p>This is where my frustration with Tensorflow.js comes in. Although each step is 1:1 the same as the step in Python, training the model in Tensorflow.js doesn&#8217;t work&#8230;. I always get an error message:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="atomic">C:\Users\[...]\node_modules\@tensorflow\tfjs-layers\dist\tf-layers.node.js:23386
            if (array.shape.length !== shapes[i].length) {
                            ^

TypeError: Cannot read properties of undefined (reading 'length')
    at standardizeInputData</pre>
<h2>General loss function and optimiser</h2>
<p>Loss functions and optimisers are key components in training a machine learning model. A loss function, also known as an objective or cost function, measures the performance of a model by quantifying the dissimilarity between predicted outputs and actual objectives. The goal of training a model is to minimise this loss function, which essentially means improving the model&#8217;s ability to make accurate predictions. The choice of loss function depends on the problem at hand. For example, in classification tasks, categorical cross entropy, binary cross entropy and softmax cross entropy are common loss functions, while in regression tasks, mean square error (<a href="https://en.wikipedia.org/wiki/Mean_squared_error" target="_blank" rel="noopener">MSE</a>) and mean absolute error (MAE) are often used.</p>
<p>An optimiser, on the other hand, is responsible for updating the model parameters (weights and biases) during training to minimise the loss function. He determines how to adjust the parameters of the model based on the calculated gradients of the loss function with respect to these parameters. Optimisers use various algorithms and techniques to efficiently search for the optimal values of the parameters. Common optimisers include Stochastic Gradient Descent (SGD), Adam, RMSprop and Adagrad. Each optimiser has its own features and hyper-parameters that can affect the training process and the convergence speed of the model.</p>
<p>The choice of loss function and optimiser depends on the specific task, the model architecture and the characteristics of the data set. It is important to select appropriate loss functions and optimisers to ensure effective model training and convergence to optimal performance.</p>
<h2>Frequently used loss functions and optimisers</h2>
<h4>Loss functions:</h4>
<ul>
<li>Categorical cross entropy: This loss function is often used in sequence-to-sequence models for multi-class classification problems where each target word is treated as a separate class.</li>
<li>Sparse categorical cross entropy: Similar to categorical cross entropy, but suitable when the target sequences are represented as sparse integer sequences (e.g. using word indices).</li>
</ul>
<h4>Optimiser:</h4>
<ul>
<li>Adam: Adam is a popular optimiser that combines the advantages of the Adaptive Gradient Algorithm (AdaGrad) and Root Mean Square Propagation (RMSprop). It adjusts the learning rate for each parameter based on previous gradients, which contributes to faster convergence and better handling of sparse gradients.</li>
<li>RMSprop: RMSprop is an optimiser that maintains a moving average of squared gradients for each parameter. It adjusts the learning rate based on the size of the gradient, allowing for faster convergence and better performance on non-stationary targets.</li>
<li>Adagrad: Adagrad adjusts the learning rate individually for each parameter based on historical gradient accumulation. It performs larger updates for infrequent parameters and smaller updates for frequent parameters.</li>
</ul>
<h2>Files for download</h2>
<ul>
<li><a  data-e-Disable-Page-Transition="true" class="download-link" title="" href="https://nerd-corner.com/download/1432/?tmstv=1755847702" rel="nofollow" id="download-link-1432" data-redirect="false" >
	NLP Tensorflow.js code (model has an error!)</a>
</li>
</ul>
<p>The post <a href="https://nerd-corner.com/nlp-application-tensorflow-js-vs-tensorflow-python-part-2/">NLP Application: Tensorflow.js vs Tensorflow Python &#8211; Part 2</a> appeared first on <a href="https://nerd-corner.com">Nerd Corner</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://nerd-corner.com/nlp-application-tensorflow-js-vs-tensorflow-python-part-2/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>NLP Application: Tensorflow.js vs Tensorflow Python &#8211; Part 1</title>
		<link>https://nerd-corner.com/nlp-application-tensorflow-js-vs-tensorflow-python-part-1/</link>
					<comments>https://nerd-corner.com/nlp-application-tensorflow-js-vs-tensorflow-python-part-1/#respond</comments>
		
		<dc:creator><![CDATA[Nerds]]></dc:creator>
		<pubDate>Wed, 31 May 2023 19:17:55 +0000</pubDate>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[CSV dataset]]></category>
		<category><![CDATA[Decoder]]></category>
		<category><![CDATA[Encoder]]></category>
		<category><![CDATA[GPU]]></category>
		<category><![CDATA[hidden state]]></category>
		<category><![CDATA[JSON]]></category>
		<category><![CDATA[Keras]]></category>
		<category><![CDATA[Long short term memory]]></category>
		<category><![CDATA[loss function]]></category>
		<category><![CDATA[LSTM]]></category>
		<category><![CDATA[Map]]></category>
		<category><![CDATA[Natural Language Processing]]></category>
		<category><![CDATA[natural library]]></category>
		<category><![CDATA[NLP]]></category>
		<category><![CDATA[nlp application]]></category>
		<category><![CDATA[OOV]]></category>
		<category><![CDATA[OOV Token]]></category>
		<category><![CDATA[optimizer]]></category>
		<category><![CDATA[Pad sequences]]></category>
		<category><![CDATA[Padding]]></category>
		<category><![CDATA[read in CSV]]></category>
		<category><![CDATA[rmsprop]]></category>
		<category><![CDATA[Tensorflow]]></category>
		<category><![CDATA[TensorFlow GPU]]></category>
		<category><![CDATA[Tensorflow guide]]></category>
		<category><![CDATA[Tensorflow python]]></category>
		<category><![CDATA[Tensorflow.js]]></category>
		<category><![CDATA[Tensorflow.js vs Tensorflow]]></category>
		<category><![CDATA[Tfjs]]></category>
		<category><![CDATA[tokenization]]></category>
		<category><![CDATA[txt dataset]]></category>
		<category><![CDATA[WordTokenizer]]></category>
		<guid isPermaLink="false">https://nerd-corner.com/de/?p=1440</guid>

					<description><![CDATA[<p>I am currently working on a project in which I want to program a German to Bavarian translator using machine learning. This is called Natural &#8230; </p>
<p>The post <a href="https://nerd-corner.com/nlp-application-tensorflow-js-vs-tensorflow-python-part-1/">NLP Application: Tensorflow.js vs Tensorflow Python &#8211; Part 1</a> appeared first on <a href="https://nerd-corner.com">Nerd Corner</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>I am currently working on a project in which I want to program a German to Bavarian translator using machine learning. This is called Natural Language Processing (NLP). A Google Library called Tensorflow is often used for the implementation. There is Tensorflow.js as well as Tensorflow (Python). Since I develop professionally with Angular and therefore I am familiar with TypeScript and JavaScript, I initially decided to use the NLP application in Tensorflow.js. I was naive enough to assume that the only difference between the two libraries would be the programming language used. This is definitely not the case! For my NLP project, some basic functions are missing in Tensorflow.js (such as a tokenizer).<a href="https://nerd-corner.com/tensorflow-js-vs-tensorflow-python/"> In this post I explained the general differences between Tensorflow.js and Tensorflow (Python).</a></p>
<p>I spent many evenings trying to get my project to work with Tensorflow.js and failed in the end. Switching to Python brought the breakthrough I was hoping for! I would recommend everyone to use Python for NLP applications! Nevertheless, in this article I want to explain the differences between Tensorflow.js and Tensorflow in relation to my project using code examples. In between, I will also incorporate my newly accumulated knowledge into the respective sections as best I can.</p>
<p><em><strong>You might also be interested in:</strong> <a href="https://nerd-corner.com/nlp-application-tensorflow-js-vs-tensorflow-python-part-2/">NLP application part 2 (OOV token, padding, creating the model and training the model).</a></em></p>
<h2>Reading in data</h2>
<p>First of all, you need a data set with which the model will be trained later. Here I can recommend <a href="https://www.kaggle.com/" target="_blank" rel="noopener">https://www.kaggle.com/</a>. There you find a large number of data sets for free use and even some code examples. You can either read in the data set via a link or download it and then read it in locally from the file system. A good data set should contain over 100,000 examples. Preferably also whole paragraphs. For example, this is what an English/French data set looks like as a CSV:</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1389 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/sample-dataset-tensorflow.png" alt="Sample dataset tensorflow CSV" width="1380" height="332" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/sample-dataset-tensorflow.png 1393w, https://nerd-corner.com/wp-content/uploads/2023/05/sample-dataset-tensorflow-300x72.png 300w, https://nerd-corner.com/wp-content/uploads/2023/05/sample-dataset-tensorflow-1024x246.png 1024w, https://nerd-corner.com/wp-content/uploads/2023/05/sample-dataset-tensorflow-768x185.png 768w" sizes="auto, (max-width: 1380px) 100vw, 1380px" /></p>
<p>First, the simple variant using Python:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-title="Read in CSV" data-enlighter-theme="atomic">import pandas as pd

# read in dataSet for training
df = pd.read_csv("./dataset/eng_-french.csv")
df.columns = ["english", "french"]
print(df.head())
print(df.info())</pre>
<p>We use the pandas library and read in the CSV with it. With the head() we can test if it worked and display the first 5 rows. With info() we get more information like number of columns and number of rows:</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1388 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/output-read-in-CSV-python.png" alt="output CSV read in with python pandas lib" width="575" height="516" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/output-read-in-CSV-python.png 583w, https://nerd-corner.com/wp-content/uploads/2023/05/output-read-in-CSV-python-300x269.png 300w" sizes="auto, (max-width: 575px) 100vw, 575px" /></p>
<p>For comparison in Tensorflow.js (Tfjs) there is also a possibility to read in CSV:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="js" data-enlighter-title="index.js" data-enlighter-theme="atomic">const tf = require("@tensorflow/tfjs");

async function readInData() {
  await tf.ready();
  const languageDataSet = tf.data.csv("file://" + "./ger_en_trans.csv");

  // Extract language pairs
  const dataset = languageDataSet.map((record) =&gt; ({
    en: record.en,
    de: record.de,
  }));

  const pairs = await dataset.toArray();

  console.log(pairs);
}

readInData();</pre>
<p>I tried at first to read in the same data set as in the Python version:</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1408 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/Tensorflow.js-ReadIn-Same-CSV.png" alt="read in same CSV as in tf" width="745" height="220" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/Tensorflow.js-ReadIn-Same-CSV.png 758w, https://nerd-corner.com/wp-content/uploads/2023/05/Tensorflow.js-ReadIn-Same-CSV-300x89.png 300w" sizes="auto, (max-width: 745px) 100vw, 745px" /></p>
<p>Afterwards I wanted to shorten the headings in the original CSV, but this strangely gave me an error message when reading in. Even when I restored the CSV to its original state, the error remained:</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1407 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/read-in-modified-CSV-before-it-breaks-tfjs.png" alt="error when reading in CSV" width="610" height="298" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/read-in-modified-CSV-before-it-breaks-tfjs.png 620w, https://nerd-corner.com/wp-content/uploads/2023/05/read-in-modified-CSV-before-it-breaks-tfjs-300x147.png 300w" sizes="auto, (max-width: 610px) 100vw, 610px" /></p>
<p>In the end, I decided to use a different data set:</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1406 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/CSV-sample-screenshot-en-de.png" alt="other CSV data samples" width="375" height="356" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/CSV-sample-screenshot-en-de.png 389w, https://nerd-corner.com/wp-content/uploads/2023/05/CSV-sample-screenshot-en-de-300x285.png 300w" sizes="auto, (max-width: 375px) 100vw, 375px" /></p>
<p>This one was also much more readable when it was read in:</p>
<p><img loading="lazy" decoding="async" class="zoooom aligncenter wp-image-1405" src="https://nerd-corner.com/wp-content/uploads/2023/05/reading-in-the-german-english-CSV.png" alt="NLP Anwendung Tensorflow.js" width="825" height="276" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/reading-in-the-german-english-CSV.png 836w, https://nerd-corner.com/wp-content/uploads/2023/05/reading-in-the-german-english-CSV-300x100.png 300w, https://nerd-corner.com/wp-content/uploads/2023/05/reading-in-the-german-english-CSV-768x257.png 768w" sizes="auto, (max-width: 825px) 100vw, 825px" /></p>
<p>And here is the final result after the mapping:</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1404 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/result-data-read-in-tfjs.png" alt="after mapping csv" width="515" height="99" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/result-data-read-in-tfjs.png 528w, https://nerd-corner.com/wp-content/uploads/2023/05/result-data-read-in-tfjs-300x58.png 300w" sizes="auto, (max-width: 515px) 100vw, 515px" /></p>
<p>Although Tfjs offers an extra function to read in the CSV, I still had more trouble than in the Python version. I have also not found a quick way to read in a data set in txt format. However, txt files are widespread!</p>
<p>Prepare data</p>
<p>I have often seen that a cleaning function was written for data preparation and that the output set also received a start and end token. I then wondered whether the input set, i.e. the encoder, also needs a start and end token. In the context of sequence-to-sequence models, however, the encoder does not need explicit start and end tokens. Its purpose is to process the input sequence as it is and produce a representation of the input.</p>
<p>The decoder, on the other hand, which generates the output sequence, usually benefits from the use of start and end tokens. These tokens help to mark the beginning and end of the generated sequence. The use of start and end tokens is therefore specific to the decoder. During training, the input sequence of the decoder includes a start token at the beginning and excludes an end token at the end. The output sequence of the decoder contains the end token and excludes the start token. In this way, the model learns to generate the correct output sequence based on the input.</p>
<p>When creating translations with the trained model, you start with the start token and generate one token after another until you hit the end token or reach a maximum sequence length. Adding start and end tokens to the decoder set improves the performance of the NLP translator model. It helps to establish clear sequence boundaries and supports the generation process by indicating where the translation starts and ends.</p>
<h4>In summary:</h4>
<ul>
<li>Encoder: No need for start and end tokens. Processes the input sequence as it is.</li>
<li>Decoder: Start and end tokens are helpful for generating the output sequence.</li>
</ul>
<p>We start again with the easy part, namely Python. We want to clean up the data we read in. This means converting everything to lower case and removing characters that are not part of the alphabet or punctuation marks. For this we need the regex library (re).</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="atomic">import re

def clean(text):
    text = text.lower()  # lower case
    # remove any characters not a-z and ?!,'
    # please note that french has additional characters...I just simplified that
    text = re.sub(u"[^a-z!?',]", " ", text)
    return text


# apply cleaningFunctions to dataframe
data["english"] = data["english"].apply(lambda txt: clean(txt))
data["french"] = data["french"].apply(lambda txt: clean(txt))

# add &lt;start&gt; &lt;end&gt; token to decoder sentence (french)
data["french"] = data["french"].apply(lambda txt: f"&lt;start&gt; {txt} &lt;end&gt;")

print(data.sample(10))</pre>
<p>I have simplified here. Since this is a French data set, one should actually write an extra cleaning function that also takes French letters like &#8220;ê&#8221; into account. The sample() function only serves to illustrate the data:</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1391 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/random-sample-of-cleaned-data.png" alt="tensorflow random sample of cleaned data" width="630" height="385" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/random-sample-of-cleaned-data.png 639w, https://nerd-corner.com/wp-content/uploads/2023/05/random-sample-of-cleaned-data-300x184.png 300w" sizes="auto, (max-width: 630px) 100vw, 630px" /></p>
<p>In Tfjs the process is absolutely identical. I have created a cleanData() function and modified the previous code:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="js" data-enlighter-theme="atomic">function cleanData(text) {
  //if necessary also remove any characters not a-z and ?!,'
  return text.toLowerCase();
}</pre>
<pre class="EnlighterJSRAW" data-enlighter-language="js" data-enlighter-theme="atomic">const dataset = languageDataSet.map((record) =&gt; ({
   en: cleanData(record.en),
   de: "startToken " + cleanData(record.de) + " endToken",
 }));</pre>
<p>The result is therefore also identical to the Python approach:</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1410 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/cleaned-up-tfjs-data.png" alt="cleaned up tfjs data" width="630" height="146" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/cleaned-up-tfjs-data.png 639w, https://nerd-corner.com/wp-content/uploads/2023/05/cleaned-up-tfjs-data-300x69.png 300w" sizes="auto, (max-width: 630px) 100vw, 630px" /></p>
<p>If the words &#8220;start&#8221; and &#8220;end&#8221; are part of regular sentences and are not used as special tokens to mark the beginning and end of sequences, then they should definitely not be replaced by corresponding indices during tokenisation. When tokenising, it is important to choose special tokens that are unlikely to occur in the actual input data. This ensures that the model can distinguish them from normal words and learns to produce the appropriate output sequences.</p>
<p>If the words &#8221; start&#8221; and &#8220;end&#8221; are regular words in the input sentences, consider using different special tokens to mark the start and end of sequences. A common choice is &#8221; &lt;start&gt;&#8221; and &#8220;&lt;end&gt;&#8221;. Using special tokens that are unlikely to be part of the regular vocabulary can ensure that they can be correctly identified and processed by the model during training and generation.</p>
<h4>For example, the tokenised sequences would look like this:</h4>
<ul>
<li>Decoder Input: [&#8220;&lt;start&gt;&#8221;, &#8220;hello&#8221;, &#8220;world&#8221;]</li>
<li>Decoder Output: [&#8220;hello&#8221;, &#8220;world&#8221;, &#8220;&lt;end&gt;&#8221;]</li>
</ul>
<h4>Therefore AVOID the following:</h4>
<ul>
<li>Decoder Input: [&#8220;start&#8221;, &#8220;hello&#8221;, &#8220;world&#8221;]</li>
<li>Decoder output: [&#8220;hello&#8221;, &#8220;world&#8221;, &#8220;end&#8221;]</li>
</ul>
<h2>Tokenisation</h2>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="atomic"># tokenization
import tensorflow as tf
from tensorflow import keras
from keras.preprocessing.text import Tokenizer
import numpy as np

# english tokenizer
english_tokenize = Tokenizer(filters='#$%&amp;()*+,-./:;&lt;=&gt;@[\\]^_`{|}~\t\n')
english_tokenize.fit_on_texts(data["english"])
num_encoder_tokens = len(english_tokenize.word_index)+1
# print(num_encoder_tokens)
encoder = english_tokenize.texts_to_sequences(data["english"])
# print(encoder[:5])
max_encoder_sequence_len = np.max([len(enc) for enc in encoder])
# print(max_encoder_sequence_len)

# french tokenizer
french_tokenize = Tokenizer(filters="#$%&amp;()*+,-./:;&lt;=&gt;@[\\]^_`{|}~\t\n")
french_tokenize.fit_on_texts(data["french"])
num_decoder_tokens = len(french_tokenize.word_index)+1
# print(num_decoder_tokens)
decoder = french_tokenize.texts_to_sequences(data["french"])
# print(decoder[:5])
max_decoder_sequence_len = np.max([len(dec) for dec in decoder])
# print(max_decoder_sequence_len)

</pre>
<p>This code performs tokenisation and sequence preprocessing with the Tokenizer class in TensorFlow.</p>
<ol>
<li><strong>english_tokenize = Tokenizer(filters=&#8217;#$%&amp;()*+,-./:;&lt;=&gt;@[\]^_`{|}~\t\n&#8217;)</strong> Initialises a tokenizer object for English sentences. The <strong>filters</strong> parameter specifies characters to be filtered out during tokenisation. We have already filtered the data in the cleaning process, so it is not really necessary to filter again here.</li>
<li><strong>english_tokenize.fit_on_texts(data[&#8220;english&#8221;])</strong> Updates the internal vocabulary of the tokenizer based on the English sentences in the variable <strong>data</strong>. Each word in the vocabulary is assigned a unique index.</li>
<li><strong>num_encoder_tokens = len(english_tokenize.word_index) + 1</strong> Determines the number of unique tokens (words) in the English vocabulary. The <strong>word_index</strong> attribute of the tokeniser returns a dictionary that maps words to their respective indices.</li>
<li><strong>encoder = english_tokenize.texts_to_sequences(data[&#8220;english&#8221;])</strong> Converts the English sentences in the variable <strong>data</strong> into sequences of token indices using the tokenizer. Each sentence is replaced by a sequence of integers representing the corresponding words.</li>
<li><strong>max_encoder_sequence_len = np.max([len(enc) for enc in encoder])</strong> Calculates the maximum length (number of tokens) among all encoded sequences. It uses the <strong>max</strong> function of NumPy to find the maximum value in a list comprehension.</li>
</ol>
<p>These steps help to prepare the sentences for further processing in an NLP model. This is necessary for both languages!</p>
<p>The sentences have now been tokenised, then converted into sequences of token indices and the maximum sequence length determined. An example sentence from the dataset now looks like this: [[148], [252], [59], [14], [111]]. Here, 148 could stand for &#8220;I&#8221;, 252 for &#8220;am&#8221;, 59 for &#8220;very&#8221;, 14 for &#8220;hungry&#8221; and 111 for &#8220;now&#8221;.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="atomic">idx_2_txt_decoder = {k: i for i, k in french_tokenize.word_index.items()}
# print(idx_2_txt_decoder)
idx_2_txt_encoder = {k: i for i, k in english_tokenize.word_index.items()}
# print(idx_2_txt_encoder)

idx_2_txt_decoder[0] = "&lt;pad&gt;"
idx_2_txt_encoder[0] = "&lt;pad&gt;"</pre>
<p>The code snippet <strong>idx_2_txt_encoder = {k: i for i, k in english_tokenize.word_index.items()}</strong> creates a dictionary directory <strong>idx_2_txt_encoder</strong> that maps token indices to the corresponding words in the English vocabulary: <strong>{k: i for i, k in english_tokenize.word_index.items()}</strong> is a dictionary that iterates over the key-value pairs in <strong>english_tokenize.word_index</strong>. At each iteration, the key (<strong>k</strong>) represents a word in the vocabulary, and the value (<strong>i</strong>) represents the corresponding index. Understanding creates a new dictionary whose keys are the indices (<strong>i</strong>) and the values are the words (<strong>k</strong>).</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1395 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/index2textencoder-printed.png" alt="index 2 tokenizer dicitonary sample" width="1730" height="253" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/index2textencoder-printed.png 1742w, https://nerd-corner.com/wp-content/uploads/2023/05/index2textencoder-printed-300x44.png 300w, https://nerd-corner.com/wp-content/uploads/2023/05/index2textencoder-printed-1024x150.png 1024w, https://nerd-corner.com/wp-content/uploads/2023/05/index2textencoder-printed-768x112.png 768w, https://nerd-corner.com/wp-content/uploads/2023/05/index2textencoder-printed-1536x225.png 1536w" sizes="auto, (max-width: 1730px) 100vw, 1730px" /></p>
<p>The resulting <strong>idx_2_txt_encoder</strong> &#8211; dictionary allows you to look up the word corresponding to a particular index in the English vocabulary. <strong>english_tokenize.word_index</strong>, by the way, would swap the displays exactly. Here the key would be the word and the value the index. The second line, <strong>idx_2_txt_encoder[0] = &#8220;&lt;pad&gt;&#8221;</strong>, adds a special entry to the dictionary. Here, the word &#8220;&lt;pad&gt;&#8221; is assigned to index &#8220;0&#8221; to specify a padding token that is used when padding sequences.</p>
<p>Afterwards, one should save the dictionary directory, because later when the model has been trained and is used, the translations of the model will also be a series of indices that are transformed back into readable sentences with the help of the dictionary.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="atomic"># Saving the dicitionaries
pickle.dump(idx_2_txt_encoder, open("./saves/idx_2_word_input.txt", "wb"))
pickle.dump(idx_2_txt_decoder, open("./saves/idx_2_word_target.txt", "wb"))</pre>
<p>The same process as in Python can also be constructed for the NLP application in Tensorflow.js. Of course, you need a little more lines of code and the overall workload is higher. The first hurdle here is the tokeniser. Unfortunately, unlike Tensorflow (Python), Tfjs does not have its own tokenizer. After extensive research, I luckily found the natural.WordTokenizer. I would like to point out here that a Node.js project is definitely required. Tfjs can be integrated via a &lt;script&gt; tag, but the natural.WordTokenizer cannot!</p>
<p>Another important point is that the WordTokenizer removes &#8220;&lt;&#8221; and &#8220;&gt;&#8221;. An output sentence &#8220;&lt;start&gt; I eat &lt;end&gt;&#8221; therefore simply becomes [&#8216;start&#8217;, &#8216;I&#8217;, &#8216;eat&#8217;, &#8216;end&#8217;]. Thus the &#8220;&lt;start&gt;&#8221; and &#8220;&lt;end&gt;&#8221; tokens are no longer clearly recognisable! I have therefore replaced them in the JS code from the beginning with &#8220;startToken&#8221; and &#8220;endToken&#8221;.</p>
<p>First, we tokenise every single sentence from the dataset again and then create a vocabulary dictionary for each of the two languages. Finally, we replace all the words with the indexes from the vocabulary dictionary:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="js" data-enlighter-theme="atomic">const natural = require("natural");

function tokenize(data) {
  const tokenizer = new natural.WordTokenizer();

  enData = data.map((row) =&gt; tokenizer.tokenize(row.en));
  deData = data.map((row) =&gt; tokenizer.tokenize(row.de));

  const enVocabulary = new Map();
  const deVocabulary = new Map();

  // Insert &lt;pad&gt; at index 0
  enVocabulary.set("&lt;pad&gt;", 0);
  deVocabulary.set("&lt;pad&gt;", 0);

  const fillVocabulary = (langData, vocabMap) =&gt; {
    langData.forEach((sentence) =&gt; {
      sentence.forEach((word) =&gt; {
        if (!vocabMap.has(word)) {
          const newIndex = vocabMap.size;
          vocabMap.set(word, newIndex);
        }
      });
    });
  };

  fillVocabulary(enData, enVocabulary);
  fillVocabulary(deData, deVocabulary);

  // Replace words with indexes
  const indexedEnData = enData.map((element) =&gt;
    element.map((word) =&gt; enVocabulary.get(word))
  );
  const indexedDeData = deData.map((element) =&gt;
    element.map((word) =&gt; deVocabulary.get(word))
  );

  return { en: indexedEnData, de: indexedDeData };
}</pre>
<p>In order to be able to convert the results of our model back into words later, and in order to be able to use the model later in the real application case, we save the two vocabulary dictionaries. I have swapped the key and value pairs, but in the end this is not mandatory:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="js" data-enlighter-theme="atomic">const fs = require("fs");

// store the input and output key value pairs
  fs.writeFileSync(
    "vocabulary/inputVocableSet.json",
    JSON.stringify(switchKeysAndValues(Object.fromEntries(enVocabulary)))
  );
  fs.writeFileSync(
    "vocabulary/outputVocableSet.json",
    JSON.stringify(switchKeysAndValues(Object.fromEntries(deVocabulary)))
  );

function switchKeysAndValues(obj) {
  const switchedObj = {};
  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      const value = obj[key];
      switchedObj[value] = key;
    }
  }
  return switchedObj;
}</pre>
<p>As a result we get a JSON object with our vocabulary:</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1411 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/06/excerpt-of-the-stored-json-map.png" alt="excerpt of the stored json map" width="240" height="553" srcset="https://nerd-corner.com/wp-content/uploads/2023/06/excerpt-of-the-stored-json-map.png 249w, https://nerd-corner.com/wp-content/uploads/2023/06/excerpt-of-the-stored-json-map-130x300.png 130w" sizes="auto, (max-width: 240px) 100vw, 240px" /></p>
<p>We then return the result of our function:</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1412 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/06/result-after-tokenization.png" alt="result after tokenization" width="890" height="679" srcset="https://nerd-corner.com/wp-content/uploads/2023/06/result-after-tokenization.png 895w, https://nerd-corner.com/wp-content/uploads/2023/06/result-after-tokenization-300x229.png 300w, https://nerd-corner.com/wp-content/uploads/2023/06/result-after-tokenization-768x586.png 768w" sizes="auto, (max-width: 890px) 100vw, 890px" /></p>
<h2>Files for download</h2>
<ul>
<li><a  data-e-Disable-Page-Transition="true" class="download-link" title="" href="https://nerd-corner.com/download/1432/?tmstv=1755847702" rel="nofollow" id="download-link-1432" data-redirect="false" >
	NLP Tensorflow.js code (model has an error!)</a>
</li>
</ul>
<p>The post <a href="https://nerd-corner.com/nlp-application-tensorflow-js-vs-tensorflow-python-part-1/">NLP Application: Tensorflow.js vs Tensorflow Python &#8211; Part 1</a> appeared first on <a href="https://nerd-corner.com">Nerd Corner</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://nerd-corner.com/nlp-application-tensorflow-js-vs-tensorflow-python-part-1/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Tensorflow.js vs Tensorflow (Python)</title>
		<link>https://nerd-corner.com/tensorflow-js-vs-tensorflow-python/</link>
					<comments>https://nerd-corner.com/tensorflow-js-vs-tensorflow-python/#respond</comments>
		
		<dc:creator><![CDATA[Nerds]]></dc:creator>
		<pubDate>Thu, 18 May 2023 14:11:37 +0000</pubDate>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[CUDA]]></category>
		<category><![CDATA[Deep Learning]]></category>
		<category><![CDATA[GPU]]></category>
		<category><![CDATA[Machine Learning]]></category>
		<category><![CDATA[Natural Language Processing]]></category>
		<category><![CDATA[NLP]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[software]]></category>
		<category><![CDATA[software comparison]]></category>
		<category><![CDATA[Tensorflow]]></category>
		<category><![CDATA[Tensorflow Anleitung]]></category>
		<category><![CDATA[tensorflow comparison]]></category>
		<category><![CDATA[TensorFlow GPU]]></category>
		<category><![CDATA[Tensorflow guide]]></category>
		<category><![CDATA[Tensorflow python]]></category>
		<category><![CDATA[Tensorflow.js]]></category>
		<category><![CDATA[Tensorflow.js vs Tensorflow]]></category>
		<category><![CDATA[TPU]]></category>
		<guid isPermaLink="false">https://nerd-corner.com/de/?p=1384</guid>

					<description><![CDATA[<p>In the world of machine learning and deep learning, there are a variety of tools and libraries that help developers create and train advanced models. &#8230; </p>
<p>The post <a href="https://nerd-corner.com/tensorflow-js-vs-tensorflow-python/">Tensorflow.js vs Tensorflow (Python)</a> appeared first on <a href="https://nerd-corner.com">Nerd Corner</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>In the world of machine learning and deep learning, there are a variety of tools and libraries that help developers create and train advanced models. Two popular options are Tensorflow.js and the original Tensorflow, developed for JavaScript and Python respectively. Due to a project where I want to use NLP (Natural Language Processing) to automatically convert standard language texts into dialect, I took a closer look at the two options. In this blog article I will discuss the differences and advantages of Tensorflow.js vs Tensorflow (Python). Afterwards, everyone should be able to decide between Tensorflow.js vs Tensorflow (Python).</p>
<p><em><strong>This might also be interesting for you:</strong> <a href="https://nerd-corner.com/enable-tensorflow-gpu-under-windows/">How to enable GPU for TensorFlow in Windows</a></em></p>
<h2>Language and environment</h2>
<p>Tensorflow.js was developed specifically for JavaScript, which means that models can be implemented directly in JavaScript code. This allows seamless integration into web applications and execution of models in the browser without additional server infrastructure.</p>
<p>Tensorflow Python provides a comprehensive Python library for machine learning. Python is a widely used programming language in the field of machine learning and offers a variety of libraries and frameworks to facilitate model development and deployment.</p>
<h2>Target platform</h2>
<p>One of the greatest strengths of Tensorflow.js is the ability to run models directly in the browser. This allows developers to create interactive web applications with machine learning without the user having to install additional software or send external server requests.</p>
<p>Tensorflow Python, on the other hand, allows models to be developed and run on multiple platforms, including desktop computers, servers and mobile devices. It offers a wide range of features and supports advanced techniques such as training models on GPUs or TPUs. TPU stands for &#8220;Tensor Processing Unit&#8221;. These are application-specific chips to accelerate machine learning applications.</p>
<h2>Community</h2>
<p>To be honest, the Tensorflow.js community is simply too small. With many questions, one has the feeling that one is doing &#8220;pioneering work&#8221; here, which is not recommended, especially for beginners. To be more specific: There are 80 times more StackOverflow questions about TensorFlow than about TensorFlow.js. However, depending on the use case, a number of resources, tutorials and examples now also exist for TensorFlow.js that can help with development with Tensorflow.js. Since JavaScript is a widely used language, one can also access a large amount of general web development resources that can help when working with Tensorflow.js.</p>
<p>Tensorflow Python, on the other hand, benefits from a large and active community that regularly develops and publishes new models, techniques and resources. There are a variety of tutorials, forums and open source projects that can help you advance your machine learning projects. The Python community is known for its support and collaboration, which can make it easier to get started with Tensorflow Python.</p>
<h2>Libraries</h2>
<p>In many cases, and specifically for my NLP project, a knockout criterion for Tensorflow.js was the libraries. Tensorflow.js has far less libraries compared to Tensorflow Python. In my view, this is due to the following 3 points:</p>
<ol>
<li>Development status: Tensorflow Python exists for several years and has had a long development period. During this time, numerous extensions, modules and additional libraries have been developed specifically for Tensorflow Python. Tensorflow.js, on the other hand, is a comparatively newer technology and may still be in an earlier stage of development. Therefore, Tensorflow.js may have fewer libraries and extensions developed specifically for this platform.</li>
<li>Target platform: Tensorflow Python targets a wide range of platforms, including desktop computers, servers and mobile devices. As a result, there are a variety of specialised libraries and extensions for different use cases and hardware configurations. Tensorflow.js, on the other hand, aims to run models directly in the browser. Therefore, the functions and libraries of Tensorflow.js are optimised for the requirements of web applications and the limited resource availability in the browser.</li>
<li>Compatibility: Tensorflow.js is based on JavaScript, a language primarily used for web development. Although JavaScript has a large developer community and many existing libraries and frameworks, not all of them are directly compatible with Tensorflow.js. Therefore, not all available libraries and extensions for Tensorflow Python may also be available for Tensorflow.js.</li>
</ol>
<p>However, it is important to note that Tensorflow.js is constantly evolving and the library and available extensions may grow over time. The community around Tensorflow.js is working to expand the ecosystem and provide new libraries as well as tools to improve the functionality and expand the possibilities of Tensorflow.js.</p>
<h2>Platform support</h2>
<p>Written in C++, TensorFlow is a cross-platform library supported on various operating systems including Windows, macOS and Linux. It can be used to develop and run models on servers as well as desktop computers.</p>
<p>TensorFlow therefore supports a wider range of platforms than TensorFlow.js, as TensorFlow.js is mainly focused on JavaScript environments such as the browser and Node.js.</p>
<h2>Training large models</h2>
<p>TensorFlow can train and process very large models, whereas TensorFlow.js is limited to smaller models due to the performance limitations of JavaScript engines.</p>
<p>JavaScript engines are less powerful compared to specialised machine learning frameworks and hardware accelerators such as GPUs or TPUs. This means that TensorFlow.js is usually suitable for smaller models due to the limited processing power and memory of JavaScript engines.</p>
<p>Therefore, TensorFlow is recommended for projects where large models need to be trained or complex calculations need to be performed, while TensorFlow.js is better suited for applications that use smaller models and are intended to run in web browsers or JavaScript environments.</p>
<h2>Why TensorFlow.js in the browser?</h2>
<p>There is the issue of speed. Since you don&#8217;t have to send data to a remote server, classification is faster. In addition, you have direct access to sensors such as camera, microphone, GPS, etc.</p>
<p>In addition, data protection is an important issue in many countries. You can train and classify data on your own computer without having to send it to an external web server. This may be necessary to comply with data protection laws such as the DSGVO or if one does not want to share the data with third parties.</p>
<p>With one click, anyone in the world can access and use the application via a link without the need for a complex setup with servers and special hardware such as graphics cards.</p>
<p>Finally, with ML you should also keep an eye on the costs. You only have to pay for hosting the client. This is much cheaper than maintaining your own server permanently.</p>
<h2>Conclusion: Tensorflow.js vs. Tensorflow?</h2>
<p>Overall, the question Tensorflow.js vs. Tensorflow can be answered in a simplified way by saying that Tensorflow with Python is almost ALWAYS the better choice due to its broad acceptance and large <a href="https://www.tensorflow.org/community" target="_blank" rel="noopener">community</a>. Nevertheless, Tensorflow.js is becoming increasingly important and is predominantly used by developers who want to develop web applications with machine learning on the client side.</p>
<p>This means if you want to build apps that run in the web browser, TensorFlow.js is actually the better choice. However, if you want to build apps to run on a server or desktop computer, TensorFlow is the better option. Also, the Python version is much better suited if you want to work with powerful devices like GPUs.</p>
<p>The post <a href="https://nerd-corner.com/tensorflow-js-vs-tensorflow-python/">Tensorflow.js vs Tensorflow (Python)</a> appeared first on <a href="https://nerd-corner.com">Nerd Corner</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://nerd-corner.com/tensorflow-js-vs-tensorflow-python/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Enable TensorFlow GPU under Windows</title>
		<link>https://nerd-corner.com/enable-tensorflow-gpu-under-windows/</link>
					<comments>https://nerd-corner.com/enable-tensorflow-gpu-under-windows/#comments</comments>
		
		<dc:creator><![CDATA[Nerds]]></dc:creator>
		<pubDate>Tue, 09 May 2023 21:40:43 +0000</pubDate>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[Visual Studio]]></category>
		<category><![CDATA[Deep Learning]]></category>
		<category><![CDATA[GPU]]></category>
		<category><![CDATA[graphics card]]></category>
		<category><![CDATA[guide]]></category>
		<category><![CDATA[Machine Learning]]></category>
		<category><![CDATA[ML]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Step by step guide]]></category>
		<category><![CDATA[Tensorflow]]></category>
		<category><![CDATA[TensorFlow GPU]]></category>
		<category><![CDATA[Tensorflow guide]]></category>
		<category><![CDATA[user guide]]></category>
		<category><![CDATA[Windows]]></category>
		<guid isPermaLink="false">https://nerd-corner.com/de/?p=1376</guid>

					<description><![CDATA[<p>I am currently working on a hobby project where I want to translate German sentences into Bavarian using AI, for example on the website Dialektl.com. &#8230; </p>
<p>The post <a href="https://nerd-corner.com/enable-tensorflow-gpu-under-windows/">Enable TensorFlow GPU under Windows</a> appeared first on <a href="https://nerd-corner.com">Nerd Corner</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>I am currently working on a hobby project where I want to translate German sentences into Bavarian using AI, for example on the website <a href="http://dialektl.com" target="_blank" rel="noopener">Dialektl.com</a>. I am working with Tensorflow, an open source platform for machine learning and deep learning. Tensorflow is one of the most widely used libraries for Deep Learning because it offers a wide range of features and a very active developer community. For almost all machine learning models, the training process is extremely computationally expensive. To increase the speed of the training process, it is recommended to use the computer&#8217;s graphics card (GPU) instead of the processor (CPU).</p>
<p>My naive thought was that Tensorflow would automatically use the GPU. However, you first have to follow the step-by-step instructions below in order for Tensorflow to recognise and use the GPU at all.</p>
<p><em><strong>You might also be interested in:</strong> <a href="https://nerd-corner.com/tensorflow-js-vs-tensorflow-python/">Should I use Tensorflow.js or Tensorflow (Python)?</a></em></p>
<p><strong>DISCLAIMER: As of version 2.11, Tensorflow no longer supports GPUs under Windows! Either change the operating system or downgrade Tensorflow to version 2.10. In addition, a graphics card from NVIDIA is required!</strong></p>
<h3>1. Checking the desired CUDA and cuDNN versions</h3>
<p>First, you should find out which CUDA and cuDNN version you need for TensorFlow. The website lists all versions of Tensorflow and the desired CUDA versions or cuDNN versions:<a href="https://www.tensorflow.org/install/source_windows#gpu"> https://www.tensorflow.org/install/source_windows#gpu</a></p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1372 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/tensorflow_requirements.png" alt="tensorflow requirements" width="1120" height="562" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/tensorflow_requirements.png 1128w, https://nerd-corner.com/wp-content/uploads/2023/05/tensorflow_requirements-300x151.png 300w, https://nerd-corner.com/wp-content/uploads/2023/05/tensorflow_requirements-1024x514.png 1024w, https://nerd-corner.com/wp-content/uploads/2023/05/tensorflow_requirements-768x385.png 768w" sizes="auto, (max-width: 1120px) 100vw, 1120px" /></p>
<h2>2. Checking your own graphics card</h2>
<p>As already mentioned in the disclaimer, the graphics card must be from NVIDIA. On the website <a href="https://developer.nvidia.com/cuda-gpus" target="_blank" rel="noopener">https://developer.nvidia.com/cuda-gpus</a> you can search for your own GPU and check the &#8220;Compute Capability&#8221;. The minimum requirement for Tensorflow is a value of 3.5, but this is fulfilled by all current graphics cards.</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1371 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/cuda-architectures.png" alt="cuda architectures" width="1120" height="158" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/cuda-architectures.png 1129w, https://nerd-corner.com/wp-content/uploads/2023/05/cuda-architectures-300x42.png 300w, https://nerd-corner.com/wp-content/uploads/2023/05/cuda-architectures-1024x144.png 1024w, https://nerd-corner.com/wp-content/uploads/2023/05/cuda-architectures-768x108.png 768w" sizes="auto, (max-width: 1120px) 100vw, 1120px" /></p>
<h2>3. Installing the latest NVIDIA drivers</h2>
<p>To enable the GPU for TensorFlow, the latest NVIDIA drivers must be installed. Simply go to the NVIDIA website and download the latest driver for your graphics card.</p>
<h2>4. Install Visual Studio (optional)</h2>
<p>In TensorFlow, some parts of the library have been written in C++ to maximise performance. Therefore, installing Visual Studio can help improve the compatibility and performance of TensorFlow:<a style="font-size: 1.125rem; color: midnightblue; outline: 0px;" href="https://visualstudio.microsoft.com/de/vs/"> https://visualstudio.microsoft.com/de/vs/</a></p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1370 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/vs-studio-download.png" alt="Visual studio installer" width="690" height="304" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/vs-studio-download.png 694w, https://nerd-corner.com/wp-content/uploads/2023/05/vs-studio-download-300x132.png 300w" sizes="auto, (max-width: 690px) 100vw, 690px" /></p>
<p>It is sufficient to select individual components here. I have selected everything with C++ in &#8220;Compiler, Buildtools and Runtimes&#8221; and also MS Build, which in turn automatically installs a few more components. All in all, however, over 25 GB!</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1369 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/VS-studio-components.png" alt="choose components in visual studio" width="880" height="751" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/VS-studio-components.png 885w, https://nerd-corner.com/wp-content/uploads/2023/05/VS-studio-components-300x256.png 300w, https://nerd-corner.com/wp-content/uploads/2023/05/VS-studio-components-768x655.png 768w" sizes="auto, (max-width: 880px) 100vw, 880px" /></p>
<h2>5. Install CUDA Toolkit</h2>
<p>The CUDA Toolkit is a toolkit for CUDA application development provided by NVIDIA. TensorFlow requires CUDA to run on the GPU. Simply download and install the version of the CUDA toolkit requested in step 1 from the NVIDIA website: <a href="https://developer.nvidia.com/cuda-downloads">https://developer.nvidia.com/cuda-downloads</a></p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1368 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/Cuda-toolkit-download.png" alt="download cuda toolkit" width="1130" height="889" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/Cuda-toolkit-download.png 1135w, https://nerd-corner.com/wp-content/uploads/2023/05/Cuda-toolkit-download-300x236.png 300w, https://nerd-corner.com/wp-content/uploads/2023/05/Cuda-toolkit-download-1024x806.png 1024w, https://nerd-corner.com/wp-content/uploads/2023/05/Cuda-toolkit-download-768x604.png 768w" sizes="auto, (max-width: 1130px) 100vw, 1130px" /></p>
<h2>6. Installing the cuDNN libraries</h2>
<p>cuDNN is a library of deep learning primitives provided by NVIDIA. TensorFlow also requires cuDNN to run on the GPU. cuDNN is free, but you have to create an account as NVIDIA Developer: <a href="https://developer.nvidia.com/rdp/cudnn-download">https://developer.nvidia.com/rdp/cudnn-download</a></p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1367 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/cuDNN-download.png" alt="cuDNN download" width="1710" height="875" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/cuDNN-download.png 1717w, https://nerd-corner.com/wp-content/uploads/2023/05/cuDNN-download-300x154.png 300w, https://nerd-corner.com/wp-content/uploads/2023/05/cuDNN-download-1024x524.png 1024w, https://nerd-corner.com/wp-content/uploads/2023/05/cuDNN-download-768x393.png 768w, https://nerd-corner.com/wp-content/uploads/2023/05/cuDNN-download-1536x786.png 1536w" sizes="auto, (max-width: 1710px) 100vw, 1710px" /></p>
<p>After the download, the content must be unpacked. The content is moved to the &#8220;NVIDIA GPU Computing Toolkit&#8221; in the &#8220;Programs&#8221; folder. After moving, copy the file path of the &#8220;bin&#8221; folder.</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1366 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/unzip-cuDNN.png" alt="unzip cuDNN" width="670" height="226" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/unzip-cuDNN.png 673w, https://nerd-corner.com/wp-content/uploads/2023/05/unzip-cuDNN-300x101.png 300w" sizes="auto, (max-width: 670px) 100vw, 670px" /></p>
<h2>7. Set PATH variable</h2>
<p>Open the environment variables by simply typing the term into the Windows search. Then edit the system variable &#8220;path&#8221; and add a new entry with the file path of the &#8220;bin&#8221; folder from the previous step.</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-1365 zoooom" src="https://nerd-corner.com/wp-content/uploads/2023/05/path-variable.png" alt="Set path variable for tensorflow gpu" width="1220" height="203" srcset="https://nerd-corner.com/wp-content/uploads/2023/05/path-variable.png 1223w, https://nerd-corner.com/wp-content/uploads/2023/05/path-variable-300x50.png 300w, https://nerd-corner.com/wp-content/uploads/2023/05/path-variable-1024x170.png 1024w, https://nerd-corner.com/wp-content/uploads/2023/05/path-variable-768x127.png 768w" sizes="auto, (max-width: 1220px) 100vw, 1220px" /></p>
<h2>8. Creating a virtual environment</h2>
<p>It is recommended to install TensorFlow in a virtual environment to avoid conflicts with other Python packages. Ideally using Anaconda. Simply download and install here: <a href="https://www.anaconda.com/download">https://www.anaconda.com/download</a></p>
<p>Then start the Anaconda Navigator. Create a new environment under Environments &gt; Create. Then click on Home again and launch an existing Python IDE from here. Additional IDEs such as PyCharm should also be automatically displayed here after the download.</p>
<h2>9. Verify Tensorflow GPU support</h2>
<p>To check whether the TensorFlow GPU support was successfully detected, the Tensorflow package should first be installed in the Python IDE. But note that under Windows the GPU is only recognised up to version 2.10! In the following versions it is no longer recognised! Tensorflow 2.10 must therefore be installed.</p>
<p>Then run the following code to display a list of available GPUs:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python">import tensorflow as tf

tf.config.list_physical_devices('GPU')</pre>
<p>&nbsp;</p>
<p>The post <a href="https://nerd-corner.com/enable-tensorflow-gpu-under-windows/">Enable TensorFlow GPU under Windows</a> appeared first on <a href="https://nerd-corner.com">Nerd Corner</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://nerd-corner.com/enable-tensorflow-gpu-under-windows/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Import Swagger in Node TypeScript Project</title>
		<link>https://nerd-corner.com/import-swagger-in-node-typescript-project/</link>
					<comments>https://nerd-corner.com/import-swagger-in-node-typescript-project/#comments</comments>
		
		<dc:creator><![CDATA[Nerds]]></dc:creator>
		<pubDate>Wed, 26 Oct 2022 17:10:41 +0000</pubDate>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[angular]]></category>
		<category><![CDATA[Api]]></category>
		<category><![CDATA[Backend]]></category>
		<category><![CDATA[Backend Server]]></category>
		<category><![CDATA[Backend with Swagger]]></category>
		<category><![CDATA[CRUD]]></category>
		<category><![CDATA[Endpoints]]></category>
		<category><![CDATA[express.js]]></category>
		<category><![CDATA[Github]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[JSON]]></category>
		<category><![CDATA[Node]]></category>
		<category><![CDATA[Node with Swagger and Typescript]]></category>
		<category><![CDATA[Node.js]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[REST]]></category>
		<category><![CDATA[Server]]></category>
		<category><![CDATA[software]]></category>
		<category><![CDATA[software development]]></category>
		<category><![CDATA[Step by step guide]]></category>
		<category><![CDATA[Swagger]]></category>
		<category><![CDATA[swagger documentation]]></category>
		<category><![CDATA[Swagger express]]></category>
		<category><![CDATA[Swagger.json]]></category>
		<category><![CDATA[typescript]]></category>
		<guid isPermaLink="false">https://nerd-corner.com/de/?p=1287</guid>

					<description><![CDATA[<p>I recently coded a Node server with express.js in Typescript. This is a typical combination for backend development. It is especially beneficial if you already &#8230; </p>
<p>The post <a href="https://nerd-corner.com/import-swagger-in-node-typescript-project/">Import Swagger in Node TypeScript Project</a> appeared first on <a href="https://nerd-corner.com">Nerd Corner</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>I recently coded a Node server with express.js in Typescript. This is a typical combination for backend development. It is especially beneficial if you already have experience with frontend development in Typescript. In general I strongly recommend to use Typescript instead of Javascript for frontend as well as backend development. Typescript uses Javascript but is type based and therefore not as error prone!</p>
<p>Also, the focus should always be on clean code and it should be properly documented. Especially when a project gets bigger, proper documented work pays off. For example, to document REST endpoints it is recommended to use Swagger. The Swagger API documentation provides an overview of all endpoints and even the possibility to interact with them.</p>
<p><strong><em>This might also be interesting for you: </em></strong><a href="https://nerd-corner.com/how-to-build-a-custom-angular-reactive-svg-form-with-clickable-elements/" target="_blank" rel="noopener"><em>Angular Form with clickable SVG</em></a></p>
<h2>List of components</h2>
<ul>
<li>IDE (for example VS Code)</li>
<li>Node.js</li>
</ul>
<h2>Documentation with swagger</h2>
<p>Although Swagger is quite well known, I could not find a detailed implementation guide. Therefore I would like to explain 2 ways to implement Swagger in an existing node server. The first way is to add Swagger parameters directly to each endpoint. This is probably the faster variant, but can get messy depending on the number of endpoints. The second option is to create a &#8220;swagger.json&#8221; file that summarizes the parameters of the endpoints.</p>
<p>To use Swagger the following library and its type extension will be needed:</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 creates documentation of the endpoints:</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>These can be opened and you can see an example request and an example 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>The &#8220;Try it out&#8221; button can be used to interact directly with the endpoint.</p>
<h2>Swagger API documentation with params</h2>
<p>This variant is more recommended for smaller projects. First, these two Swagger libraries must be integrated into the project:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">npm install --save-dev @types/swagger-jsdoc 
npm install --save swagger-jsdoc</pre>
<p>The libraries are configured in app.ts or for non Typescript users app.js. Due to the configuration set here, the documentation is located in &#8220;/api-docs&#8221;:</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>The endpoint is described with the parameters as follows:</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 documentation with swagger.json</h2>
<p>To have a better overview and not to mix the documentation with the actual code, it is recommended to use one, or even more json files. When using TypeScript, the Swagger.json file must be placed in the 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>Again, the configuration is first defined in app.ts or, for non-Typescript users, in app.js. The domain path &#8220;/api-docs&#8221; is also selected here for the documentation:</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>Important note: To import JSON files into a Typescript project this must be allowed in tsconfig.json. Also, the JSON file must be located in the root directory.</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>The previous Swagger documentation with parameters would thus look like this in swagger.json:</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>Download files</h2>
<ul>
<li><a href="https://github.com/hanneslim/Node-with-swagger-params" target="_blank" rel="noopener">Github example projcet for swagger params</a></li>
<li><a href="https://github.com/hanneslim/node-with-swagger-json" target="_blank" rel="noopener">Github example project for swagger.json</a></li>
</ul>
<p>The post <a href="https://nerd-corner.com/import-swagger-in-node-typescript-project/">Import Swagger in Node TypeScript Project</a> appeared first on <a href="https://nerd-corner.com">Nerd Corner</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://nerd-corner.com/import-swagger-in-node-typescript-project/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
	</channel>
</rss>
