<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Patrice Ferlet</title>
    <description>The latest articles on Forem by Patrice Ferlet (@metal3d).</description>
    <link>https://forem.com/metal3d</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F664799%2F58944665-dba8-4a05-9ffc-ccc04eab4ba9.jpeg</url>
      <title>Forem: Patrice Ferlet</title>
      <link>https://forem.com/metal3d</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/metal3d"/>
    <language>en</language>
    <item>
      <title>Git worktree like a boss</title>
      <dc:creator>Patrice Ferlet</dc:creator>
      <pubDate>Wed, 18 Mar 2026 21:14:01 +0000</pubDate>
      <link>https://forem.com/metal3d/git-worktree-like-a-boss-2j1b</link>
      <guid>https://forem.com/metal3d/git-worktree-like-a-boss-2j1b</guid>
      <description>&lt;p&gt;If there’s one Git tool that few people know about, it’s “worktree.” Once you’ve mastered this tool, you’ll find it incredibly hard to do without it. But you have to use it the right way. Here’s a method I’ve been using for a very long time&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;In an empty directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Clone the repo into a hidden .bare folder&lt;/span&gt;
git clone &lt;span class="nt"&gt;--bare&lt;/span&gt; git@github.com:user/repo.git .bare

&lt;span class="c"&gt;# 2. Tell the root folder where the Git history is hidden&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"gitdir: ./.bare"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .git

&lt;span class="c"&gt;# 3. Fix the fetch configuration to see all remote branches&lt;/span&gt;
git config remote.origin.fetch &lt;span class="s2"&gt;"+refs/heads/*:refs/remotes/origin/*"&lt;/span&gt;

&lt;span class="c"&gt;# 4. Create your first worktree (the main branch)&lt;/span&gt;
git worktree add main
git worktree add &amp;lt;name of the branch&amp;gt;

&lt;span class="c"&gt;# Each folder correspond to a branch, everything is centralized&lt;/span&gt;
&lt;span class="c"&gt;# Read the doc... or follow the article&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;I provide you a script you can put in your &lt;code&gt;PATH&lt;/code&gt; to ease the process. Take a look at the end of the article.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  First of all, what is a “worktree”?
&lt;/h2&gt;

&lt;p&gt;In the fast-paced world of software development, &lt;strong&gt;context switching&lt;/strong&gt; is often an unavoidable reality. You’ve likely been there: you’re deep into a complex feature branch when a critical production bug suddenly demands your immediate attention.&lt;/p&gt;

&lt;p&gt;Or, sometimes, you have to manage 2 branches at the same time because you are preparing a big migration, refactoring or something complex that will be merged. So you need direct to several "working trees".&lt;/p&gt;

&lt;p&gt;Traditionally, developers have relied on git stash to clear their workspace or simply cloned the entire repository into a separate folder. However, these methods can be clunky, error-prone, or heavy on disk space. This is where Git Worktree comes in.&lt;/p&gt;

&lt;p&gt;Introduced in version 2.5, git worktree allows you to have &lt;strong&gt;multiple working directories attached to a single repository&lt;/strong&gt;. Instead of having just one "checked out" branch at a time, you can have several branches checked out simultaneously in different folders. All these folders share the same underlying .git history, meaning you don't need to re-download data or deal with multiple independent clones.&lt;/p&gt;

&lt;p&gt;You will understand why it's so powerful as soon as you will follow my method 😄&lt;/p&gt;

&lt;h2&gt;
  
  
  The bad way
&lt;/h2&gt;

&lt;p&gt;I often see articles that explains you can do a worktree using &lt;code&gt;"../name-of-worktree"&lt;/code&gt;. That works and I can understand why it's the "common way to do". The developper has already cloned the repository and it does this to quickly have a second workspace for a branch.&lt;/p&gt;

&lt;p&gt;But... it's not what I advice.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to do better
&lt;/h2&gt;

&lt;p&gt;What I do for a long time now is to use a "bare" clone. This will not clone the repository but an history of the répository. So, after the below command, you are not ready to work. Let's take an example with my project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/Projects/Katenary
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/Projects/Katenary
git clone &lt;span class="nt"&gt;--bare&lt;/span&gt; git@repo.katenary.io:Katenary/katenary.git .bare
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;.bare&lt;/code&gt; directory, you'll see something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.bare
├── config
├── description
├── HEAD
├── hooks
├── info
├── objects
├── packed-refs
└── refs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you use the Bare Clone method, you want your root project folder to stay clean. The goal is to have a hidden administrative folder and then your various worktrees (branches) as sibling folders.&lt;/p&gt;

&lt;p&gt;Here is the "magic trick" to make the root folder behave like a Git repository without actually cluttering it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"gitdir: ./.bare"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, if you are inside your project folder but outside a worktree, Git won't know where the history is. By adding this one-line .git file, you are telling your terminal: "Treat this entire directory as part of the Git project located in ./bare."&lt;/p&gt;

&lt;p&gt;This allows you to run git worktree commands from the root of your project instead of having to cd into the .bare folder every time. It’s the glue that holds the "Pro" structure together.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;And now, ladies and gentlemen !&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# I will explain this line in a few moments&lt;/span&gt;
git config remote.origin.fetch &lt;span class="s2"&gt;"+refs/heads/*:refs/remotes/origin/*"&lt;/span&gt;

&lt;span class="c"&gt;# let's start to work on different branches&lt;/span&gt;
git worktree add main
git worktree add feature/better-depends-on
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── feature
│   └── better-depends-on
└── main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I now see 2 folders, &lt;code&gt;main&lt;/code&gt; and &lt;code&gt;better-dependencies&lt;/code&gt;. Each directory contains the content of the desired branch. I can work in both of them, push, pull, and so on.&lt;/p&gt;

&lt;h1&gt;
  
  
  The "Blind Clone" Trap: Why Your Fetch is Empty
&lt;/h1&gt;

&lt;p&gt;If you've followed the steps so far, you might run into a confusing situation. You try to add a worktree &lt;strong&gt;for an existing remote branch&lt;/strong&gt;, but Git instead creates a new, empty branch starting from main.&lt;/p&gt;

&lt;p&gt;Look at your terminal. If git fetch only shows this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git fetch
&lt;span class="k"&gt;*&lt;/span&gt; branch            HEAD       -&amp;gt; FETCH_HEAD
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;You are officially "Blind." &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Your local repository knows the code exists, but it has no idea that other branches exist on the server&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When you use git clone --bare, Git assumes you want a backup or a server-side mirror, not a workspace for active development. To save resources, it doesn't set up "Remote Tracking." It only fetches the default branch (main or master).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Fix: Mapping the Remote&lt;/strong&gt;&lt;br&gt;
To turn your bare repository into a fully-aware development hub, you need to tell Git: "I want to see everything that happens on the origin server."&lt;/p&gt;

&lt;p&gt;Run this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config remote.origin.fetch &lt;span class="s2"&gt;"+refs/heads/*:refs/remotes/origin/*"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, run git fetch --all again. You should see a list of all your teammates' branches appearing in your terminal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wait... it's only like having cloned twice, right ?
&lt;/h2&gt;

&lt;p&gt;At first glance, you might think: "Why go through all this trouble? If I need to work on two branches at once, I’ll just clone the repo into two different folders and be done with it."&lt;/p&gt;

&lt;p&gt;Technically, you could. But comparing a Git Worktree setup to multiple clones is like comparing a synchronized multi-room audio system to buying two separate stereos and trying to press "Play" at the exact same millisecond.&lt;/p&gt;

&lt;p&gt;Here is why the "Multiple Clones" method is actually a trap:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Storage Tax
&lt;/h3&gt;

&lt;p&gt;If your repository is 500MB, two clones take up 1GB. With Worktrees, you have one .git folder (the "source of truth"). Each additional worktree only adds the weight of the actual text files you are editing. Your disk will thank you.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The "Single Source of Truth" Problem
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;This is the real game-changer&lt;/strong&gt;. In a multi-clone setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you git fetch in Clone A, Clone B knows nothing about it.&lt;/li&gt;
&lt;li&gt;If you create a local branch in Clone A, you have to push it to the server (or a local remote) just to see it in Clone B.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With Worktrees, &lt;strong&gt;they all share the same internal database&lt;/strong&gt;. If you fetch in your main worktree, the new branches are instantly available in your feature-xyz worktree. It’s one brain, many bodies.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Hooks and Configs
&lt;/h3&gt;

&lt;p&gt;Ever spent 20 minutes setting up a specific Git hook or a local config, only to realize you have to do it all over again because you're in a different clone?&lt;/p&gt;

&lt;p&gt;Since Worktrees share the same .git directory, &lt;strong&gt;your hooks, aliases, and local configurations are universal&lt;/strong&gt; across all your working folders.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. The "Safety Valve"
&lt;/h3&gt;

&lt;p&gt;Git is smart: it won't let you check out the same branch in two different worktrees at the same time. This prevents you from accidentally overwriting your own work or creating massive HEAD conflicts—a protection you don't have if you're just using multiple clones.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In short: Multiple clones are isolated silos. Worktrees are a unified workspace.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The "pro" setup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Clone the repo into a hidden .bare folder&lt;/span&gt;
git clone &lt;span class="nt"&gt;--bare&lt;/span&gt; git@github.com:user/repo.git .bare

&lt;span class="c"&gt;# 2. Tell the root folder where the Git history is hidden&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"gitdir: ./.bare"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .git

&lt;span class="c"&gt;# 3. Fix the fetch configuration to see all remote branches&lt;/span&gt;
git config remote.origin.fetch &lt;span class="s2"&gt;"+refs/heads/*:refs/remotes/origin/*"&lt;/span&gt;

&lt;span class="c"&gt;# 4. Create your first worktree (the main branch)&lt;/span&gt;
git worktree add main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now, a if you're in the "main" folder, an urgent hotfix must be done.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# inside the "main" directory&lt;/span&gt;
git worktree add ../hotfix
&lt;span class="nb"&gt;cd&lt;/span&gt; ../hotfix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This create a branch &lt;strong&gt;and&lt;/strong&gt; the folder where you can work without having to stop working on "main" branch.&lt;/p&gt;

&lt;p&gt;To be explicit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git worktree add &amp;lt;where to create the tree&amp;gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;-b&lt;/span&gt; name of the branch to create] &lt;span class="o"&gt;[&lt;/span&gt;branch name reference]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, I need to create "feature/fix-db" from "feature/improve-db". I'm in the root workspace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git worktree add improve-db &lt;span class="nt"&gt;-b&lt;/span&gt; feeature/fix-db feature/improve-db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The directory is created, I can now go to "&lt;code&gt;./improve-db&lt;/code&gt;" and do my job.&lt;/p&gt;

&lt;p&gt;Of course, you can use subdirectories (place all features in a &lt;code&gt;features&lt;/code&gt; directory) and git worktree create them by default if you use slashes in your branchnames. I mean that &lt;code&gt;git worktree add features/A&lt;/code&gt; creates &lt;code&gt;features/A&lt;/code&gt; directory. And the branch is named this way, and ready to be pushed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some traps
&lt;/h2&gt;

&lt;p&gt;Removing a worktree with "rm -rf" doesn't really remove the worktree (no panic, it doesn't remove remote branches). Then if you want to get it back, you will think that "git worktree add" one more time will respawn the directory. But, an error will appear saying that the worktree already exists.&lt;/p&gt;

&lt;p&gt;Is this is the case, you can do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git worktree list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will see "prunable" worktrees. You can then prune them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git worktree prune
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, you can use the &lt;code&gt;add&lt;/code&gt; subcommand.&lt;/p&gt;

&lt;p&gt;You can protect worktrees to not being pruned with the "lock" subcommand, and of course "unlock" it at any time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: Give it a Week
&lt;/h2&gt;

&lt;p&gt;I’ll be honest: if you’ve been using git stash and multiple clones for years, the Git Worktree workflow might feel like "over-engineering" a problem you don't think you have. It’s hard to see the immediate value when you're used to the friction of traditional context switching.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;But here is my challenge to you: Try it for a week.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Force yourself to use a Bare Clone setup on your next big project. The moment you realize you can run a heavy test suite in one folder while simultaneously hotfixing a bug in another—without ever losing your place or your "flow"—it will click.&lt;/p&gt;

&lt;p&gt;You’ll stop thinking about "switching branches" and start thinking about "parallel workspaces." Your terminal will be cleaner, your disk space will be optimized, and your brain will thank you for not having to remember what you stashed three hours ago.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Once you go Worktree, you rarely go back.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A simple script
&lt;/h2&gt;

&lt;p&gt;I placed this in my &lt;code&gt;~/.local/bin&lt;/code&gt; folder named &lt;code&gt;wtree&lt;/code&gt; (don't forget to &lt;code&gt;chmod +x wtree&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="c"&gt;# Usage: wtree &amp;lt;git url&amp;gt; (in an empty directory)&lt;/span&gt;

&lt;span class="nv"&gt;REPO_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;span class="nv"&gt;SCRIPT_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$REPO_URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"❌ Error: Missing repository URL."&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Usage: &lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt; &amp;lt;repo-url&amp;gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# 1. Check if the current directory is empty&lt;/span&gt;
&lt;span class="c"&gt;# We allow the script itself to be present&lt;/span&gt;
&lt;span class="nv"&gt;FILES_IN_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SCRIPT_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FILES_IN_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"❌ Error: Current directory is not empty!"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"To keep your worktree setup clean, please run this in a fresh folder."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🚀 Initializing Pro Git Worktree setup..."&lt;/span&gt;

&lt;span class="c"&gt;# 2. Clone as bare into a hidden directory&lt;/span&gt;
git clone &lt;span class="nt"&gt;--bare&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$REPO_URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; .bare

&lt;span class="c"&gt;# 3. Link the root to the bare repository&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"gitdir: ./.bare"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .git

&lt;span class="c"&gt;# 4. Enable remote tracking (The "Remote Awareness" fix)&lt;/span&gt;
&lt;span class="c"&gt;# This ensures 'git fetch' sees all branches from the server&lt;/span&gt;
git config remote.origin.fetch &lt;span class="s2"&gt;"+refs/heads/*:refs/remotes/origin/*"&lt;/span&gt;

&lt;span class="c"&gt;# 5. Fetch everything&lt;/span&gt;
git fetch &lt;span class="nt"&gt;--all&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ Setup complete!"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Next step: git worktree add main"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>git</category>
      <category>devops</category>
      <category>development</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Patrice Ferlet</dc:creator>
      <pubDate>Thu, 23 Jan 2025 21:03:51 +0000</pubDate>
      <link>https://forem.com/metal3d/-52mi</link>
      <guid>https://forem.com/metal3d/-52mi</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/metal3d" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F664799%2F58944665-dba8-4a05-9ffc-ccc04eab4ba9.jpeg" alt="metal3d"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/metal3d/simple-backup-service-with-rclone-restic-and-systemd-on-webdav-or-another-storage-3dlb" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Simple backup service with rclone, restic and systemd on WebDAV (or another storage)&lt;/h2&gt;
      &lt;h3&gt;Patrice Ferlet ・ Jan 23&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#backup&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#systemd&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#rclone&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#restic&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>linux</category>
      <category>devops</category>
      <category>automation</category>
    </item>
    <item>
      <title>Simple backup service with rclone, restic and systemd on WebDAV (or another storage)</title>
      <dc:creator>Patrice Ferlet</dc:creator>
      <pubDate>Thu, 23 Jan 2025 21:03:31 +0000</pubDate>
      <link>https://forem.com/metal3d/simple-backup-service-with-rclone-restic-and-systemd-on-webdav-or-another-storage-3dlb</link>
      <guid>https://forem.com/metal3d/simple-backup-service-with-rclone-restic-and-systemd-on-webdav-or-another-storage-3dlb</guid>
      <description>&lt;p&gt;Let me show you how I back up my computers with &lt;a href="https://restic.net" rel="noopener noreferrer"&gt;restic&lt;/a&gt; and &lt;a href="https://rclone.org" rel="noopener noreferrer"&gt;rclone&lt;/a&gt; using simple systemd configuration.&lt;/p&gt;

&lt;p&gt;On &lt;a href="https://dev.to/metal3d/nextcloud-on-raspberry-pi-fedora-podman-quadlets-1k49"&gt;my previous article&lt;/a&gt;, I presented how I was able to have a nice NAS (storage) with Nextcloud. It has got the good idea to propose a WebDAV endpoint that can be mounted on our Linux computers, or on Windows with the client.&lt;/p&gt;

&lt;p&gt;By chance, Gnome provides an 'Online Account' option to automatically mount my remote hard drive on Nautilus, the file manager, and to synchronize contacts, calendars, etc.&lt;/p&gt;

&lt;p&gt;But, I wanted to have a “real” backup service too.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Because a "real" backup system is not only to send files on a remote drive. It is a complete snapshot management, with incremental checks, compression, security...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This signifies that I intend to create backups for certain directories, excluding others, to retrieve files from a period of 7 days, 1 week, 1 month, or even 1 year ago.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is what we call an "incremental" backup system.&lt;/p&gt;

&lt;p&gt;☝️ This article proposes a backup configuration for a "user" on Linux but you can adapt the configuration to backup a server or your system as an administrator. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  💡 What we will see in this article
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What are Restic and Rclone commands&lt;/li&gt;
&lt;li&gt;How to back up some directories and restore them&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How to automate the backup with &lt;code&gt;systemd&lt;/code&gt; for a Linux user&lt;/strong&gt; (or for an admin account)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create your configuration to connect a storage&lt;/span&gt;
&lt;span class="c"&gt;# if restic doesn't provide the connector (e.g. for WebDav).&lt;/span&gt;
&lt;span class="c"&gt;# Name your connection, for example "nas"&lt;/span&gt;
rclone config

&lt;span class="c"&gt;# initialize the backup storage in a directory,&lt;/span&gt;
&lt;span class="c"&gt;# e.g. Backups&lt;/span&gt;
restic &lt;span class="nt"&gt;-r&lt;/span&gt; rclone:nas:Backups init

&lt;span class="c"&gt;# Create a simple backup one shot&lt;/span&gt;
restic &lt;span class="nt"&gt;-r&lt;/span&gt; rclone:nas:Backups backup &lt;span class="nv"&gt;$HOME&lt;/span&gt;/Images &lt;span class="nt"&gt;--verbose&lt;/span&gt;

&lt;span class="c"&gt;# Forget some backups:&lt;/span&gt;
restic &lt;span class="nt"&gt;-r&lt;/span&gt; rclone:nas:Backups &lt;span class="se"&gt;\&lt;/span&gt;
    forget &lt;span class="nt"&gt;--prune&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--keep-daily&lt;/span&gt; 7 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--keep-weekly&lt;/span&gt; 4 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--keep-monthly&lt;/span&gt; 6 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--keep-yearly&lt;/span&gt; 1

&lt;span class="c"&gt;# This is to make management easiers:&lt;/span&gt;
&lt;span class="c"&gt;# Create files to list directories to backup and what to exclude.&lt;/span&gt;
&lt;span class="c"&gt;# It support "patterns"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/Documents"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.config/backup.list
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"*.tmp"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.config/backup.exclude


&lt;span class="c"&gt;# example of backup command:&lt;/span&gt;
restic &lt;span class="nt"&gt;-r&lt;/span&gt; rclone:nas:Backups backup &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--files-from&lt;/span&gt; ~/.config/backup.list &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--exclude-file&lt;/span&gt; ~/.config/backup.exclude

&lt;span class="c"&gt;# Scroll to the bottom of the article to find systemd&lt;/span&gt;
&lt;span class="c"&gt;# service and timers&lt;/span&gt;

&lt;span class="c"&gt;# Done !&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please read the article to see how to restore a snapshot, to simplify the management, …&lt;/p&gt;

&lt;h2&gt;
  
  
  Incremental backup and GFS Backups?
&lt;/h2&gt;

&lt;p&gt;An incremental backup is a method of backing up solely the modifications from the most recent backup to conserve space.&lt;/p&gt;

&lt;p&gt;Moreover, we can do a &lt;strong&gt;GFS backup&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;GFS? What is this?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;GFS means " Grandfather-Father-Son Backup", this is a method to make daily backup every day but:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All backups older than a certain date, such as 7 days old, are removed.&lt;/li&gt;
&lt;li&gt;Keep a backup each week for four weeks.&lt;/li&gt;
&lt;li&gt;Keep a backup every month for six months.&lt;/li&gt;
&lt;li&gt;Keep one backup per year, per year.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;This facilitates the storage of substantial data in a timely manner while utilizing minimal space.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It's a very common method, I use it at work to backup dozen of servers..&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;🎉 ❤️ And yes, Restic can do all of this!&lt;/p&gt;

&lt;h2&gt;
  
  
  Restic
&lt;/h2&gt;

&lt;p&gt;Restic is a backup command line. It can use many local or remote endpoints like SFTP, Samba, or S3 protocol.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Restic can make backups, and make snapshot restoration, cleanup, &lt;strong&gt;mounting snapshots to navigate into&lt;/strong&gt;, secure data by encryption, and many more! 👏&lt;/p&gt;

&lt;p&gt;Unfortunatly, Restic doesn't support WebDav, but there is a solution: using rclone adapter.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Restic will create a “repository”. It's a remote or local path where Restic will keep snapshots, data, and status.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5gn30xik337zcahlnzm7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5gn30xik337zcahlnzm7.png" alt="The Restic repository on my NAS" width="749" height="121"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will initialize it in a while.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rclone
&lt;/h2&gt;

&lt;p&gt;Rclone is a command line to send / synchronize and manage local or remote directories. It is compatible with &lt;a href="https://rclone.org/overview/#features" rel="noopener noreferrer"&gt;a huge number of providers&lt;/a&gt; like Google Drive, Amazon S3, S3 compatible, Samba, Proton Drive, Dropbox… and &lt;strong&gt;WebDAV&lt;/strong&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Rclone configuration
&lt;/h2&gt;

&lt;p&gt;OK, in my case, I need to contact my local WebDAV (&lt;code&gt;nas.home&lt;/code&gt; is a domain locally assigned by my router, an Orange Livebox)&lt;/p&gt;

&lt;p&gt;On my Nextcloud account, browsing my files, I can see the WebDAV URL in the settings:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F34t5y3221hcnhnbdhx1a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F34t5y3221hcnhnbdhx1a.png" alt="Where to find WebDAV URL" width="694" height="700"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In a terminal, I only do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rclone config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I needed to find the WebDAV option, then Nextcloud, then give my URL, user and password.&lt;/p&gt;

&lt;p&gt;Another way is to use one command line to set it up all:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# change the url, user and password of course&lt;/span&gt;
rclone config create nas &lt;span class="se"&gt;\&lt;/span&gt;
webdav &lt;span class="se"&gt;\&lt;/span&gt;
url https://nas.home/remote.php/dav/files/Patrice &lt;span class="se"&gt;\&lt;/span&gt;
user Patrice &lt;span class="se"&gt;\&lt;/span&gt;
vendor nextcloud &lt;span class="se"&gt;\&lt;/span&gt;
pass &lt;span class="si"&gt;$(&lt;/span&gt;rclone obscure &lt;span class="s1"&gt;'TYPE YOUR NextCloud Password here'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ The &lt;code&gt;obscure&lt;/code&gt; command is not a encrption method. It "obscures" the password. It is only made to avoid "direct reading". Rclone can read the original string.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To check if it works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# liste directories only&lt;/span&gt;
rclone lsd nas:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it works, you may continue&lt;/p&gt;

&lt;h2&gt;
  
  
  Restic, initialization
&lt;/h2&gt;

&lt;p&gt;We have to initialize a Restic repository. This will create the data and configuration on the remote storage.&lt;/p&gt;

&lt;p&gt;So, we created a &lt;code&gt;nas:&lt;/code&gt; Rclone configuration to access our WebDAV endpoint; let's use it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;restic &lt;span class="nt"&gt;-r&lt;/span&gt; rclone:nas:Backups init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command will ask you to provide a password. &lt;strong&gt;This password is used to encrypt your backup!&lt;/strong&gt; This is not your WebDAV password, this is a secret to avoid others to read your backups.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ If you lose this secret, you will never be able to restore snapshots later!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, Restic is ready.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test Restic backup
&lt;/h2&gt;

&lt;p&gt;First, create a simple directory where we will store a simple file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; /tmp/data
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"hello"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/hello.txt
&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/data/file.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let backup this directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;restic &lt;span class="nt"&gt;-r&lt;/span&gt; rclone:nas:Backups backup /tmp/data
&lt;span class="c"&gt;# it will ask you the repository password&lt;/span&gt;

&lt;span class="c"&gt;# output&lt;/span&gt;
repository 52592651 opened &lt;span class="o"&gt;(&lt;/span&gt;version 2, compression level auto&lt;span class="o"&gt;)&lt;/span&gt;
no parent snapshot found, will &lt;span class="nb"&gt;read &lt;/span&gt;all files
&lt;span class="o"&gt;[&lt;/span&gt;0:01] 100.00%  5 / 5 index files loaded

Files:           2 new,     0 changed,     0 unmodified
Dirs:            2 new,     0 changed,     0 unmodified
Added to the repository: 1.902 KiB &lt;span class="o"&gt;(&lt;/span&gt;1.361 KiB stored&lt;span class="o"&gt;)&lt;/span&gt;

processed 2 files, 38 B &lt;span class="k"&gt;in &lt;/span&gt;0:03
snapshot cb53a1b9 saved
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The directory is now snapshot in the &lt;code&gt;Backups&lt;/code&gt; repository.&lt;/p&gt;

&lt;p&gt;Let's take a look:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;restic &lt;span class="nt"&gt;-r&lt;/span&gt; rclone:nas:Backups snapshots
&lt;span class="c"&gt;# output&lt;/span&gt;
repository 52592651 opened &lt;span class="o"&gt;(&lt;/span&gt;version 2, compression level auto&lt;span class="o"&gt;)&lt;/span&gt;
ID        Time                 Host            Tags        Paths                           Size
&lt;span class="nt"&gt;--------------------------------------------------------------------&lt;/span&gt;
cb53a1b9  2025-01-22 22:13:21  patrice-laptop              /tmp/data                       38 B
&lt;span class="nt"&gt;--------------------------------------------------------------------&lt;/span&gt;
1 snapshots
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Actually there are many other snapshots in my output as I have already made several backups)&lt;/p&gt;

&lt;p&gt;OK, so the snapshot is here. Let's break our local directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# oops, accidentally removed :)&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; /tmp/data/hello.txt

&lt;span class="c"&gt;# remove the date, write "broken"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"broken"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/data/file.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How to recover?&lt;/p&gt;

&lt;p&gt;The simplest is to use the snapshot ID and specify where to restore:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;restic &lt;span class="nt"&gt;-r&lt;/span&gt; rclone:nas:Backups restore cb53a1b9 &lt;span class="nt"&gt;--target&lt;/span&gt; /
&lt;span class="c"&gt;# output&lt;/span&gt;
repository 52592651 opened &lt;span class="o"&gt;(&lt;/span&gt;version 2, compression level auto&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;0:03] 100.00%  7 / 7 index files loaded
restoring snapshot cb53a1b9 of &lt;span class="o"&gt;[&lt;/span&gt;/tmp/data] at 2025-01-22 22:13:21.433737756 +0100 CET by metal3d@patrice-laptop to /tmp/data
Summary: Restored 4 files/dirs &lt;span class="o"&gt;(&lt;/span&gt;38 B&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0:04
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Why "&lt;code&gt;/&lt;/code&gt;" and not "&lt;code&gt;/tmp/data&lt;/code&gt;"? Because the full path is stored in restic.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ℹ️ You can use &lt;code&gt;latest&lt;/code&gt; instead of the &lt;code&gt;cb53a1b9&lt;/code&gt; ID (which is different for you). &lt;code&gt;latest&lt;/code&gt; means "the latest snapshot". Using the ID is interesting to get an older snapshot, for example if the latest snapshot contains a broken file.&lt;/p&gt;

&lt;p&gt;Let it restoring, and…&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;tree /tmp/data
/tmp/data/
├── file.txt
└── hello.txt

&lt;span class="c"&gt;# back!&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; /tmp/hello.txt
hello

&lt;span class="c"&gt;# not broken :)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; /tmp/file.txt
mer. 22 janv. 2025 22:12:47 CET
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you can create other snapshots, it “incrementally” adds files. &lt;/li&gt;
&lt;li&gt;if nothing changed in your local directory, so the snapshot will be “empty”, it's normal&lt;/li&gt;
&lt;li&gt;only the newest files and changed content will be stored in the following snapshots — it saves space!&lt;/li&gt;
&lt;li&gt;you can recover snapshots at a certain date&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;But... We need to cleanup very old backups&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To clean backups, the &lt;code&gt;forget&lt;/code&gt; command is simple to use.&lt;/p&gt;

&lt;p&gt;I want to keep 7 days of backups, but also 1 backup per week for 4 weeks, and 1 backup per month for 6 months, and finally one backup per year for one year…&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;restic &lt;span class="nt"&gt;-r&lt;/span&gt; rclone:nas:Backups forget &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--keep-daily&lt;/span&gt; 7 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--keep-weekly&lt;/span&gt; 4 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--keep-monthly&lt;/span&gt; 6 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--keep-yearly&lt;/span&gt; 1 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--prune&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OK, there are a lot of information. And there are plenty of options and commands in &lt;code&gt;restic&lt;/code&gt;. Read the doc 😉&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚠️ Crucial:
&lt;/h2&gt;

&lt;p&gt;If you never heard about incremental backup, you may wonder if a large directory will consume all your CPU and bandwidth every day.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;No&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is why a “real” backup system is very intriguing. The first backup is a bit long as there is no track of the files you're backing up.&lt;/p&gt;

&lt;p&gt;The next backup will only take into account the unknown and changed files.&lt;/p&gt;

&lt;p&gt;So then:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you can revert files as if they were versioned&lt;/li&gt;
&lt;li&gt;and the next backups will be faster, really faster&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Some nice features
&lt;/h2&gt;

&lt;p&gt;Something very interesting when you want to restore a file, is that you can use &lt;code&gt;--include&lt;/code&gt; to only get one file or directory. Moreover, there is a &lt;code&gt;find&lt;/code&gt; subcommand to find a file.&lt;/p&gt;

&lt;p&gt;But, another way is to “mount” the snapshots on your host.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Yes, you can navigate the snapshots&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Mounting snapshots
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;It only works on Linux... OK for me 😉&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you use your WebDAV drive, or the web interface of Nextcloud, the snapshots are encrypted and not usable:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh4m3fgj7xl63zwap35u3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh4m3fgj7xl63zwap35u3.png" alt="On WebDAV, snapshots are not browsable" width="504" height="284"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But, using &lt;code&gt;restic mount&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; /tmp/restic
restic &lt;span class="nt"&gt;-r&lt;/span&gt; rclone:nas:Backup mount /tmp/restic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can now browse the snapshots:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwbicnwel41zeplegj6bm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwbicnwel41zeplegj6bm.png" alt="Restic mount snapshots, it's now OK" width="504" height="284"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is very comfortable to be able to navigate snapshots, at a certain date, check the content of a file and to be able to copy and paste it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Now, automate with Systemd
&lt;/h2&gt;

&lt;p&gt;There are several new things that are interesting to know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you can set the Restic password as &lt;code&gt;RESTIC_PASSWORD&lt;/code&gt; environment variable&lt;/li&gt;
&lt;li&gt;you can set the Restic repository name in the &lt;code&gt;RESTIC_REPOSITORY&lt;/code&gt; environment variable&lt;/li&gt;
&lt;li&gt;you can provide several directories to snapshot at once&lt;/li&gt;
&lt;li&gt;the list can be set in the command line, or inside a text file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, with this information, we can create a user service and associated timer — this to automate backup.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 If your want to use Restic for a server or to backup a root system, so you only need to change the paths to &lt;code&gt;/etc/systemd/system&lt;/code&gt; to save files, and do not use &lt;code&gt;--user&lt;/code&gt; option.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The service in “&lt;code&gt;~/.config/systemd/user/backup.service&lt;/code&gt;”:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Backup service to NextCloud&lt;/span&gt;

&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;oneshot&lt;/span&gt;
&lt;span class="py"&gt;Nice&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;19&lt;/span&gt;
&lt;span class="c"&gt;# environment variables to simplify commands
&lt;/span&gt;&lt;span class="py"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;RESTIC_PASSWORD=YOUR PASSWORD HERE&lt;/span&gt;
&lt;span class="py"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;RESTIC_REPOSITORY=rclone:nas:Backups&lt;/span&gt;

&lt;span class="c"&gt;# Call sequentially
&lt;/span&gt;&lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/usr/bin/restic backup --files-from %h/.config/backup.list --exclude-file %h/.config/backup.exclude --verbose&lt;/span&gt;
&lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/usr/bin/restic forget --prune --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --keep-yearly 1&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Type=oneshot&lt;/code&gt; is important:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you can start several commands sequentially&lt;/li&gt;
&lt;li&gt;it will not restart as other services that are expected to be continuously “alive”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you can see, I'm using files to list directories and exclusion.&lt;/p&gt;

&lt;p&gt;Let's create them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# for example, backup Document folder&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/Documents &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.config/backup.list
&lt;span class="c"&gt;# and exclude all .tmp files&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'*.tmp'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.config/backup.exclude
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;You can edit the file with a text editor, of course.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then, create the timer in &lt;code&gt;~/.config/systemd/user/backup.timer&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Planify the Restic backup service&lt;/span&gt;

&lt;span class="nn"&gt;[Timer]&lt;/span&gt;
&lt;span class="c"&gt;# you can set specific date / time
# OnCalendar=*-*-* 02:00:00
# or simply leave the system default which
# is 3:00 am
&lt;/span&gt;&lt;span class="py"&gt;OnCalendar&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;daily&lt;/span&gt;
&lt;span class="py"&gt;Persistent&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;timers.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;It's important to have the same basename for the timer and the service.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;OK, let's go:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl &lt;span class="nt"&gt;--user&lt;/span&gt; daemon-reload
systemctl &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; backup.timer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And voilà! Every day, a snapshot will be made for all directories you've listed in the list file. And it avoids all directory or patterns that you've listed in the exclude file.&lt;/p&gt;

&lt;p&gt;To check if the backup works, you can, of course, launch the service right now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Warning, if the backup is huge, this command will&lt;/span&gt;
&lt;span class="c"&gt;# stay alive for a long time. Test with a tiny directory to backup.&lt;/span&gt;
systemctl &lt;span class="nt"&gt;--user&lt;/span&gt; start backup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Restic and Rclone are both powerful. Typing a few command lines to manage backup is much more comfortable on Unix/Like and Linux systems (it works on FreeBSD, OpenBSD, macOS…)&lt;/p&gt;

&lt;p&gt;It can work on Windows computers. But I never tried (I don't have any Windows machine at home)&lt;/p&gt;

&lt;p&gt;Anyway, one more time &lt;code&gt;systemd&lt;/code&gt; is IMHO one of the best things that happened to Linux. Whatever others think, I love it.&lt;/p&gt;

&lt;p&gt;And, again, using a terminal command that does one thing, but does it well, is a power in our hands.&lt;/p&gt;

</description>
      <category>backup</category>
      <category>systemd</category>
      <category>rclone</category>
      <category>restic</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Patrice Ferlet</dc:creator>
      <pubDate>Sun, 19 Jan 2025 20:51:18 +0000</pubDate>
      <link>https://forem.com/metal3d/-293g</link>
      <guid>https://forem.com/metal3d/-293g</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/metal3d" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F664799%2F58944665-dba8-4a05-9ffc-ccc04eab4ba9.jpeg" alt="metal3d"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/metal3d/how-to-resolve-the-dlopen-problem-with-nvidia-and-pytorch-or-tensorflow-inside-a-virtual-env-181e" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;How to resolve the dlopen problem with Nvidia and PyTorch or Tensorflow inside a virtual env&lt;/h2&gt;
      &lt;h3&gt;Patrice Ferlet ・ Jan 19&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#nvidia&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#pytorch&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#tensorflow&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#gpu&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>python</category>
      <category>tensorflow</category>
      <category>nvidia</category>
    </item>
    <item>
      <title>How to resolve the dlopen problem with Nvidia and PyTorch or Tensorflow inside a virtual env</title>
      <dc:creator>Patrice Ferlet</dc:creator>
      <pubDate>Sun, 19 Jan 2025 20:50:58 +0000</pubDate>
      <link>https://forem.com/metal3d/how-to-resolve-the-dlopen-problem-with-nvidia-and-pytorch-or-tensorflow-inside-a-virtual-env-181e</link>
      <guid>https://forem.com/metal3d/how-to-resolve-the-dlopen-problem-with-nvidia-and-pytorch-or-tensorflow-inside-a-virtual-env-181e</guid>
      <description>&lt;p&gt;If you install PyTorch or Tensorflow with cuda dependencies, you probably have the same problem as I had: the GPU is not detected and an error about &lt;code&gt;dlopen&lt;/code&gt; appears. This article explains why and how to fix it.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# before launching your python, poetry run, pipenv run commands&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;LD_LIBRARY_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;find .venv &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.so*"&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;nvidia | xargs &lt;span class="nb"&gt;dirname&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; | &lt;span class="nb"&gt;paste&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;":"&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; -&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using Nvidia pip packages
&lt;/h2&gt;

&lt;p&gt;I really don't like having CUDA inside my root system on Linux. It's not open source, it's huge, and I often need to fix symlinks on several versions. This is not comfortable, and it needs to add a repository.&lt;/p&gt;

&lt;p&gt;By chance, &lt;a href="https://www.tensorflow.org/" rel="noopener noreferrer"&gt;Tensorflow&lt;/a&gt; or &lt;a href="https://pytorch.org/" rel="noopener noreferrer"&gt;PyTorch&lt;/a&gt; can work with &lt;code&gt;pip&lt;/code&gt; packages from Nvidia.&lt;/p&gt;

&lt;p&gt;You can, for example, use &lt;code&gt;nvidia-cudnn-cu11&lt;/code&gt; to install &lt;code&gt;cuDnn&lt;/code&gt; for CUDA v11 &lt;strong&gt;inside your virtual environment&lt;/strong&gt;. This even using &lt;code&gt;poetry&lt;/code&gt;, &lt;code&gt;pipenv&lt;/code&gt; or manually.&lt;/p&gt;

&lt;p&gt;Tensorflow offers a nice subpackage that installs everything needed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# or poetry add, or pipenv install ...&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"tensorflow[and-cuda]"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OK, that's fun. But here is the problem...&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Trying to use my GPU gives me an error. Actually, the libraries are not found and so Nvidia isn't able to use my GPU.&lt;/p&gt;

&lt;p&gt;Let me show you a simple case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# you could remve this after your tests&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; Projects/ML/testgpu
&lt;span class="nb"&gt;cd &lt;/span&gt;Projects/ML/testgpu

&lt;span class="c"&gt;# install with poetry&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; .venv
poetry init &lt;span class="nt"&gt;--no-interaction&lt;/span&gt; &lt;span class="nt"&gt;--python&lt;/span&gt; &lt;span class="s2"&gt;"^3.12"&lt;/span&gt;
poetry &lt;span class="nb"&gt;env &lt;/span&gt;use 3.12

&lt;span class="c"&gt;# could be long&lt;/span&gt;
poetry add &lt;span class="s2"&gt;"tensorflow[and-cuda]"&lt;/span&gt;

poetry run python &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import tensorflow;tensorflow.keras.Sequential().compile()"&lt;/span&gt;

&lt;span class="c"&gt;# in the output:&lt;/span&gt;
&lt;span class="c"&gt;#...&lt;/span&gt;
W0000 00:00:1737318822.063024   15978 gpu_device.cc:2344] Cannot dlopen some GPU libraries. Please make sure the missing libraries mentioned above are installed properly &lt;span class="k"&gt;if &lt;/span&gt;you would like to use GPU. Follow the guide at https://www.tensorflow.org/install/gpu &lt;span class="k"&gt;for &lt;/span&gt;how to download and setup the required libraries &lt;span class="k"&gt;for &lt;/span&gt;your platform.
Skipping registering GPU devices...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;OK, what's the problem? &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You installed "CUDA" in "&lt;code&gt;.venv&lt;/code&gt;" but Nvidia uses subdirectories and paths that are not activated by default.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We need to force the &lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt; environment to include every directories where Nvidia installed their shared libraries...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But there are a lot of libraries, a lot of "&lt;code&gt;.so&lt;/code&gt;" files with suffix.&lt;/p&gt;

&lt;h2&gt;
  
  
  The solution
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;By chance, we are using Linux!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On our terminal, we have got some very powerful tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;find&lt;/code&gt; can "find" (surprise...) files in a directory with a pattern&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;grep&lt;/code&gt; can filter the output to find what we need&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;xargs&lt;/code&gt; can be used to apply a command on each output line, and we can use &lt;code&gt;dirname&lt;/code&gt; to get the directory name&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sort&lt;/code&gt; can sort the output and also remove doubles with "&lt;code&gt;-u&lt;/code&gt;" (unique) option&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;paste&lt;/code&gt; is a very nice tool to concatenate strings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, let's try:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find .venv &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.so*"&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;nvidia | xargs &lt;span class="nb"&gt;dirname&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt;
&lt;span class="c"&gt;# output:&lt;/span&gt;
.venv/lib/python3.12/site-packages/nvidia/cublas/lib
.venv/lib/python3.12/site-packages/nvidia/cuda_cupti/lib
.venv/lib/python3.12/site-packages/nvidia/cuda_nvcc/nvvm/lib64
.venv/lib/python3.12/site-packages/nvidia/cuda_nvrtc/lib
.venv/lib/python3.12/site-packages/nvidia/cuda_runtime/lib
.venv/lib/python3.12/site-packages/nvidia/cudnn/lib
.venv/lib/python3.12/site-packages/nvidia/cufft/lib
.venv/lib/python3.12/site-packages/nvidia/curand/lib
.venv/lib/python3.12/site-packages/nvidia/cusolver/lib
.venv/lib/python3.12/site-packages/nvidia/cusparse/lib
.venv/lib/python3.12/site-packages/nvidia/nccl/lib
.venv/lib/python3.12/site-packages/nvidia/nvjitlink/lib

&lt;span class="c"&gt;# let's concatenate&lt;/span&gt;
find .venv &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.so*"&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;nvidia | xargs &lt;span class="nb"&gt;dirname&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; | &lt;span class="nb"&gt;paste&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;":"&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; -
&lt;span class="c"&gt;# output:&lt;/span&gt;
.venv/lib/python3.12/site-packages/nvidia/cublas/lib:.venv/lib/python3.12/site-packages/nvidia/cuda_cupti/lib:.venv/lib/python3.12/site-packages/nvidia/cuda_nvcc/nvvm/lib64:.venv/lib/python3.12/site-packages/nvidia/cuda_nvrtc/lib:.venv/lib/python3.12/site-packages/nvidia/cuda_runtime/lib:.venv/lib/python3.12/site-packages/nvidia/cudnn/lib:.venv/lib/python3.12/site-packages/nvidia/cufft/lib:.venv/lib/python3.12/site-packages/nvidia/curand/lib:.venv/lib/python3.12/site-packages/nvidia/cusolver/lib:.venv/lib/python3.12/site-packages/nvidia/cusparse/lib:.venv/lib/python3.12/site-packages/nvidia/nccl/lib:.venv/lib/python3.12/site-packages/nvidia/nvjitlink/lib

&lt;span class="c"&gt;# OK, so make the LD_LIBRARY_PATH variable:&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;LD_LIBRARY_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;find .venv &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.so*"&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;nvidia | xargs &lt;span class="nb"&gt;dirname&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; | &lt;span class="nb"&gt;paste&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;":"&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; -&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And so, in the same terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;poetry run python &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import tensorflow;tensorflow.keras.Sequential().compile()"&lt;/span&gt;
&lt;span class="c"&gt;# output&lt;/span&gt;
&lt;span class="c"&gt;#...&lt;/span&gt;
I0000 00:00:1737319126.393498   16936 gpu_device.cc:2022] Created device /job:localhost/replica:0/task:0/device:GPU:0 with 4169 MB memory:  -&amp;gt; device: 0, name: NVIDIA GeForce RTX 3060 Laptop GPU, pci bus &lt;span class="nb"&gt;id&lt;/span&gt;: 0000:01:00.0, compute capability: 8.6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hooray, my GPU is now detected!&lt;/p&gt;

</description>
      <category>nvidia</category>
      <category>pytorch</category>
      <category>tensorflow</category>
      <category>gpu</category>
    </item>
    <item>
      <title>Nextcloud on Raspberry Pi - Fedora + Podman Quadlets</title>
      <dc:creator>Patrice Ferlet</dc:creator>
      <pubDate>Wed, 15 Jan 2025 13:45:20 +0000</pubDate>
      <link>https://forem.com/metal3d/nextcloud-on-raspberry-pi-fedora-podman-quadlets-1k49</link>
      <guid>https://forem.com/metal3d/nextcloud-on-raspberry-pi-fedora-podman-quadlets-1k49</guid>
      <description>&lt;p&gt;Let me show you how to install &lt;a href="https://nextcloud.com" rel="noopener noreferrer"&gt;Nextcloud&lt;/a&gt; on a Raspberry Pi using Fedora, Podman and Quadlet (containers). It's simple !&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Quadlet
&lt;/h2&gt;

&lt;p&gt;A &lt;a href="https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html" rel="noopener noreferrer"&gt;Quadlet&lt;/a&gt; is a way to create a service (with systemd) from a container. It uses &lt;a href="https://podman.io/" rel="noopener noreferrer"&gt;Podman&lt;/a&gt;, a container engine. As it is OCI complient, the Docker images are compatibles with Podman.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Don't be impressed by the number of command and configuration lines in this article. &lt;strong&gt;The "Quadlet" part is very simple.&lt;/strong&gt; Only the configurations required for Nginx, Nextcloud and the creation of a mount point for the hard disk are a little more tricky. But that's not Podman's fault.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Quadlet management is now integrated inside Podman. If you've got Podman, you can create Quadlets 😄&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Podman is installed by default on Fedora 41. It works with amd64 and aarm64 CPU. So it works on Raspberry Pi too.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Quadlets are easy to write, easy to maintain, and it helps a lot to manage services as if they are &lt;em&gt;real&lt;/em&gt; services.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Nextcloud
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://nextcloud.com" rel="noopener noreferrer"&gt;Nextcloud&lt;/a&gt; is a complete cloud service providing file shares, online office tools, messaging with visio-conference, and many others tools. It could be used as NAS as it provides a WebDav sharing service per user.&lt;/p&gt;

&lt;p&gt;It works great on Raspberry Pi v4.&lt;/p&gt;

&lt;h2&gt;
  
  
  The prerequisites
&lt;/h2&gt;

&lt;p&gt;You need to install Fedora 41 aarm64 version on your Raspberry (actually, it should work on others distributions, but I love Fedora). &lt;/p&gt;

&lt;p&gt;Because I'm using Nextcloud to store files, I mounted a 2To USB SSD disk on the blue USB port (USB 3). I will show you how to make it available at boot time. If you want to use an internal directory, it's up to you. But, keep in mind that your SD card will not support read/write for a long time. It's &lt;strong&gt;recommended&lt;/strong&gt; to use an external hard drive.&lt;/p&gt;

&lt;p&gt;Then you'll need an SSH connection and root access.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mounting the drive.
&lt;/h2&gt;

&lt;p&gt;It's optional, you'll need this part only if you use an external hard drive.&lt;/p&gt;

&lt;p&gt;First, please, format the partition to EXT4 or BTRFS. It's important in order not having problems with labeling and file authorizations. On my side, I only used the “Format drive” option in the file explorer on my personal computer.&lt;/p&gt;

&lt;p&gt;Then, plug the drive on your Raspberry Pi (use the blue port if possible, as it is USB 3.0 and is faster).&lt;/p&gt;

&lt;p&gt;You should now be able to access the drive. Use &lt;code&gt;dmesg&lt;/code&gt; command line to check if the hard drive is detected and the device name (something like &lt;code&gt;/dev/sda&lt;/code&gt; or &lt;code&gt;/dev/sdb&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;We need to mount it somewhere. I choose &lt;code&gt;/mnt/nas&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Create a directory where to mount the drive :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; /mnt/nas
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, find the UUID of the partition on your hard drive. (Actually, we can mount the drive with its device path, but it may change later. It's safer to use the partition UUID)&lt;/p&gt;

&lt;p&gt;My hard drive is detected as &lt;code&gt;/dev/sda&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;lsblk &lt;span class="nt"&gt;--fs&lt;/span&gt;
NAME        FSTYPE FSVER LABEL         UUID                                 FSAVAIL FSUSE% MOUNTPOINTS
sda
└─sda1      ext4   1.0   NAS           2ce801a3-4911-4d55-ad17-44b1c91d6391    1,7T     0% /mnt/nas
mmcblk1
├─mmcblk1p1 vfat   FAT32               22A3-9756                             562,3M     6% /boot/efi
├─mmcblk1p2 ext4   1.0                 c8e29478-8fe9-49bc-90cd-e816cbe753b8    579M    34% /boot
└─mmcblk1p3 btrfs        fedora_fedora 66ed970b-b91a-483e-9b82-b10fdfc12ea7   51,9G    10% /var/lib/containers/storage/overlay
                                                                                           /home
                                                                                           /
zram0                                                                                      &lt;span class="o"&gt;[&lt;/span&gt;SWAP]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I keep the UUID and I added this line in &lt;code&gt;/etc/fstab&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UUID=2ce801a3-4911-4d55-ad17-44b1c91d6391 /mnt/nas      ext4    defaults        0 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, typing &lt;code&gt;mount -a&lt;/code&gt; I can see that my hard drive is mounted in &lt;code&gt;/mnt/nas&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mount | &lt;span class="nb"&gt;grep &lt;/span&gt;sda1
/dev/sda1 on /mnt/nas &lt;span class="nb"&gt;type &lt;/span&gt;ext4 &lt;span class="o"&gt;(&lt;/span&gt;rw,relatime,seclabel,stripe&lt;span class="o"&gt;=&lt;/span&gt;256&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's OK. Now, each time the Raspberry restart, the external disk is mounted at the right place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prepare TLS/SSL certificate
&lt;/h2&gt;

&lt;p&gt;Because I want to use SSL (HTTPS), I will create the certificate. Everything is saved in the SSD hard drive to be able to reinstall everything without losing my mind.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You need to know the hostname you use to contact your Raspberry. In my case, my router applies &lt;code&gt;.home&lt;/code&gt; on the network devices. As I set up my Rasbperry to be named "nas", the hostname is "nas.home". Be sure to be able to contact it (ping it)&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# change these information
HOSTNAME=nas
DOMAIN=.home
O=Home
COUNTRY=FR
STATE=France
LOC=Laval

# to name your machine, DO NOT INCLUDE DOMAIN
hostnamectl hostname $HOSTNAME

# generate certificates
mkdir -p /mnt/nas/nginx/ssl

# 3650 days = 10 years
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
  -keyout /mnt/nas/ssl/${HOSTNAME}${DOMAIN}.key \
  -out /mnt/nas/ssl/${HOSTNAME}${DOMAIN}.crt \
  -subj "/C=${COUNTRY}/ST=${STATE}/L=${LOCATION}/O=${O}/CN=${HOSTNAME}${DOMAIN}"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key and the certificate are now created.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You should backup the private key.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Nginx configuration
&lt;/h2&gt;

&lt;p&gt;As we will use Nginx as a container, we need to make a reverse proxy to the container name. As we will name the Nextcloud container "Nextcloud", it's pretty simple. Replace the line I indicate if you have another name to use as server name.&lt;/p&gt;

&lt;p&gt;Edit and save it in &lt;code&gt;/mnt/nas/nginx/conf.d/nextcloud.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="s"&gt;[::]:443&lt;/span&gt; &lt;span class="s"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt; &lt;span class="s"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;nas.home&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;# replace this&lt;/span&gt;

    &lt;span class="kn"&gt;ssl_certificate&lt;/span&gt; &lt;span class="n"&gt;/etc/nginx/ssl/nas.home.crt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;# replace this&lt;/span&gt;
    &lt;span class="kn"&gt;ssl_certificate_key&lt;/span&gt; &lt;span class="n"&gt;/etc/nginx/ssl/nas.home.key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;# replace this&lt;/span&gt;

    &lt;span class="kn"&gt;ssl_protocols&lt;/span&gt; &lt;span class="s"&gt;TLSv1.2&lt;/span&gt; &lt;span class="s"&gt;TLSv1.3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;ssl_ciphers&lt;/span&gt; &lt;span class="s"&gt;HIGH:!aNULL:!MD5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;client_max_body_size&lt;/span&gt; &lt;span class="mi"&gt;8G&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;Strict-Transport-Security&lt;/span&gt; &lt;span class="s"&gt;"max-age=31536000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="kn"&gt;includeSubDomains"&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;# to the "nextcloud" container&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://nexcloud:80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt; &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_redirect&lt;/span&gt; &lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_buffering&lt;/span&gt; &lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="s"&gt;[::]:80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;nas.home&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;# replace this&lt;/span&gt;

    &lt;span class="c1"&gt;# Redirect HTTP to HTTPS&lt;/span&gt;
    &lt;span class="kn"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;301&lt;/span&gt; &lt;span class="s"&gt;https://&lt;/span&gt;&lt;span class="nv"&gt;$host$request_uri&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create the Quadlets
&lt;/h2&gt;

&lt;p&gt;It's now time to create our services. Instead of installing everything by service, we will use containers.&lt;/p&gt;

&lt;p&gt;First, I need a "network". It's only to make all my containers communicating in the same namespace. This way, I don't have to publish unnecessary ports to the host. I only require the nginx ports.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch&lt;/span&gt; /etc/containers/systemd/nextcloud.network
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The file can be empty, the default values are OK.&lt;/p&gt;

&lt;p&gt;I then create 3 containers, but you can avoid the "valkey" container if you don't want to use it (it's a recommendation, it works without)&lt;/p&gt;

&lt;p&gt;The "valkey" Quadlet is defined like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="c"&gt;# /etc/containers/systemd/valkey.container
&lt;/span&gt;&lt;span class="nn"&gt;[Container]&lt;/span&gt;
&lt;span class="py"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;docker.io/valkey/valkey&lt;/span&gt;
&lt;span class="py"&gt;ContainerName&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;valkey&lt;/span&gt;
&lt;span class="py"&gt;Network&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;nexcloud.network&lt;/span&gt;


&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;Restart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;always&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;default.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Nextcloud Quadlet is defined this way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Container]&lt;/span&gt;
&lt;span class="py"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;docker.io/nextcloud:30&lt;/span&gt;
&lt;span class="py"&gt;ContainerName&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;nextcloud&lt;/span&gt;
&lt;span class="py"&gt;Network&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;nexcloud.network&lt;/span&gt;

&lt;span class="c"&gt;# where to store data
&lt;/span&gt;&lt;span class="py"&gt;Volume&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/mnt/nas/nextcloud:/var/www/html:z&lt;/span&gt;

&lt;span class="c"&gt;# wait for valkey
&lt;/span&gt;&lt;span class="py"&gt;Requires&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;valkey.container&lt;/span&gt;
&lt;span class="py"&gt;After&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;valkey.container&lt;/span&gt;

&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;Restart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;always&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;default.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the Nextcloud container should start only if Valkey is started.&lt;/p&gt;

&lt;p&gt;Then, the nginx container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Container]&lt;/span&gt;
&lt;span class="py"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;docker.io/nginx:latest&lt;/span&gt;
&lt;span class="py"&gt;ContainerName&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;nginx&lt;/span&gt;
&lt;span class="py"&gt;Network&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;nexcloud.network&lt;/span&gt;
&lt;span class="c"&gt;# export 80 and 443 ports to the network
&lt;/span&gt;&lt;span class="py"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;443:443&lt;/span&gt;
&lt;span class="py"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;80:80&lt;/span&gt;
&lt;span class="c"&gt;# mount certificates and configuration
&lt;/span&gt;&lt;span class="py"&gt;Volume&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/mnt/nas/nginx/ssl:/etc/nginx/ssl:z&lt;/span&gt;
&lt;span class="py"&gt;Volume&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/mnt/nas/nginx/conf.d:/etc/nginx/conf.d:z&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After having created these 3 files, you can reload &lt;code&gt;systemd&lt;/code&gt; and start containers :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl daemon-reload
systemctl start valkey
systemctl start nextcloud
systemctl start nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The ":z" suffix is required on Red Hat based distribution to set SeLinux labels to allow the write access. Debian based distribution ignores the prefix. So, keep it, there is no problem.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You'll probably need to open firewall ports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;firewall-cmd &lt;span class="nt"&gt;--add-service&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http &lt;span class="nt"&gt;--permanent&lt;/span&gt;
firewall-cmd &lt;span class="nt"&gt;--add-service&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https &lt;span class="nt"&gt;--permanent&lt;/span&gt;
firewall-cmd &lt;span class="nt"&gt;--reload&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Wait! There is something more to do!&lt;/strong&gt; Do not navigate to Nextcloud yet!&lt;/p&gt;

&lt;h2&gt;
  
  
  Last configuration
&lt;/h2&gt;

&lt;p&gt;Now that Nextcloud has started, you should have &lt;code&gt;/mnt/nas/nextcloud/config/config.php&lt;/code&gt; file. You need to alter some points.&lt;/p&gt;

&lt;p&gt;First, force HTTPS and hostname:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;  &lt;span class="s1"&gt;'overwrite.cli.url'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'https://nas.home'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// your FQDN&lt;/span&gt;
  &lt;span class="s1"&gt;'overwritehost'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'nas.home'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// same, your FQDN&lt;/span&gt;
  &lt;span class="s1"&gt;'overwriteprotocol'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'https'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And, if you want to use Vakey, as I do, add this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;  &lt;span class="s1"&gt;'memcache.locking'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'\\OC\\Memcache\\Redis'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'memcache.distributed'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'\\OC\\Memcache\\Redis'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'redis'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'host'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'valkey'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// this is the container name&lt;/span&gt;
    &lt;span class="s1"&gt;'port'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;6379&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now you can navigate to &lt;a href="https://nas.home" rel="noopener noreferrer"&gt;https://nas.home&lt;/a&gt; (use your own domain)&lt;/p&gt;

&lt;h2&gt;
  
  
  Precision
&lt;/h2&gt;

&lt;p&gt;It is possible to not use Nginx container and use the Nginx package. But, on Red Hat based distribution, SELinux will refuse to leave Nginx to proxy pass to a container. If you decided to use nginx package:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;change the Nextcloud container to bind &lt;code&gt;8080:80&lt;/code&gt;, or whatever you want&lt;/li&gt;
&lt;li&gt;change nginx configuration to point on &lt;code&gt;localhost:8080&lt;/code&gt; instead of &lt;code&gt;nextcloud:80&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;allow the HTTP connection from nginx to container:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;setsebool &lt;span class="nt"&gt;-P&lt;/span&gt; httpd_can_network_connect on
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart nginx, &lt;code&gt;nginx -s reload&lt;/code&gt; and it's OK.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;You probably think it's a bit complex, but actually it's not. &lt;/p&gt;

&lt;p&gt;Check one more time, the Quadlet section is very simple. The only "hard" topics are the hard drive mount point automation and the  Nginx and Nextcloud configuration. Everything that is related to Podman and Quadlet was simple.&lt;/p&gt;

&lt;p&gt;Also, I propose Valkey (a Redis fork by Linux Foundation), but you can avoid it.&lt;/p&gt;

&lt;p&gt;Quadlet are very easy to use. As soon as you understand the syntax, you'll never use "compose" file anymore for long-term services.&lt;/p&gt;

</description>
      <category>containers</category>
      <category>podman</category>
      <category>linux</category>
      <category>raspberrypi</category>
    </item>
    <item>
      <title>You forget RethinkDB, it's a shame</title>
      <dc:creator>Patrice Ferlet</dc:creator>
      <pubDate>Mon, 20 Nov 2023 12:53:32 +0000</pubDate>
      <link>https://forem.com/metal3d/you-forget-rethinkdb-its-a-shame-p9n</link>
      <guid>https://forem.com/metal3d/you-forget-rethinkdb-its-a-shame-p9n</guid>
      <description>&lt;p&gt;There is no only MongoDB... and while you may think of ArangoDB, or CouchDB, you haven't thought of RethinkDB. This project must be saved, urgently!&lt;/p&gt;

&lt;p&gt;In the world of database servers, there are many different solutions. PostgreSQL is potentially one of the most reliable solutions, MariaDB has beaten MySQL to the punch, and there are countless projects based on MongoDB.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Document databases have become commonplace.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's practical, it's quick to set up, and it's easy to manage the schema within the application. As a result, the data is in JSON/BSON form, and is structured as a result.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;All's well in the best of worlds.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;OK, have you ever tried to quickly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;scale your database, I mean "QUICKLY"?&lt;/li&gt;
&lt;li&gt;listen for changes in a table, to use like a bus, providing events to your clients/users?&lt;/li&gt;
&lt;li&gt;having a UI where you can see what happens on tables, where are replicas, shards,... ?&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Yes, of cours, you can scale, shard, monitor MongoDB, CouchDB. But please go ahead and follow my article.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I've been poking around, testing and breaking database servers for a long time (more than 20 years today). But a few years ago I came across a jewel, the grail, one of the best solutions available. Under the radar, shunned for whatever reason, &lt;a href="https://rethinkdb.com/" rel="noopener noreferrer"&gt;RethinkDB&lt;/a&gt; is nonetheless one of the finest database server projects I've ever tested.&lt;/p&gt;

&lt;p&gt;And the pleasure of using this server can be summed up in 3 points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it's as simple as can be, with a web-based interface for querying, administration, and monitoring&lt;/li&gt;
&lt;li&gt;With the "changes API", I can listen to a table and wait for changes. So it can be used as a bus, and I can use server side event with ease in my web applications.&lt;/li&gt;
&lt;li&gt;efficient scalability through &lt;strong&gt;sharding&lt;/strong&gt; and &lt;strong&gt;replication&lt;/strong&gt;. Can be configured in no time at all, with no need for in-depth skills.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wanna test?
&lt;/h2&gt;

&lt;p&gt;Below, you can replace "&lt;code&gt;podman&lt;/code&gt;" commands to "&lt;code&gt;docker&lt;/code&gt;" of course. I prefer &lt;code&gt;podman&lt;/code&gt; while I'm using Fedora, and because it works rootless.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a network. It's important for what happens next.&lt;/span&gt;
podman network create rdb

&lt;span class="c"&gt;# Start our fist node, bind 8080 to see the &lt;/span&gt;
&lt;span class="c"&gt;# web interface&lt;/span&gt;
&lt;span class="c"&gt;# It's important to name the container, and&lt;/span&gt;
&lt;span class="c"&gt;# to connect the container to a network. &lt;/span&gt;
&lt;span class="c"&gt;# You'll see why...&lt;/span&gt;
podman run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;--network&lt;/span&gt; rdb &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;--name&lt;/span&gt; rdb1 &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 &lt;span class="se"&gt;\&lt;/span&gt;
   docker.io/rethinkdb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OK, now visit &lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt; and see the web interface. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fflw9hq2q4e1mkjkjy0kl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fflw9hq2q4e1mkjkjy0kl.png" alt="RethinkDB admin page" width="800" height="631"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The 8080 port is the web admin page port. RethinkDB uses the default 28015 port for query (from your application), and 29015 for clustering.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can now visit the "Tables" tab to create databases and tables. No scheme to define, of course. At this time, we cannot "replicate" and "shard" the data. But, it will be OK in a few seconds.&lt;/p&gt;

&lt;p&gt;Because, we will add a new node in the cluster. Ready?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a new node in the network, but now&lt;/span&gt;
&lt;span class="c"&gt;# we tell rethinkdb to "join" one node&lt;/span&gt;
&lt;span class="c"&gt;# (whatever the node!), here "rdb1".&lt;/span&gt;
podman run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--network&lt;/span&gt; rdb &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--name&lt;/span&gt; rdb2 &lt;span class="se"&gt;\&lt;/span&gt;
    docker.io/rethinkdb &lt;span class="se"&gt;\&lt;/span&gt;
    rethinkdb &lt;span class="nt"&gt;-j&lt;/span&gt; rdb1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's important, with podman and docker, to be in the same network to be able to communicate by "name" (via name resolution). &lt;/p&gt;

&lt;p&gt;And see:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr9i4ubwos2kgylxt79q9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr9i4ubwos2kgylxt79q9.png" alt="Adding more nodes in RethinkDB" width="473" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yes, 2 servers now!&lt;/p&gt;

&lt;p&gt;You can now configure your tables to be replicated and/or shard.&lt;/p&gt;

&lt;p&gt;Add more and more nodes, you can tell the "&lt;code&gt;rethinkdb&lt;/code&gt;" to join any node in the cluster, RethinkDB will do the job for you.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In our example, with Docker or Podman, you only need to ensure that you connect the container to the same network. This to use name resolution. In Kubernetes, you will need to create &lt;code&gt;Statefulset&lt;/code&gt; and use headless services. In bare metal and VM, use whatever you want to contact one node.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Let's create a table
&lt;/h2&gt;

&lt;p&gt;Let's create an "app" database, and add a "movies" table:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7k0qxke1q90bqw5y2npp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7k0qxke1q90bqw5y2npp.png" alt="Create a database and table in RethinkDB" width="800" height="346"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's time to create some entries. Go to "data explorer" tab, and type this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;movies&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Titanic&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;director&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Cameron&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Citizen Kane&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;director&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Welles&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;I will not give details. But yes you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;add indexes&lt;/li&gt;
&lt;li&gt;add structures&lt;/li&gt;
&lt;li&gt;create DB and tables with RQL langage (Javascript like)&lt;/li&gt;
&lt;li&gt;and many other things&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;This inserts 2 new entries. As we gave to &lt;code&gt;insert()&lt;/code&gt; an array. Of course you can add one entry.&lt;/p&gt;

&lt;p&gt;If you now visit the table view, you can see this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2x9pwwnybna3v9k37lp2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2x9pwwnybna3v9k37lp2.png" alt="RethinkDB table operation monitor" width="800" height="651"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nice, isn't it? A monitor of the table. OK, let's replicate the data to be sure that, if one node fails, we can get it back from a second server. Click "Reconfigure" button and do this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fck6vl9x4fxrmmjrrctxr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fck6vl9x4fxrmmjrrctxr.png" alt="Reconfigure RethinkDB tables replication" width="589" height="307"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By saying that we now need 2 replicas, RethinkDB will do what it must to do to replicate the data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwfozb5srmnmotdybaucu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwfozb5srmnmotdybaucu.png" alt="RethinkDB balances the data" width="800" height="522"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And, as you can see now, the documents are distributed in several servers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgfrcw8dxks7oob7evo6b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgfrcw8dxks7oob7evo6b.png" alt="RethinkDB replication placement" width="800" height="182"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Of course, all we did here can be made programatically!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The queries are easy to understand:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn1595fm05rldyuymy2t1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn1595fm05rldyuymy2t1.png" alt="Get all documents" width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc76wjdaxlheebbbywp01.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc76wjdaxlheebbbywp01.png" alt="Filtering documents" width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The "Changes" API
&lt;/h2&gt;

&lt;p&gt;One of the best features of RethinkDB is the "changes" api. This is a very interesting thing that I use a lot.&lt;/p&gt;

&lt;p&gt;You can listen for "changes" in a table, and react to this changes. Then you can create a serverside event handler in you web server, or whatever you want, to send messages to clients. It's very easy to understand:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;movies&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;changes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F07r5oog9w5m1ysfdgwn8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F07r5oog9w5m1ysfdgwn8.png" alt="The changes API in RethinkDB" width="800" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each changes provide the "old" and "new" element that changed in the table. And of course, you can filter the changes!&lt;/p&gt;

&lt;h2&gt;
  
  
  Using with Python, Go, JS, Ruby, ...
&lt;/h2&gt;

&lt;p&gt;RethinkDB officially support Python, JS, Java and Ruby drivers. But there are plenty of drivers for Go, C#, Dart, Perl, PHP, Lua... See &lt;a href="https://rethinkdb.com/docs/install-drivers" rel="noopener noreferrer"&gt;the documentation page here&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq27itaprqs1aynyll197.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq27itaprqs1aynyll197.png" alt="RethinkDB drivers" width="735" height="848"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  But it's dying by inches (or not)
&lt;/h2&gt;

&lt;p&gt;The developers of this service have gone to work for Stripe. Kudos to them, without any irony, because they richly deserve their success.&lt;/p&gt;

&lt;p&gt;The sad thing is that RethinkDB is now only benefiting from a few advances and maintenance operations. It works, the project is maintained at arm's length, certain pull-requests continue to be integrated. But the fear is growing for its users. Because you don't want to lose a tool like this.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Really, we've got to do something to save it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But what? Of course, sponsorship is the first thing to do. If companies are listening, the message is clear:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;you benefit from free software, and so much the better! but help us to offer you continuity of development. Give to the projects you use.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But there's another thing you can do at your own level: &lt;strong&gt;use it!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because a tool can only feel useful if it's used.&lt;/p&gt;

&lt;p&gt;I use RethinkDB against all fears of seeing it disappear. I refuse to succumb to the fear of obsolescence that I constantly hear. &lt;/p&gt;

&lt;p&gt;RethinkDB is developed in C++ and is completely opensource. There are &lt;strong&gt;no major bugs&lt;/strong&gt; and as a result:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;we can therefore say that it doesn't logically need frequent updating&lt;/li&gt;
&lt;li&gt;we can be sure that it can be recompiled for future OSes&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Clearly, I'm not afraid to say that RethinkDB is production-ready, reliable, usable and future-proof.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  So I've got three messages for you
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Users&lt;/strong&gt;: Go for it, use it, thank the developers, go and click on the little star in GitHub to show your encouragement not to let it die in a corner.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Companies, CIOs, CTOs&lt;/strong&gt;: use it. Test it. Stop falling into the trap of commercial arguments for certain solutions, even opensource ones. RethinkDB works and offers you a service. A service you won't find as complete with other solutions. And for God's sake, donate to the opensource tools you use!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developers&lt;/strong&gt;, RethinkDB authors: please don't give up. To date, there's no document database solution that offers such simple scalability, with such a well-designed "changes" API system, and that combines simplicity and efficiency.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>database</category>
      <category>development</category>
      <category>programming</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Understand how to use C libraries in Go, with CGO</title>
      <dc:creator>Patrice Ferlet</dc:creator>
      <pubDate>Mon, 09 Oct 2023 08:50:42 +0000</pubDate>
      <link>https://forem.com/metal3d/understand-how-to-use-c-libraries-in-go-with-cgo-3dbn</link>
      <guid>https://forem.com/metal3d/understand-how-to-use-c-libraries-in-go-with-cgo-3dbn</guid>
      <description>&lt;p&gt;You probably ever heard about CGO, and that Go can use shared libraries from your system to use the power of C. But how? I will explain the process, and you'll see that it's not that complicated – in fact, it's quite simple.&lt;/p&gt;

&lt;p&gt;This tutorial is made for very beginners, but you need to have a bit of knowledge in C programming.&lt;/p&gt;

&lt;p&gt;I wrote this tutorial for the simple reason that I find the official documentation, as well as the tutorials I've read, a little austere. For my part, I needed to start from a very simple situation, a real "hello world", and slowly move towards the use of a shared library. So this is my vision, my way of looking at things, which I offer you here.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are shared libraries?
&lt;/h2&gt;

&lt;p&gt;Shared libraries, also known as dynamic link libraries (DLL) on Windows or shared objects (SO) on Unix-based systems like Linux, are files containing compiled code that multiple programs can use simultaneously. Instead of having the code for a particular task duplicated in every program that needs to perform that task, the code is stored in a shared library. This way, programs can use the functions and procedures from these libraries without having to include the code directly in their files.&lt;/p&gt;

&lt;p&gt;And Go can take advantage of this. Like Python or Rust, of course.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does CGO?
&lt;/h2&gt;

&lt;p&gt;It is a tool that allows calling C functions and using C libraries from Go code. It serves as the bridge between Go and C languages, enabling Go programs to incorporate existing C libraries and leverage existing C codebases.&lt;/p&gt;

&lt;p&gt;Actually, it can do this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Passing Go functionalities to C&lt;/strong&gt; but this is not what we will see today&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Calling C functions&lt;/strong&gt; directly from a "comment" above the &lt;code&gt;import "C"&lt;/code&gt; statement. Or from a &lt;code&gt;.c&lt;/code&gt; file inside the project.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Using C libraries&lt;/strong&gt; to use already compiled functions and types in a shared library.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;CGO is provided with Go, but you'll need a C compiler like GCC on Ming (for Windows).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;CGO will use the "C" package where all C functions and variables are accessibles. It will then use the C compiler to make the link to your Go project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Let's try to use C in Go!
&lt;/h2&gt;

&lt;p&gt;First, just to understand what does CGO, we will call a C function that we will create.&lt;/p&gt;

&lt;p&gt;Create a project, for example in &lt;code&gt;~/Projects/testCGO&lt;/code&gt; and inside this directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go mod init testcgo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then create a &lt;code&gt;main.go&lt;/code&gt; file and write this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="c"&gt;// #include &amp;lt;stdio.h&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;// void hello() {&lt;/span&gt;
&lt;span class="c"&gt;//    printf("Hello from C")&lt;/span&gt;
&lt;span class="c"&gt;//}&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="c"&gt;// let's call it&lt;/span&gt;
    &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In your terminal, type &lt;code&gt;go run .&lt;/code&gt; and see the result. Yes, it says "Hello from C".&lt;/p&gt;

&lt;p&gt;But, what does CGO here?&lt;/p&gt;

&lt;p&gt;Actually, CGO has used the comment above the &lt;code&gt;import "C"&lt;/code&gt; statement and it shared the function in the "C" package. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;That means that the &lt;code&gt;hello()&lt;/code&gt; function, developped in C, is accessible as &lt;code&gt;C.hello()&lt;/code&gt; in Go. The "C" package is like a namespace where C variables and function are accessed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But, we can do this a bit prettier. Coding in comments can be useful if we don't have too many lines of C to do, but when the project becomes substantial, it can quickly become a bit annoying. So, let's use real C source files.&lt;/p&gt;

&lt;p&gt;In the same directory, create the &lt;code&gt;hello.c&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello from C in another file"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And, a &lt;code&gt;hello.h&lt;/code&gt; file to declare the function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, in your &lt;code&gt;main.go&lt;/code&gt; file, replace the content to get this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="c"&gt;// #include "hello.h"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="c"&gt;// let's call it&lt;/span&gt;
    &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way, we're using the &lt;code&gt;include&lt;/code&gt; statement, which is a C inclusion syntax. CGO has no problem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;go run main.go
Hello from C in another file
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One more time, CGO takes the comments above the &lt;code&gt;import "C"&lt;/code&gt; and because the &lt;code&gt;#include&lt;/code&gt; statement is a valid C call, it compiles the C file without any problem.&lt;/p&gt;

&lt;p&gt;We've just seen the base. Having C code on one side and a Go project on the other, we now know how to connect the two. But of course, there will be more restrictive things to manage.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to work with C types?
&lt;/h2&gt;

&lt;p&gt;Let's change the &lt;code&gt;hello.c&lt;/code&gt; file to accept an argument and say hello to someone.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="c1"&gt;// say hello to the name&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, change the header file to decralre the function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, change the &lt;code&gt;main.go&lt;/code&gt; file to now try to say hello to John:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="c"&gt;// #include "hello.h"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"John"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will fail...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./main.go:7:10: cannot use "John" (untyped string constant) as *_Ctype_char value in argument to (_Cfunc_hello)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;That's something very important to keep in mind, we need to cast vars from and to Go and C.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Go ease the work with many types, like array, pointers, and strings. When you need to send or receive a variable from or to C, the types are not exactly the same. So we need to "cast" types. But, no panic, after a while you will do it naturally.&lt;/p&gt;

&lt;p&gt;So, how to fix this?&lt;/p&gt;

&lt;p&gt;We need to modify the Go &lt;code&gt;string&lt;/code&gt; to &lt;code&gt;char*&lt;/code&gt; type. We can use &lt;code&gt;C.char&lt;/code&gt; type but that needs to manually allocate memory. Instead, there is a &lt;code&gt;C.CString&lt;/code&gt; type which is a bit easier to use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Gopher"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now, it works!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is the "complex" part of using CGO, you will need to convert, cast, manipulate the variables type to ensure that it will work.&lt;br&gt;
And because it's C, there is no garbage collector for C variables, so you need to free memory when needed (using &lt;code&gt;C.free()&lt;/code&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  So let's use a C library!
&lt;/h1&gt;

&lt;p&gt;Now that we have seen how CGO can compile C code, let's try to tell it to link shared libraries.&lt;/p&gt;

&lt;p&gt;For the example, I will use the very simple "libuuid".&lt;/p&gt;

&lt;p&gt;You need to install the devel package of the library to get the header files. On Fedora, that was a simple &lt;code&gt;sudo dnf install libuuid-devel&lt;/code&gt; command line.&lt;/p&gt;

&lt;p&gt;To be able to generate a UUID, you need to read the documentation of the library (yes... RTFM...) - of course, I already did it and I can explain how to generate a UUID.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In C:&lt;/span&gt;
&lt;span class="c1"&gt;// we need a uuid_t variable to initalize&lt;/span&gt;
&lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="n"&gt;uuid_t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// then we generate the random string. I use the random form&lt;/span&gt;
&lt;span class="c1"&gt;// but you can use other generate methods.&lt;/span&gt;
&lt;span class="n"&gt;uuid_generate_random&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// To get a uuid string, we need to "unparse"&lt;/span&gt;
&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;uuid_str&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="n"&gt;uuid_unparse_lower&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uuid_str&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// In the uuid_str char*, there is a uuid&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OK, so let's try.&lt;/p&gt;

&lt;p&gt;We will include the &lt;code&gt;uuid/uuid.h&lt;/code&gt; file, commonly in &lt;code&gt;/usr/include&lt;/code&gt; on Linux, that CGO will find. And let's use the types and functions from this header file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="c"&gt;// #include &amp;lt;uuid/uuid.h&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"fmt"&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uuid_t&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;uuid_str&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;char&lt;/span&gt;
    &lt;span class="n"&gt;uuid_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;char&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;37&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uuid_generate_random&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uuid_unparse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uuid_str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GoString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid_str&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will fail...&lt;/p&gt;

&lt;p&gt;The first problem, here, is that the &lt;code&gt;typdedef&lt;/code&gt; doesn't work. We need to read the error to understand that, actually, &lt;code&gt;uuid_t&lt;/code&gt; is a &lt;code&gt;uchar*&lt;/code&gt;. But, not exactly... Actually, reading the header file, you'll see that it's a &lt;code&gt;char[16]&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Remember, in C, a &lt;code&gt;char*&lt;/code&gt; is like a &lt;code&gt;char[]&lt;/code&gt; (I'm grossly oversimplifying here). &lt;br&gt;
But, libuuid declares the &lt;code&gt;uuid_t&lt;/code&gt; with 16 chars of size, that means that, using pointer form, we need to allocate the memory with &lt;code&gt;malloc&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So let's change the line to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uchar&lt;/span&gt;
&lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uchar&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;You need to know a bit of C to work. Here, what I do is a simple C &lt;code&gt;uuid = (uchar*)malloc(16)&lt;/code&gt; transposed with "C" package in Go. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's run one more time and...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/usr/lib/golang/pkg/tool/linux_amd64/link: running gcc failed: exit status 1
/usr/bin/ld: /tmp/go-link-645695464/000001.o: in function `_cgo_b1eb6bfc450b_Cfunc_uuid_generate_random':
/tmp/go-build/cgo-gcc-prolog:49: undefined reference to `uuid_generate_random'
/usr/bin/ld: /tmp/go-link-645695464/000001.o: in function `_cgo_b1eb6bfc450b_Cfunc_uuid_unparse':
/tmp/go-build/cgo-gcc-prolog:62: undefined reference to `uuid_unparse'
collect2: error: ld returned 1 exit status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;That's now the time to use the "linker". Of course, we need to tel the compiler to use the &lt;code&gt;libuuid.so&lt;/code&gt; shared library.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Understand the problem. Earlier, we used our own C source files that are compiled with CGO. But, now, &lt;strong&gt;we want to use "already" compiled sources to a ".so" library&lt;/strong&gt; (or &lt;code&gt;.dll&lt;/code&gt; for Windows). The header files are there to only provide function declaration (name, arguments and return types).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is a common thing in C/C++ - that makes compilation very smart and fast, because we don't need to compile the libraries. We only "link" them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To inform CGO to link the shared library, we can use a specific flags to the compiler. For &lt;code&gt;libuuid&lt;/code&gt; it's a simple &lt;code&gt;-luuid&lt;/code&gt; (understand &lt;code&gt;-l uuid&lt;/code&gt;) to append. This will link &lt;code&gt;libuuid.so&lt;/code&gt; to our binary. And Go proposes to specify these arguments inside the comments, as a &lt;code&gt;#cgo:&lt;/code&gt; statement.&lt;/p&gt;

&lt;p&gt;Above the inclusion of the header file, only append a special instruction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// #cgo LDFLAGS: -luuid&lt;/span&gt;
&lt;span class="c"&gt;// #include &amp;lt;uuid/uuid.h&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now it's OK&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; go run  .
78137255-35a3-4f61-af7c-e04bf9eb513a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;That's it, you have a UUID generated by a C shared library.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The entire source code is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="c"&gt;// #cgo LDFLAGS: -luuid&lt;/span&gt;
&lt;span class="c"&gt;// #include &amp;lt;uuid/uuid.h&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"fmt"&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uchar&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;uuid_str&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;char&lt;/span&gt;
    &lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uchar&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;uuid_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;char&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;37&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uuid_generate_random&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uuid_unparse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uuid_str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GoString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid_str&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works very well, but is it practical? No...&lt;/p&gt;

&lt;h2&gt;
  
  
  Make it better
&lt;/h2&gt;

&lt;p&gt;Calling C functions, with on-the-fly type casting, is impractical, unattractive and not at all easy to maintain.&lt;/p&gt;

&lt;p&gt;Using C, the functions are way simpler to use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;uuid_t&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;uuid_generate_random&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// because 36 chars + \0&lt;/span&gt;
&lt;span class="n"&gt;uuid_unparse_lower&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// and I can return "str" variable&lt;/span&gt;
&lt;span class="c1"&gt;// that contains a UUID&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then...&lt;/p&gt;

&lt;p&gt;What do we really want? &lt;strong&gt;A function that gives us a UUID&lt;/strong&gt;. So we're going to do something very practical: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;code a function in C that will make our work easier, and just make sure we have access to it in our Go program.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;OK, try:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="c"&gt;// #cgo LDFLAGS: -luuid&lt;/span&gt;
&lt;span class="c"&gt;//&lt;/span&gt;
&lt;span class="c"&gt;// #include &amp;lt;uuid/uuid.h&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;// #include &amp;lt;stdlib.h&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;//&lt;/span&gt;
&lt;span class="c"&gt;// // create a uuid function in C to return a uuid char*&lt;/span&gt;
&lt;span class="c"&gt;// char* _go_uuid() {&lt;/span&gt;
&lt;span class="c"&gt;//   uuid_t uuid;&lt;/span&gt;
&lt;span class="c"&gt;//   uuid_generate_random(uuid);&lt;/span&gt;
&lt;span class="c"&gt;//   char *str = malloc(37);&lt;/span&gt;
&lt;span class="c"&gt;//   uuid_unparse_lower(uuid, str);&lt;/span&gt;
&lt;span class="c"&gt;//   return str;&lt;/span&gt;
&lt;span class="c"&gt;// }&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"fmt"&lt;/span&gt;

&lt;span class="c"&gt;// uuid generates a UUID using the C shared library.&lt;/span&gt;
&lt;span class="c"&gt;// It returns a Go string.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GoString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_go_uuid&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// and now it's simple to use&lt;/span&gt;
    &lt;span class="n"&gt;myuuid&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// this is a go string now&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;myuuid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, we could create the &lt;code&gt;_go_uuid()&lt;/code&gt; function in a C source file and create a &lt;code&gt;.h&lt;/code&gt; file to declare our function. Then, include &lt;code&gt;go_uuid.h&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;What we did here is very common when we want to bind shared libraries to Go. We create some helper functions to cast types and to call C function without asking the user to use the &lt;code&gt;C&lt;/code&gt; package by itself.&lt;/p&gt;

&lt;p&gt;And this is how &lt;a href="https://github.com/go-gst/go-gst" rel="noopener noreferrer"&gt;https://github.com/go-gst/go-gst&lt;/a&gt;, &lt;a href="https://github.com/go-gl/glfw" rel="noopener noreferrer"&gt;https://github.com/go-gl/glfw&lt;/a&gt;, and even &lt;a href="https://fyne.io/" rel="noopener noreferrer"&gt;https://fyne.io/&lt;/a&gt; are using system libraries to propose a lot of functionalities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reminder
&lt;/h2&gt;

&lt;p&gt;So, what you need to keep in mind when you want to use shared libraries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the "C" package gives access to C functions, types and variables&lt;/li&gt;
&lt;li&gt;you can include header files using comments &lt;strong&gt;above the import of "C" pacakge"&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;you can provide &lt;code&gt;LDFLAGS&lt;/code&gt; and &lt;code&gt;CFLAGS&lt;/code&gt; to the compiler using the &lt;code&gt;#cgo&lt;/code&gt; statement in comments&lt;/li&gt;
&lt;li&gt;you often need to cast types to Go types, or Go types to C&lt;/li&gt;
&lt;li&gt;you can create helpers in comments, in C, to ease the use of the librairies&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;C isn't the only language that lets you generate shared libraries. You can, of course, generate them in Go, Rust, Python, etc. But to date, it's C that's most widely used to generate libraries.&lt;/p&gt;

&lt;p&gt;Having the possibility to use C, or calling C functions from a shared library opens Go to a wide range of powerful functionalities.&lt;/p&gt;

&lt;p&gt;Obviously, &lt;strong&gt;we prefer libraries developed entirely in Go&lt;/strong&gt;. This avoids dependence on a library that the user will have to intall on his system, or by sharing this library with him (in the form of &lt;code&gt;.so&lt;/code&gt; or &lt;code&gt;.dll&lt;/code&gt;). As Go is a language that normally uses static compilation, it's a "bit of a pity" to force the passage. But it's very useful in practice. For example, the very powerful Gstreamer library would be very complicated to recreate entirely in Go. It was created in C and works very well on many platforms. So here, having a dependency on this library is an excellent solution to open streaming sound and video to Go.&lt;/p&gt;

&lt;p&gt;You'll need some knowledge of C to be able to link a library in Go. But you don't have to be a specialist. You just need to find the right variable cast, and create a helper function from time to time.&lt;/p&gt;

&lt;p&gt;In any case, I hope that my article has opened the way for you, cleared up a few misunderstandings, and that you'll be able to use shared libraries with Go!&lt;/p&gt;

</description>
      <category>go</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>coding</category>
    </item>
    <item>
      <title>How to develop a UI and an API at the same time, with no headaches</title>
      <dc:creator>Patrice Ferlet</dc:creator>
      <pubDate>Wed, 08 Mar 2023 10:12:12 +0000</pubDate>
      <link>https://forem.com/metal3d/how-to-develop-a-ui-and-an-api-at-the-same-time-with-no-headaches-1gol</link>
      <guid>https://forem.com/metal3d/how-to-develop-a-ui-and-an-api-at-the-same-time-with-no-headaches-1gol</guid>
      <description>&lt;p&gt;You develop a WEB interface with ParcelJS, Angular, Vue, React... And of course, this interface will have to contact the API that you code in parallel. So you'll spend a little time fighting with CORS headers, having to configure the access point to take into account the production environment...&lt;/p&gt;

&lt;p&gt;It's a shame, though. I am a strong supporter of the separation of view and control, and I think that a reverse-proxy is of course important in production. But, you are certainly coding your backend with a language and/or framework that knows how to serve WEB pages and static content.&lt;/p&gt;

&lt;p&gt;This article shows the method with ParcelJS, but it will be easy enough to adapt it for any other bundler that allows to do automatic build. The main idea, as you will have understood, is not to use the built-in web server, but to make your backend application do it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "bad" workflow
&lt;/h2&gt;

&lt;p&gt;Before seeing the method I prefer to use, let me show you what I often see in many projects. The &lt;em&gt;bad&lt;/em&gt; method...&lt;/p&gt;

&lt;p&gt;Using ParcelJS, it makes it very easy to start developing an interface. Because &lt;code&gt;parcel&lt;/code&gt; builds the application and use a "live reload" system. So, using &lt;code&gt;parcel&lt;/code&gt; or &lt;code&gt;parcel serve&lt;/code&gt; is simple.&lt;/p&gt;

&lt;p&gt;This will make parcel start a WEB service on port &lt;code&gt;1234&lt;/code&gt;, we just have to go to the page &lt;a href="https://localhost:1234" rel="noopener noreferrer"&gt;https://localhost:1234&lt;/a&gt; and that's it.&lt;/p&gt;

&lt;p&gt;But what we are interested in is that our WEB application connects to an API that we develop in Go or Python, for example.&lt;/p&gt;

&lt;p&gt;Obviously, the API will listen to &lt;strong&gt;another port&lt;/strong&gt;, and this gets complicated.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Above all, the application should be configured to connect to the port of the API, in the development phase this is different from the production phase&lt;/li&gt;
&lt;li&gt;but even if we work locally, it is likely that your navigator prevents access to the API due to the management of "Cross Origin" headers (CORS).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And then, usually, I see project managers to install a reverse proxy, manipulating headers, making the Web UI to be able to connect the API to another port, and of course using a "settings" file that is changed from developmment environment to production.&lt;/p&gt;

&lt;p&gt;That's a bit complex, isn't it?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;So what can we do? How to ease the developmment phase?&lt;/p&gt;

&lt;p&gt;We make our API the web server!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And you'll see that this will resolve both development and production builds. Yeah.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's go for a little project
&lt;/h2&gt;

&lt;p&gt;We start by creating a working directory, and we will do this, for example, in Go (we will see later for the Python with Flask)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/Project/demo-web1
&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;
&lt;span class="c"&gt;# we create the "ui" here&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ui
&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;
&lt;span class="c"&gt;# then create the project&lt;/span&gt;
yarn init
yarn add &lt;span class="nt"&gt;--dev&lt;/span&gt; parcel rimraf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, you can add TypeScript, ESLint, and whatever the tools you need.&lt;/p&gt;

&lt;p&gt;Then, change the &lt;code&gt;package.json&lt;/code&gt; file to set up scripts and the "source" entrypoint + add scripts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;//...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src/index.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;//...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rimraf dist .parcel-cache; parcel watch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rimraf dist .parcel-cache; parcel build"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;//...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;It's important to change the "main" entry point to "source" and to point it on the "index.html" file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;OK, now create &lt;code&gt;src/index.html&lt;/code&gt; page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Exemple&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"./main.js"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Exemple&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"message"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Click me&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, create this script (&lt;code&gt;src/main.js&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// call the /api/demo&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;callApi&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/demo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DOMContentLoaded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callApi&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note that the call with &lt;code&gt;fetch&lt;/code&gt; does not change the port, does not provide a particular url, it will be a call made on the same host. Since we will be serving the application via our API&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, at this time, you should have this structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;└── ui
    ├── node_modules
    ├── package.json
    ├── src
    └── yarn.lock
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Did you realize that we don't use &lt;code&gt;parcel&lt;/code&gt; or &lt;code&gt;parcel serve&lt;/code&gt; but &lt;code&gt;parcel watch&lt;/code&gt; ?&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We now don't ask parcel to "serve" the interface, we only need to build tha application to &lt;code&gt;dist&lt;/code&gt; folder. And we will serve this with our API/Server! That's all!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;It's time to create the API and server.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's go up a level to &lt;code&gt;demo-web1&lt;/code&gt; – we'll create the API here, but feel free to create a subdirectory if you like. You'll just have to change the path of the static folder.&lt;/p&gt;

&lt;p&gt;In short!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# if you're still in the ui directory, go up&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ..

&lt;span class="c"&gt;# create the api&lt;/span&gt;
go mod init example
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here, create a &lt;code&gt;main.go&lt;/code&gt; file that contains this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;
    &lt;span class="s"&gt;"time"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c"&gt;// respond to /api/demo, send the date in json format&lt;/span&gt;
    &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/demo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusOK&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;`{"date": "%s"}`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RFC3339&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="c"&gt;// serve the ui/dist directory as static&lt;/span&gt;
    &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FileServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Dir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ui/dist"&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

    &lt;span class="c"&gt;// start the server&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Starting server on port 8080"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should now have this project structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── go.mod
├── main.go
└── ui
    ├── node_modules
    ├── package.json
    ├── src
    │   ├── index.html
    │   └── main.js
    └── yarn.lock
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now access two endpoints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"&lt;code&gt;/api/demo&lt;/code&gt;" returns the date (yes, it's fast-coded, we could use the &lt;code&gt;encoding/json&lt;/code&gt; package, but that's just to show you)&lt;/li&gt;
&lt;li&gt;any other path will try to go to &lt;code&gt;ui/dist&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;ui/dist&lt;/code&gt; is where &lt;code&gt;parcel&lt;/code&gt; will transpile our web interface - it doesn't exists yet, but it will be there.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;OK, so now we can make sure that our UI is completed at each modification, on one side, and we launch the API on the other. To restart the Go application at each change, I often use &lt;code&gt;entr&lt;/code&gt; which is very easy to use.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The "entr" command which is a standard package in Linux (install this with &lt;code&gt;dnf&lt;/code&gt; or &lt;code&gt;apt&lt;/code&gt;) and could be found for Mac. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We will now run two commands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one that will compile the web interface code continuously, at each change&lt;/li&gt;
&lt;li&gt;another that will simply recompile the API&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They have to run in parallel, so we will need two terminals for the moment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# in the first terminal&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;ui &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; yarn run start

&lt;span class="c"&gt;# in the second terminal&lt;/span&gt;
find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.go"&lt;/span&gt; | entr &lt;span class="nt"&gt;-r&lt;/span&gt; go run main.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;For those who have &lt;code&gt;tmux&lt;/code&gt; on their machine, a nice little trick is to run the following command. It can work perfectly well in a &lt;code&gt;Makefile&lt;/code&gt; by the way&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tmux new-session \
    "cd ui &amp;amp;&amp;amp; yarn run start;" \
    split-window -h \
    "find . -name '*.go' | entr -r go run main.go"
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;p&gt;Then go to &lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt;, click on the button, and the date is displayed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Same with Flask
&lt;/h2&gt;

&lt;p&gt;With Python and Flask (or Quart), you can do the same.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can, if you want, remove the &lt;code&gt;go.mod&lt;/code&gt; and &lt;code&gt;main.go&lt;/code&gt; file if you created them earlier.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;OK, let's create a &lt;code&gt;virtualenv&lt;/code&gt;, and install Flask.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ensure you're not in "ui" directory then&lt;/span&gt;

python &lt;span class="nt"&gt;-mvenv&lt;/span&gt; venv
&lt;span class="nb"&gt;source &lt;/span&gt;venv/bin/activate
pip &lt;span class="nb"&gt;install &lt;/span&gt;flask
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's now OK – time to create our application.&lt;/p&gt;

&lt;p&gt;Create the &lt;code&gt;main.py&lt;/code&gt; file and type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jsonify&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# use ui/dist as static
&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;static_folder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ui/dist&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/api/demo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;demo&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;return the date and hour in json format&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;%d/%m/%Y %H:%M:%S&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&amp;lt;path:path&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Serve static files&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_static_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One more time, start the UI generation, and now the Python service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# in on terminal&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;ui &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; yarn run start

&lt;span class="c"&gt;# in anther one (ensure you activated the venv before)&lt;/span&gt;
python main.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And go to &lt;a href="http://localhost:5000" rel="noopener noreferrer"&gt;http://localhost:5000&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In debug mode, Flask reloads the server when Python sources changes. No need to use &lt;code&gt;entr&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One more time, any changes in the UI is automatically compiled.&lt;/p&gt;

&lt;h2&gt;
  
  
  And so, for deployment ?
&lt;/h2&gt;

&lt;p&gt;Because the &lt;code&gt;parcel watch&lt;/code&gt; process compiles everythin in &lt;code&gt;dist&lt;/code&gt; directory, you will only need to &lt;code&gt;build&lt;/code&gt; sources. The application, whatever if you use Go, Python, or any other language, will find the static files.&lt;/p&gt;

&lt;p&gt;It's very cool when we want to use Docker (or Podman) in multistage build:&lt;/p&gt;

&lt;p&gt;For the Go version, we can use a "scratch" image to only get UI and server binary. No need to get a Linux distribution.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;js-build&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ./ui /app&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-xe&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;cd&lt;/span&gt; /app&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    yarn &lt;span class="nb"&gt;install&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    yarn run build&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;golang:alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;go-build&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ./ /app&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-xe&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;cd&lt;/span&gt; /app&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    go mod tidy&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nv"&gt;CGO_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 go build &lt;span class="nt"&gt;-o&lt;/span&gt; app.bin &lt;span class="k"&gt;*&lt;/span&gt;.go

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; scratch&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=js-build /app /app/ui&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=go-build /app/app.bin /app/app.bin&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8080&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["/app/app.bin"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Python, we can make approximately the same or use &lt;code&gt;gunicorn&lt;/code&gt; to serve the application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;js-build&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ./ui /app&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-xe&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;cd&lt;/span&gt; /app&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    yarn &lt;span class="nb"&gt;install&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    yarn run build&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:alpine&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ./ /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=js-build /app /app/ui&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-xe&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    pip &lt;span class="nb"&gt;install &lt;/span&gt;flask gunicorn

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8000&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["gunicorn", "main:app"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This way of doing things only works if you use a language or framework that is capable of serving static content, and if the WEB interface is coded with a tool that knows how to compile continuously without necessarily having a Web service.&lt;/p&gt;

&lt;p&gt;Thus, we eliminate PHP... Because with it, it is necessary, in most cases, to use a Web server (nginx + FPM or Apache).&lt;/p&gt;

&lt;p&gt;On the other hand, with Go, Python, Rust, or even Ruby, everything will be fine because these languages are stand-alone.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I have seen, too often, many engineers, both beginners and experienced, launch a bundler in web server mode and struggle with configuration management + CORS in development mode.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The fear of using the API service to provide the pages is still big. Indeed, we can think that it would be good to use a "real" HTTP server to provide the WEB pages later (with a  reverse proxy for example)&lt;/p&gt;

&lt;p&gt;However, to go fast, for a small project, or simply to propose a first iteration of the application, this method that I propose is more than enough. And it has the merit to be adapted to a reverse-proxy.&lt;/p&gt;

&lt;p&gt;Note that it is not forbidden to serve, as I propose in this article, the static pages via the API. In fact, the rule that states that it is imperative to go through a reverse-proxy is a bit outdated, because modern languages and current fameworks are quite good in this exercise.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Don't make me say what I didn't say, an upstream reverse-proxy is of course a good security guarantee. It will allow you to manage the TLS/SSL, the cache, some access rules or the load balancing!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But this reverse-proxy can perfectly call the API to have the static pages, to cache them, instead of reading them directly from the disk. This will also allow you to manage templating or content patching from your API. So, don't forbid yourself to do it.&lt;/p&gt;

&lt;p&gt;In short, there is nothing wrong (really, nothing wrong at all) with letting the libraries provide the static pages, if only for a while.&lt;/p&gt;

</description>
      <category>software</category>
      <category>productivity</category>
      <category>learning</category>
      <category>git</category>
    </item>
    <item>
      <title>Vue + TS without class component ? No way!</title>
      <dc:creator>Patrice Ferlet</dc:creator>
      <pubDate>Thu, 15 Sep 2022 12:47:39 +0000</pubDate>
      <link>https://forem.com/metal3d/vue-ts-without-class-component-no-way-ic3</link>
      <guid>https://forem.com/metal3d/vue-ts-without-class-component-no-way-ic3</guid>
      <description>&lt;p&gt;It is with a huge disappointment that I almost stopped using Vue, because TypeScript support is oriented towards the Composition API, abandoning Class Components. Fortunately, all is not lost!&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# create a Vite based project (official way)
npm init vue@3

# install vue-facing-decorator
# cd [to the project], then
npm -i --save vue-facing-decorator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"onStuff"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Click me&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;// use the modul to import Component and Vue&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Prop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Vue&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vue-facing-decorator&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// and you can now use class component in Vue3 / Vite projects&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyComponent&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Vue&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Property from the component tag&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Prop&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;

    &lt;span class="c1"&gt;// public methods and properties can be &lt;/span&gt;
    &lt;span class="c1"&gt;// acccessed from the template too&lt;/span&gt;
    &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="nf"&gt;onStuff&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// example of handler...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;No need of "hooks", it's pure TS.&lt;/p&gt;
&lt;h2&gt;
  
  
  I love TS, and I love Vue
&lt;/h2&gt;

&lt;p&gt;I develop, among other things, Web applications with mainly 3 frameworks. One of which leaves me speechless:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vue&lt;/li&gt;
&lt;li&gt;Angular&lt;/li&gt;
&lt;li&gt;React (I don't like...)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;You notice that I did not suffix the names with "JS", because I use mostly TypeScript.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I use TypeScript for a reason, I like a component to be defined as a class, to have properties and methods, and to use the principles of encapsulation and variable scope. So, in essence, I like OOP, and I'm absolutely a fan of having type checking within TypeScript. It makes the code much more secure.&lt;/p&gt;

&lt;p&gt;Previously, using &lt;code&gt;vue-cli&lt;/code&gt; to create my projects, &lt;a href="https://class-component.vuejs.org/guide/installation.html#vue-cli-setup" rel="noopener noreferrer"&gt;I was able to activate TypeScript and "class component" syntax&lt;/a&gt;. I was so happy.&lt;/p&gt;

&lt;p&gt;So, Vue with TypeScript was closed to what I can do in Angular:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;// with vue-class-component&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Options&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyComponent&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Vue&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;myData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="nf"&gt;myMehtod&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="c1"&gt;// code here...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Everything was fine until this thunderclap... &lt;/p&gt;

&lt;p&gt;Today, &lt;a href="https://cli.vuejs.org/" rel="noopener noreferrer"&gt;&lt;code&gt;vue-cli&lt;/code&gt; is deprecated&lt;/a&gt; to create a new project. The new way is to use &lt;code&gt;Vite&lt;/code&gt; and it does'nt provides &lt;code&gt;vue-class-component&lt;/code&gt; module and there is no option to force the "old way".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6l57kx3amburocbkmq12.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6l57kx3amburocbkmq12.png" alt="Vue-Cli is now in maintenace mode" width="800" height="118"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And adding the module fails if the project is Vite based.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Vue(TS) is abandoning &lt;code&gt;vue-class-component&lt;/code&gt;, preferring to ask developers to use the &lt;a href="https://vuejs.org/guide/extras/composition-api-faq.html#what-is-composition-api" rel="noopener noreferrer"&gt;Composition API&lt;/a&gt;. &lt;br&gt;
See &lt;a href="https://github.com/vuejs/vue-class-component/issues/569" rel="noopener noreferrer"&gt;https://github.com/vuejs/vue-class-component/issues/569&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And, if you're not sure that Vue doesn't like class (exactly like React, and it's a shame in my opinion), read this:&lt;br&gt;
&lt;a href="https://vuejs.org/guide/extras/composition-api-faq.html#will-options-api-be-deprecated" rel="noopener noreferrer"&gt;https://vuejs.org/guide/extras/composition-api-faq.html#will-options-api-be-deprecated&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvpgxp9gaxcyfgu8fv56a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvpgxp9gaxcyfgu8fv56a.png" alt="It's now clear, Vue doesn't appreciate class components" width="658" height="148"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So... Here is the "new appreciated way to do" so far:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;setup&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;// now...&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;reactive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// or raeactive()&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;myMethod&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// code here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;What's the problem dude?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You probably think it's cool. It's easier to write the component logic for those who used to work in JS.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;And, be honnest, it's now closed to what we can see in React.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And I don't like React. I've got many reasons that I will expose in a future article. But the substance is this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We lose the notion that a component is an "object". We also need to call functions to declare that a variable is reactive, and we need a "JS" syntax to create &lt;code&gt;computed&lt;/code&gt; functions for example.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's not pretty.&lt;/p&gt;

&lt;p&gt;To illustrate what I mean:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This is somehting I prefer&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Example&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Vue&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;aMethod&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
        &lt;span class="c1"&gt;// this method can be called in my template&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// make a computed "message" variable&lt;/span&gt;
    &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// This is what to do in a "setup" tag&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The Title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// make a computed "message" variable&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; 
    &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;val&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()},&lt;/span&gt;
    &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Of course, there is less of code lines. But the readability is bad (my opinion, of course).&lt;/p&gt;

&lt;p&gt;The good paradigm I have in mind is that &lt;strong&gt;a component is an object that I can instantiate&lt;/strong&gt;. So, it &lt;strong&gt;must be a class&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If I want a poor support of TypeScript, using hooks or functions to declare typed objects, reactive (or ref for primitives) or computed variables, so I can use React... And, as said earlier, I don't like React.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  The solution!
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;By chance, I'm not the only one who loves to work with TypeScript using the strongness of the langage. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There is a fantastic module named &lt;code&gt;vue-facing-decorator&lt;/code&gt; here: &lt;a href="https://facing-dev.github.io/vue-facing-decorator/" rel="noopener noreferrer"&gt;https://facing-dev.github.io/vue-facing-decorator/&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/facing-dev" rel="noopener noreferrer"&gt;
        facing-dev
      &lt;/a&gt; / &lt;a href="https://github.com/facing-dev/vue-facing-decorator" rel="noopener noreferrer"&gt;
        vue-facing-decorator
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Vue typescript class component decorators
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Read me&lt;/h1&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/139db2142b76485dde712d8ff3099da25b64c94f21ae257f29c851f929f9ef1d/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f666163696e672d6465762f7675652d666163696e672d6465636f7261746f72"&gt;&lt;img src="https://camo.githubusercontent.com/139db2142b76485dde712d8ff3099da25b64c94f21ae257f29c851f929f9ef1d/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f666163696e672d6465762f7675652d666163696e672d6465636f7261746f72" alt="GitHub"&gt;&lt;/a&gt; &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/35e9ee10f25229e415047dc6294e08f30173d58369a47c1e0f1490e8095fa9d7/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f7675652d666163696e672d6465636f7261746f72"&gt;&lt;img src="https://camo.githubusercontent.com/35e9ee10f25229e415047dc6294e08f30173d58369a47c1e0f1490e8095fa9d7/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f7675652d666163696e672d6465636f7261746f72" alt="npm"&gt;&lt;/a&gt; &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/5ffad96fc9d785542fff159c8a436fedebb0d76a7dca0d7fbdc31434652aed44/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f646570656e64656e63792d76657273696f6e2f7675652d666163696e672d6465636f7261746f722f706565722f767565"&gt;&lt;img src="https://camo.githubusercontent.com/5ffad96fc9d785542fff159c8a436fedebb0d76a7dca0d7fbdc31434652aed44/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f646570656e64656e63792d76657273696f6e2f7675652d666163696e672d6465636f7261746f722f706565722f767565" alt="npm peer dependency version (scoped)"&gt;&lt;/a&gt; &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/ce1380526596c783299028b533a36235aa64de8ee70456f1ba71b7d0ac4c2ad1/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c54532d70726570617265642d626c7565"&gt;&lt;img src="https://camo.githubusercontent.com/ce1380526596c783299028b533a36235aa64de8ee70456f1ba71b7d0ac4c2ad1/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c54532d70726570617265642d626c7565" alt="lts"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Designed for vue 3, do the same work like &lt;a href="https://github.com/vuejs/vue-class-component" rel="noopener noreferrer"&gt;vue-class-component&lt;/a&gt; and &lt;a href="https://github.com/kaorun343/vue-property-decorator" rel="noopener noreferrer"&gt;vue-property-decorator&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Community desired vue class component with typescript decorators.&lt;/li&gt;
&lt;li&gt;Compatible with both stage3 and stage2 decorators.&lt;/li&gt;
&lt;li&gt;Compatible with both TypeScript and JavaScript projects.&lt;/li&gt;
&lt;li&gt;Safety. Transform ES class to vue option api according to specifications.&lt;/li&gt;
&lt;li&gt;Performance. Once transform on project loading, ready for everywhere.&lt;/li&gt;
&lt;li&gt;Support ES class inheritance, vue &lt;code&gt;extends&lt;/code&gt; and vue &lt;code&gt;mixins&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://class-component.vuejs.org" rel="nofollow noopener noreferrer"&gt;Official recommended&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Welcome to suggest and contribute.&lt;/p&gt;




&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Donate&lt;/h1&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://opencollective.com/vue-facing-decorator" rel="nofollow noopener noreferrer"&gt;opencollective&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Document&lt;/h1&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://facing-dev.github.io/vue-facing-decorator/#/" rel="nofollow noopener noreferrer"&gt;To document&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Document languages: English, 简体中文, Portuguese&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Wellknown issues&lt;/h1&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://facing-dev.github.io/vue-facing-decorator/#/en/wellknown-issues/wellknown-issues" rel="nofollow noopener noreferrer"&gt;https://facing-dev.github.io/vue-facing-decorator/#/en/wellknown-issues/wellknown-issues&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Discussion&lt;/h1&gt;

&lt;/div&gt;

&lt;p&gt;To discord &lt;a href="https://discord.gg/4exxtFgkcz" rel="nofollow noopener noreferrer"&gt;https://discord.gg/4exxtFgkcz&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;QQ群号 766350254&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Awesome List&lt;/h1&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/wangzhiguoengineer/vuex-facing-decorator" rel="noopener noreferrer"&gt;vuex-facing-decorator&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Binding helpers for Vuex and vue-facing-decorator.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;br&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/facing-dev/vue-facing-decorator" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;And it's very easy to use.&lt;/p&gt;

&lt;p&gt;Create a project with TypeScript support:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm init vue@3
&lt;span class="c"&gt;# ==&amp;gt; and select "TypeScript" support in the wizzard&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, inside the project, install the module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;--save&lt;/span&gt; vue-facing-decorator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;tsconfig.json&lt;/code&gt; file, there is a section named &lt;code&gt;compilerOptions&lt;/code&gt;, only add this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"experimentalDecorators"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;And now, you can  import &lt;code&gt;Component&lt;/code&gt; decorator, and &lt;code&gt;Vue&lt;/code&gt; base class from &lt;code&gt;vue-facing-decorator&lt;/code&gt;. Easy 😄&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How to use?
&lt;/h2&gt;

&lt;p&gt;It's pretty simple. Create a component like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;// example, TodoItem.vue
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="na"&gt;v-model=&lt;/span&gt;&lt;span class="s"&gt;"done"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Vue&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vue-facing-decorator&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TodoItem&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Vue&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;done&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, you can use properties, for example to send a "Todo" object in the &lt;code&gt;&amp;lt;TodoItem :todo="todo" /&amp;gt;&lt;/code&gt; component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;// example, TodoItem.vue
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt; 
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; 
            &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt;
            &lt;span class="na"&gt;v-model=&lt;/span&gt;&lt;span class="s"&gt;"todo.done"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Todo&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/classes/todo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Vue&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vue-facing-decorator&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TodoItem&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Vue&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Prop&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Computed are simple getters and setters (with &lt;code&gt;get&lt;/code&gt; and &lt;code&gt;set&lt;/code&gt; keywords), methods are simple methods, ...&lt;/p&gt;

&lt;p&gt;You can see the documentation to see how to use the others decorators (like &lt;code&gt;@Watch&lt;/code&gt; for example) and the options that you can pass to them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I have absolutely nothing against the fact that you may like the Vue Composition API. It's just a way of coding that doesn't suit me. I have a clear preference for Angular precisely because this Framework uses TypeScript in the most beautiful way.&lt;/p&gt;

&lt;p&gt;Vue, with &lt;code&gt;vue-class-component&lt;/code&gt; brought me the best of both worlds. Beautiful TypeScript, with the ease inherent to Vue. The abandonment of this module almost drove me away.&lt;/p&gt;

&lt;p&gt;But the &lt;code&gt;vue-facing-decorator&lt;/code&gt; module makes up for it in a big way. And I thank the authors of this module.&lt;/p&gt;

&lt;p&gt;Note: There is also another project that provides class component at setup stage. I'm not satisfied, but maybe you can appreciate: &lt;a href="https://github.com/fmfe/vue-class-setup" rel="noopener noreferrer"&gt;https://github.com/fmfe/vue-class-setup&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vue</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Create Nginx extensions in JavaScript</title>
      <dc:creator>Patrice Ferlet</dc:creator>
      <pubDate>Sat, 23 Jul 2022 06:29:37 +0000</pubDate>
      <link>https://forem.com/metal3d/create-nginx-extensions-in-javascript-3310</link>
      <guid>https://forem.com/metal3d/create-nginx-extensions-in-javascript-3310</guid>
      <description>&lt;p&gt;If you need to adapt content, authorize, filter, change the behavior of Nginx, so &lt;code&gt;njs&lt;/code&gt; can be the solution. Nginx proposes a JavaScript backend to manipulate requests and responses and even streams. This is &lt;a href="https://nginx.org/en/docs/njs/" rel="noopener noreferrer"&gt;&lt;code&gt;njs&lt;/code&gt;&lt;/a&gt; and it is very powerful.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nginx.org/" rel="noopener noreferrer"&gt;Nginx&lt;/a&gt; is a very good HTTP server and reverse proxy. It is simple to configure, easy to start, and it "does the job". We often use it as a proxy to backends. But, sometimes, you can feel some limitations. For example if you need to authorize users to several backends with a specific identity provider with a weird API. Or, sometimes, this is the backend which is problematic.&lt;/p&gt;

&lt;p&gt;For example, I serve a Rancher backend, and Rancher provides  its &lt;code&gt;kubeconfig&lt;/code&gt; YAML file (dynamically generated for each user) with a certificate inside that I need to remove.&lt;/p&gt;

&lt;p&gt;In short, I need to "filter" the content, because I cannot change the behavior of the backend. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;And this is exactly when &lt;code&gt;njs&lt;/code&gt; can be used!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What I could do is simply to detect that the user claims the KubeConfig file, remove the certificate entry in the YAML, and serve the response file. This is one of the vast variety of manipulation that you can do with &lt;code&gt;njs&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;What it &lt;code&gt;njs&lt;/code&gt;? &lt;/p&gt;

&lt;p&gt;&lt;code&gt;njs&lt;/code&gt; is a JavaScript engine integrated to Nginx. &lt;code&gt;njs&lt;/code&gt; provides a different approach that starts a JS VM on each needed process. It actually can use common JavaScript modules (like &lt;code&gt;fs&lt;/code&gt; for example), and it's ES6 compliant. That means that you will code the scripts without the need of changing your habits.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can do
&lt;/h2&gt;

&lt;p&gt;There are plenty of things that you can do with &lt;code&gt;njs&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make authorization (with or without backend)&lt;/li&gt;
&lt;li&gt;Manipulate the output content, headers, status...&lt;/li&gt;
&lt;li&gt;Interact with streams&lt;/li&gt;
&lt;li&gt;Use cryptography&lt;/li&gt;
&lt;li&gt;And so on...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You must keep in mind that &lt;code&gt;njs&lt;/code&gt; is not intend to create an application. Nginx is not an application server, it's a web server and a reverse proxy. So, it will not substitute a "real" web framework. But, it will help to fix some things that are hard to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read this before!
&lt;/h2&gt;

&lt;p&gt;Go to &lt;a href="https://nginx.org/en/docs/njs/reference.html" rel="noopener noreferrer"&gt;the documentation page&lt;/a&gt; here to check the global variables which are already defined by Nginx/njs, you'll see that there are many methods to trace, crypt, decode, or manipulate the requests. &lt;/p&gt;

&lt;p&gt;Do not spend too much of time to read the page, but just take a look to be aware of the possibilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's not activated by default
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;njs&lt;/code&gt; is not activated by default. It's a module that you need to load at startup.&lt;/p&gt;

&lt;p&gt;The legacy method to activate it is to add &lt;code&gt;load_module modules/ngx_http_js_module.so;&lt;/code&gt; on top of &lt;code&gt;nginx.conf&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;For docker, you can change the "command" to force the module to be loaded:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run --rm -it nginx:alpine \
nginx -g "daemon off; load_module modules/ngx_http_js_module.so;"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in your &lt;code&gt;http&lt;/code&gt; services, you can now load a script and call functions for different situation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prepare the tests
&lt;/h2&gt;

&lt;p&gt;To follow this article, and be able to test, we will start a Nginx service and a "Ghost" blog engine.&lt;/p&gt;

&lt;p&gt;This will help to startup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/Projects/nginx-tests
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/Projects/nginx-tests
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; nginx/conf.d
&lt;span class="nb"&gt;touch &lt;/span&gt;nginx/conf.d/default.conf

&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; | tee docker-compose.yaml
version: "3"
services:
  # our nginx reverse proxy
  # with njs activated
  http:
    image: nginx:alpine
    command: nginx -g "daemon off; load_module modules/ngx_http_js_module.so;"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d:z
    ports:
      - 80:80

  # a backend
  ghost:
    image: ghost
    depends_on:
      - http
    environment:
      url: http://example.localhost
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Debug
&lt;/h2&gt;

&lt;p&gt;Sometimes, your script fails to return somthing, you've got an error 500 and nothing is clearly displayd in the nginx logs.&lt;/p&gt;

&lt;p&gt;What you can do is to add this in the "location":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;error_log&lt;/span&gt; &lt;span class="n"&gt;/var/log/nginx/error.log&lt;/span&gt; &lt;span class="s"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For docker/podman users, use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# see the logs in real time&lt;/span&gt;
docker-compose logs &lt;span class="nt"&gt;-f&lt;/span&gt; http
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you can use in your script :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;something here&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;njs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;variable&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you finally found the problems, you can then remove the "debug" suffix of the "&lt;code&gt;error_log&lt;/code&gt;" directive.&lt;/p&gt;

&lt;h2&gt;
  
  
  There are many ways, many options, many situations
&lt;/h2&gt;

&lt;p&gt;Nginx manages 2 types of configuration: http and stream. By chance, &lt;code&gt;njs&lt;/code&gt; can be used for both.&lt;/p&gt;

&lt;p&gt;In this article, we will only speak about the "http" mode.&lt;/p&gt;

&lt;p&gt;When we work with "http" mode, we can tell to Nginx to use JavaScript to manipulate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The content (that means that we will generate the entire request before the call)&lt;/li&gt;
&lt;li&gt;The headers only (after request)&lt;/li&gt;
&lt;li&gt;The body only (after request)&lt;/li&gt;
&lt;li&gt;Set variables&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The pipeline is not hard: import a javascript file, then use a Nginx directive to use a function for a situation. Nothing more.&lt;/p&gt;

&lt;h2&gt;
  
  
  First example, create a content
&lt;/h2&gt;

&lt;p&gt;A first, let's create a file named &lt;code&gt;example.js&lt;/code&gt;. To make it easy to test, put this in &lt;code&gt;nginx/conf.d/example.js&lt;/code&gt; – in production environment, it's better to change the location of your scripts.&lt;/p&gt;

&lt;p&gt;OK, so, this is the content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;information&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;world&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// make the function(s) available&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;information&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;r&lt;/code&gt; variable (argument of the function) is a "request" object provided by Nginx. There are many methods and properties that we can use, here we only use &lt;code&gt;r.return()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It's now time to make the JavaScript function to be called as the content maker. In &lt;code&gt;nginx/conf.d/default.conf&lt;/code&gt;, append this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;js_import&lt;/span&gt; &lt;span class="n"&gt;/etc/nginx/conf.d/example.js&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;example.localhost&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;js_content&lt;/span&gt; &lt;span class="s"&gt;example.information&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yes, that's all. We import the script, and we use the function as &lt;code&gt;js_content&lt;/code&gt;. That means that Nginx will release the request to the script, and the script can yield the content.&lt;/p&gt;

&lt;p&gt;Start (or restart) the container and hit the "example.localhost" domain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;curl example.localhost
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"hello"&lt;/span&gt;: &lt;span class="s2"&gt;"world"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the first example. Here, we only generate a JSON output.&lt;/p&gt;

&lt;p&gt;We can now do some nice things, like replacing the content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Replacing the content
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;njs&lt;/code&gt; proposes several &lt;code&gt;fetch&lt;/code&gt; APIs to get, sub-request or internally redirect the request.&lt;/p&gt;

&lt;p&gt;In this example, we will replace the "/about/" page content by inserting a message inside.&lt;/p&gt;

&lt;p&gt;The following configuration is not perfect (actually we could match &lt;code&gt;location /about&lt;/code&gt; and call our JavaScript, but I want to show you the internal redirection) — but it will show you some cool stuffs that &lt;code&gt;njs&lt;/code&gt; allows.&lt;/p&gt;

&lt;p&gt;Change the &lt;code&gt;nginx/conf.d/default.conf&lt;/code&gt; file with this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;js_import&lt;/span&gt; &lt;span class="n"&gt;/etc/nginx/conf.d/example.js&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;upstream&lt;/span&gt; &lt;span class="s"&gt;ghost_backend&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="nf"&gt;ghost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2368&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;example.localhost&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;gunzip&lt;/span&gt; &lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;# call our js for /exemple url&lt;/span&gt;
    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;/exemple&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;js_content&lt;/span&gt; &lt;span class="s"&gt;example.information&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# match everything&lt;/span&gt;
    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;js_content&lt;/span&gt; &lt;span class="s"&gt;example.replaceAboutPage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# call the "ghost blog" backend&lt;/span&gt;
    &lt;span class="c1"&gt;# note the "arobase" that creates a named location&lt;/span&gt;
    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="s"&gt;@ghost_backend&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;# important !&lt;/span&gt;
        &lt;span class="kn"&gt;subrequest_output_buffer_size&lt;/span&gt; &lt;span class="mi"&gt;16k&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://ghost_backend&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt; &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_buffering&lt;/span&gt; &lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this file, you have to pay attention on this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We force "gunzip" to uncompress proxied responses. This is important if the backend forces &lt;code&gt;gzip&lt;/code&gt; responses, and you want to make replacements.&lt;br&gt;
We also make the "subrequest output buffer" size to 16k, you can set it to 128k if needed (for large CSS files if you subrequest them for example).&lt;/p&gt;

&lt;p&gt;This is important because we will make subrequests later, and the buffer will be filled too fast&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then, in the &lt;code&gt;example.js&lt;/code&gt; file, add the &lt;code&gt;replaceAboutPage&lt;/code&gt; function and export it :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;information&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;world&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;replaceAboutPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/about/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headersOut&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;content-type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Changed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;internalRedirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@ghost_backend&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;information&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;replaceAboutPage&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Take a minute to read the &lt;code&gt;replaceAboutPage&lt;/code&gt; function. In this example, we only:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;return a page with "Changed" inside if the URI is "/about"&lt;/li&gt;
&lt;li&gt;either we use &lt;code&gt;@ghost_backend&lt;/code&gt; location to proxy the blog&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Restart the nginx container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose restart http
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And visit &lt;a href="http://example.locahost" rel="noopener noreferrer"&gt;http://example.locahost&lt;/a&gt;. Then go to the "/about" page using the "About" link on top.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft9sdzflmlqpqhyyz5n51.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft9sdzflmlqpqhyyz5n51.png" alt="We changed the about page" width="481" height="257"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nice, so now, we can do a better replacement.&lt;/p&gt;

&lt;p&gt;We will need to "subrequest" the page. But a subrequest needs a "real URI path". So, let's add a location in &lt;code&gt;default.conf&lt;/code&gt; first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="c1"&gt;# a reversed uri.&lt;/span&gt;
&lt;span class="c1"&gt;# We remove the prefix and use the @ghost_backend&lt;/span&gt;
&lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/__reversed&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;internal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;rewrite&lt;/span&gt; &lt;span class="s"&gt;^/__reversed/(.*)&lt;/span&gt;$ &lt;span class="n"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="s"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;try_files&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt; &lt;span class="s"&gt;@ghost_backend&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, let's go to &lt;code&gt;example.js&lt;/code&gt; and replace the &lt;code&gt;replaceAboutPage&lt;/code&gt; function to this one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;replaceAboutPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/about/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subrequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/__reversed&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// call the reversed url&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// copy the response headersOut&lt;/span&gt;
        &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headersOut&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headersOut&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headersOut&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="c1"&gt;// replace the end of "header" tag to append a title&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;responseBuffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;responseBuffer&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/header&amp;gt;&amp;lt;h1&amp;gt;Reversed&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;responseBuffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Internal Server Error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// in any other case&lt;/span&gt;
    &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;internalRedirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@ghost_backend&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One more time, restart &lt;code&gt;http&lt;/code&gt; container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose restart http
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then visit &lt;a href="http://example.locahost/about/" rel="noopener noreferrer"&gt;http://example.locahost/about/&lt;/a&gt; – you should see:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj13dj8r77ejwnevhre9s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj13dj8r77ejwnevhre9s.png" alt="The modified about page" width="800" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Second method, use &lt;code&gt;js_body_filter&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;A probably better solution is to use &lt;code&gt;js_body_filter&lt;/code&gt; instead of &lt;code&gt;js_content&lt;/code&gt;. This leaves Nginx makes the proxy pass, then we can manipulate the body.&lt;/p&gt;

&lt;p&gt;So, let's change the &lt;code&gt;default.conf&lt;/code&gt; file to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;js_import&lt;/span&gt; &lt;span class="n"&gt;/etc/nginx/conf.d/example.js&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;upstream&lt;/span&gt; &lt;span class="s"&gt;ghost_backend&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="nf"&gt;ghost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2368&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;example.localhost&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;gunzip&lt;/span&gt; &lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;# call our js for /exemple url&lt;/span&gt;
    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;/exemple&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;js_content&lt;/span&gt; &lt;span class="s"&gt;example.information&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# call the "ghost blog" backend&lt;/span&gt;
    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;js_body_filter&lt;/span&gt; &lt;span class="s"&gt;example.replaceAboutPage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;subrequest_output_buffer_size&lt;/span&gt; &lt;span class="mi"&gt;16k&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://ghost_backend&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Accept-Encoding&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt; &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_buffering&lt;/span&gt; &lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Very important, here we force the &lt;code&gt;Accept-Encoding&lt;/code&gt; to be empty, because Ghost will return a gzipped content that is impossible (at this time) to decompress from javascript.&lt;/p&gt;

&lt;p&gt;Then change the JavaScript &lt;code&gt;replaceAboutPage&lt;/code&gt; function to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;replaceAboutPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/about/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/header&amp;gt;Reversed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;r&lt;/code&gt; is the request&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;data&lt;/code&gt; is the data to send, here it's the content taken from the Ghost backend&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;flags&lt;/code&gt; is an object with "last" flags set to &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The function needs to use &lt;code&gt;sendBuffer()&lt;/code&gt; to send the data to the client.&lt;/p&gt;

&lt;p&gt;Of course there are many others things to do, like changing the "Content-Length" header, but this works.&lt;/p&gt;

&lt;p&gt;It's very efficient and that's a good method to make some replacement, content checks or fixes to a response without the need to make a &lt;code&gt;subrequest&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make a &lt;code&gt;fetch&lt;/code&gt; to outside
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;njs&lt;/code&gt; provides a global &lt;code&gt;ngx&lt;/code&gt; variable. This object proposes the &lt;code&gt;fetch&lt;/code&gt; API which is more or less the same as you can find in modern browsers.&lt;/p&gt;

&lt;p&gt;Let's add a method to call the Dev.to API.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Please, do not abuse the API, it's a simple example, and you are pleased to not overload the servers&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The following function will get the list of my articles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getMetal3D&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ngx&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://dev.to/api/articles?username=metal3d&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;User-Agent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Nginx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;titles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;article&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headersOut&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;titles&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// don't forget to export functions&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;information&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;replaceAboutPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getMetal3D&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OK, now you think that you'll only need to add a &lt;code&gt;js_content&lt;/code&gt; call inside the &lt;code&gt;default.conf&lt;/code&gt; file. But... there will be some problems.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ngx&lt;/code&gt; object need a "resolver" to know how to resolve the domain name&lt;/li&gt;
&lt;li&gt;also, you need to define where it can find certificate authorities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But, that's not so hard to do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/metal3d&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;resolver&lt;/span&gt; &lt;span class="mf"&gt;9.9&lt;/span&gt;&lt;span class="s"&gt;.9.9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;js_fetch_trusted_certificate&lt;/span&gt; &lt;span class="n"&gt;/etc/ssl/certs/ca-certificates.crt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;js_content&lt;/span&gt; &lt;span class="s"&gt;example.getMetal3D&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, one more time, restart the &lt;code&gt;http&lt;/code&gt; container and call the &lt;code&gt;/metal3d&lt;/code&gt; URI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose restart http

curl http://example.localhost/metal3d

&lt;span class="c"&gt;# response:&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;
    &lt;span class="s2"&gt;"Python, the usefulness of &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;dataclass&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;,
    &lt;span class="s2"&gt;"Fixing a Promise return object to be typed in Typescript"&lt;/span&gt;,
    &lt;span class="s2"&gt;"Flask / Quart - manage module loading and splitting"&lt;/span&gt;,
    &lt;span class="s2"&gt;"Change local container http(s) reverse proxy to Pathwae"&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Nginx proposes a very useful JavaScript extension system. It's easy to realize the large possibilities that can be made with it.&lt;/p&gt;

&lt;p&gt;It's possible to create a custom cache system, to manipulate headers, reading token, validate authorizations, change the content, create a proxy on API, and many others things.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We only checked how to manipulate content, but it's also possible to manipulate "streams" with events.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Go to the documentation pages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NJS doc: &lt;a href="https://nginx.org/en/docs/njs/" rel="noopener noreferrer"&gt;https://nginx.org/en/docs/njs/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;JS object that you can use: &lt;a href="https://nginx.org/en/docs/njs/reference.html" rel="noopener noreferrer"&gt;https://nginx.org/en/docs/njs/reference.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Module directive:

&lt;ul&gt;
&lt;li&gt;http &lt;a href="https://nginx.org/en/docs/http/ngx_http_js_module.html" rel="noopener noreferrer"&gt;https://nginx.org/en/docs/http/ngx_http_js_module.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;stream &lt;a href="https://nginx.org/en/docs/stream/ngx_stream_js_module.html" rel="noopener noreferrer"&gt;https://nginx.org/en/docs/stream/ngx_stream_js_module.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Examples on GitHub: &lt;a href="https://github.com/nginx/njs-examples/" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://github.com/nginx/njs-examples/" rel="noopener noreferrer"&gt;https://github.com/nginx/njs-examples/&lt;/a&gt;
&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>nginx</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Python, the usefulness of "dataclass"</title>
      <dc:creator>Patrice Ferlet</dc:creator>
      <pubDate>Mon, 04 Jul 2022 11:29:26 +0000</pubDate>
      <link>https://forem.com/metal3d/python-the-usefulness-of-dataclass-1m2o</link>
      <guid>https://forem.com/metal3d/python-the-usefulness-of-dataclass-1m2o</guid>
      <description>&lt;p&gt;When you need to describe a "data", you commonly use classes (instead of &lt;code&gt;dict&lt;/code&gt;) – and Python offers a very elegant way to manage this.&lt;/p&gt;

&lt;p&gt;A Data Objects represents data which can be saved inside a database. This concept is in the heart of SQLAlchemy, but as the name should be obvious: it's for SQL Database (in general). Today, there are now document databases too (like MongoDB, ArangoDB, &lt;a href="https://rethinkdb.com" rel="noopener noreferrer"&gt;RethinkDB&lt;/a&gt; that I love so much, or even PostgreSQL). So, a "data" is like a "structured and typed document" that you save "as is". That's not the same paradigm, not the same controls. There are advantages and disadvantages, but we won't debate that here.&lt;/p&gt;

&lt;p&gt;The topic, today, is that Python can help a lot to define our "data classes" in a "generic" way with controls and initialization.&lt;/p&gt;

&lt;p&gt;At this time, you generally use a "pure plain object"… this will change.&lt;/p&gt;

&lt;p&gt;For example, to describe a "user":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
    &lt;span class="n"&gt;level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, you need a bit more controls. You require a constructor to initialize the properties, maybe a setter to make the password and to avoid it to be represented in JSON... &lt;/p&gt;

&lt;p&gt;And there comes "dataclass", a very cool built-in package in Python.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dataclasses?
&lt;/h2&gt;

&lt;p&gt;If you read &lt;a href="https://docs.python.org/3/library/dataclasses.html" rel="noopener noreferrer"&gt;the corresponding documentation page,&lt;/a&gt; you'll discover a treasure. This package offers easy to use, but powerful, decorator and functions to manage a "data object" by defining a class.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A dataclass defines constructor, operator, and managed fields if they have got &lt;strong&gt;annotations&lt;/strong&gt;. Either the property is only for internal use. This helps a lot to manage what is part of the data, and what is not.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let me show you the very basic way to use it, using annotations instead of values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;

&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;example&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;foo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="c1"&gt;# This is not a field, no annotation
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Now, the class has got &lt;code&gt;__init__&lt;/code&gt; function to create the object with keywords argument, a &lt;code&gt;__repr__()&lt;/code&gt; method, and a &lt;code&gt;__eq__&lt;/code&gt; method that overrides the equal operator.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are more things to see later, but let's check the usage :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;user1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;John&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;me@test.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;foobar&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;user2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;John&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;me2@test.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;foobar&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# show a nice object representation
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# try comparison
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user1&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;user2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# False, email differs
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OK, that's nice, but we can do more… a lot more!&lt;/p&gt;

&lt;h2&gt;
  
  
  Dataclasses fields
&lt;/h2&gt;

&lt;p&gt;Let's imagine we want to create a user without setting its level, because we define that the level should be "0" by default. &lt;/p&gt;

&lt;p&gt;The issue is that the &lt;code&gt;__init__&lt;/code&gt; constructor defines it. So at this time we must provide a value when we build the object.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;dataclasses&lt;/code&gt; packages provides a function named &lt;code&gt;field&lt;/code&gt; that will help a lot to ease the development.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;

&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# set field as optional
&lt;/span&gt;
&lt;span class="c1"&gt;# test
&lt;/span&gt;&lt;span class="n"&gt;user1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;joe&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;me@test.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;123456&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# no level provided
&lt;/span&gt;
&lt;span class="c1"&gt;# but the level is set to 0
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# &amp;gt;&amp;gt; 0
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's not the end. We can do a lot of things.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not always need for constructor, use "post init"
&lt;/h2&gt;

&lt;p&gt;Sometimes you want to make something when an object is instantiated. So, the first reaction is to create a constructor. But, of course, here, the &lt;code&gt;dataclass&lt;/code&gt; decorator provides one and it's well made to manage default values.&lt;/p&gt;

&lt;p&gt;That's why you can create a &lt;code&gt;__post_init__&lt;/code&gt; method. It is called right after the constructor.&lt;/p&gt;

&lt;p&gt;For example, let's make a check on the password length.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt; User management &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;


&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;A user object&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# set field as optional
&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__post_init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Password must be at least 8 characters long&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's enough to make some validation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Of course, you can manage this with setter or getter, but I only show you an example.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  When dataclass becomes central
&lt;/h2&gt;

&lt;p&gt;You may think that it is only a gadget, a "too simple management" of class that represents data.&lt;/p&gt;

&lt;p&gt;Now, it's time to see when simplicity provides controls.&lt;/p&gt;

&lt;p&gt;I will present how this may help you to create an API with &lt;a href="https://pgjones.gitlab.io/quart/index.html" rel="noopener noreferrer"&gt;Quart&lt;/a&gt; and a bit of &lt;a href="https://pgjones.gitlab.io/quart-schema/" rel="noopener noreferrer"&gt;Quart Schema&lt;/a&gt;. If you already use Flask, that will not be a problem as Quart is a "fork" which &lt;em&gt;only&lt;/em&gt; make it asynchronous.&lt;/p&gt;

&lt;p&gt;Before the use of this, you probably do something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;
&lt;span class="nd"&gt;@api.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;John&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;me@test.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;foo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;username&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;level&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, you probably created methods to transform the data to JSON, or to &lt;code&gt;dict&lt;/code&gt;. But now, with dataclass + quart-schema, it's way more explicit.&lt;/p&gt;

&lt;p&gt;First, you must declare that the application is encapsulated to a Schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;quart&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Quart&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;quart_schema&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;QuartSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;validate_response&lt;/span&gt;

&lt;span class="n"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Quart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nc"&gt;QuartSchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, you are able to return an object, no need to &lt;code&gt;jsonify&lt;/code&gt; or to manage transformation!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@api.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;John&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;me@test.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;foo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;http &lt;span class="nt"&gt;-b&lt;/span&gt; :5000/user
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"email"&lt;/span&gt;: &lt;span class="s2"&gt;"me@test.com"&lt;/span&gt;,
    &lt;span class="s2"&gt;"level"&lt;/span&gt;: 0,
    &lt;span class="s2"&gt;"password"&lt;/span&gt;: &lt;span class="s2"&gt;"12345678"&lt;/span&gt;,
    &lt;span class="s2"&gt;"username"&lt;/span&gt;: &lt;span class="s2"&gt;"John"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OK, but… The password…&lt;/p&gt;

&lt;h2&gt;
  
  
  Hide fields
&lt;/h2&gt;

&lt;p&gt;The problem here is of course that the password is sent to the response.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Of course, you will hash the password in database and you must &lt;strong&gt;never send back the password&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are plenty of possibilities, but I will propose you one that I prefer.&lt;/p&gt;

&lt;p&gt;In "my" design view, there are several kind of data to manage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a user, that is the representation of what I can show to everybody&lt;/li&gt;
&lt;li&gt;a data user, that is the representation of what I manage in database&lt;/li&gt;
&lt;li&gt;some specific user representation for "login" or "registration" process&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, here is my example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;User data&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kw_only&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DataUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Data class for User in database&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kw_only&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hash_password&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Hash password with SHA1&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sha1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;DataUser&lt;/code&gt; class inherits fields from &lt;code&gt;User&lt;/code&gt;. We must force &lt;code&gt;kw_only&lt;/code&gt; to ensure that field with default values doesn't interfere with derived class fields.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/user/&amp;lt;email&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Get a user&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;users&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;users&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;I want to insist: &lt;strong&gt;I'm SURE that the password will never be sent back to the response, because the &lt;code&gt;User()&lt;/code&gt; construction will raise an exception if I provide the password in argument.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And to save a user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_user&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Create a new user&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;sent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DataUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;sent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hash_password&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;app&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;users&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;asdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c1"&gt;# check errors... then
&lt;/span&gt;
    &lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;asdict()&lt;/code&gt; is taken from the &lt;code&gt;dataclasses&lt;/code&gt; package, it builds a complete dictionary from your dataclass. So it's easy to use with a document database like Mongo or RethinkDB.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that using &lt;code&gt;validate_request&lt;/code&gt; and &lt;code&gt;validate_response&lt;/code&gt; from Quart Schema simplies a lot the method. For example:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nd"&gt;@validate_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DataUser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DataUser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Create a new user&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hash_password&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;app&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;users&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;asdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c1"&gt;# check errors... then
&lt;/span&gt;    &lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Last words
&lt;/h2&gt;

&lt;p&gt;Anyway, what I hope you to understand is the interest of using &lt;code&gt;dataclass&lt;/code&gt; and the &lt;code&gt;fields, field, asdict&lt;/code&gt; and other functions to make your data structures easy to use and to manage.&lt;/p&gt;

</description>
      <category>python</category>
      <category>quart</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
