<?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>DEV Community: Sandor | tutorialhell.dev</title>
    <description>The latest articles on DEV Community by Sandor | tutorialhell.dev (@sandorturanszky).</description>
    <link>https://dev.to/sandorturanszky</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%2F94890%2F249148c3-2788-4626-b935-f6f46bdb0405.jpeg</url>
      <title>DEV Community: Sandor | tutorialhell.dev</title>
      <link>https://dev.to/sandorturanszky</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sandorturanszky"/>
    <language>en</language>
    <item>
      <title>Has anyone else noticed an increase in New, Empty Profile Followers recently?</title>
      <dc:creator>Sandor | tutorialhell.dev</dc:creator>
      <pubDate>Thu, 22 Feb 2024 14:05:41 +0000</pubDate>
      <link>https://dev.to/sandorturanszky/has-anyone-else-noticed-an-increase-in-new-empty-profile-followers-recently-4ij3</link>
      <guid>https://dev.to/sandorturanszky/has-anyone-else-noticed-an-increase-in-new-empty-profile-followers-recently-4ij3</guid>
      <description>&lt;p&gt;I've been noticing something a bit unusual over the past few days and I'm curious if anyone else has experienced the same. A significant number of new followers have been added to my profile, however, upon closer inspection, I realized that many of these accounts are newly created with no information at all - no posts, comments, or any form of engagement.&lt;/p&gt;

&lt;p&gt;This trend has left me feeling a bit puzzled and, honestly, a bit discouraged. It's made me question the authenticity of the engagement on this platform. Are these real people interested in my content, or is there something else at play here?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>community</category>
    </item>
    <item>
      <title>Must-Have Practical Tutorial on Node.js Modules for Badass Beginners</title>
      <dc:creator>Sandor | tutorialhell.dev</dc:creator>
      <pubDate>Tue, 20 Feb 2024 13:00:00 +0000</pubDate>
      <link>https://dev.to/sandorturanszky/must-have-practical-tutorial-on-nodejs-modules-for-badass-beginners-npk</link>
      <guid>https://dev.to/sandorturanszky/must-have-practical-tutorial-on-nodejs-modules-for-badass-beginners-npk</guid>
      <description>&lt;p&gt;It's a practical tutorial that will provide you with everything you need to understand how &lt;code&gt;require()&lt;/code&gt; works and how to use it efficiently. It includes detailed instructions and code examples that you can run either locally on your computer or in a prepared online Node environment to quickly get the hang of it. And there will be practical exercises waiting for you at the end of this tutorial.&lt;/p&gt;

&lt;p&gt;It's part of my &lt;a href="https://www.tutorialhell.dev/"&gt;tutorialhell.dev&lt;/a&gt; project, aimed at helping beginners learn easily without getting stuck.&lt;/p&gt;

&lt;p&gt;In Node.js, a module is a self-contained block of code that doesn't affect other code. JavaScript files are treated as modules in Node.js, where you can export functions, objects, or values from one file and use them in another.&lt;/p&gt;

&lt;p&gt;Node.js supports two module systems: CommonJS modules and ECMAScript modules. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;CommonJS modules&lt;/strong&gt; were the original method for packaging JavaScript code in Node.js. &lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;ECMAScript modules&lt;/strong&gt; standard is utilized by browsers and other JavaScript runtimes and is generally viewed as the preferred approach for being future-proof. However, to get started, we will focus on CommonJS modules since they are still widely used. As we progress, I will introduce you to ECMAScript modules in the upcoming tutorials.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's break down the basics, starting with how to create a module in Node.js, how to export from it, and how to use the exported entities in other files.&lt;/p&gt;

&lt;p&gt;If you don't have Node.js set up, you can get started using a ready-made Node.js environment in this &lt;a href="https://stackblitz.com/fork/github/stackblitz/starters/tree/main/nodemon?title=Nodemon%20Starter&amp;amp;description=A%20simple%20starter%20for%20using%20nodemon%20printing%20no%20messages%20in%20a%20terminal"&gt;sandbox&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a Simple Module
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Create a File&lt;/strong&gt;: Every module is a JavaScript file. Create a file named 'myModule.js'.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Write Some Code&lt;/strong&gt;: Inside 'myModule.js', define a function that you want to make available outside this file.&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;sayHello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`Hello, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Exporting from a Module
&lt;/h2&gt;

&lt;p&gt;To make functions, objects, or values available outside the module where they are defined, you need to export them using &lt;code&gt;module.exports&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;module.exports&lt;/code&gt; is an object included in every JavaScript file in Node.js by default. It is used to export functions, objects, or values from a module. Here is the full code:&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;sayHello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`Hello, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&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="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sayHello&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Importing a Module
&lt;/h2&gt;

&lt;p&gt;To use the exported entities from a module in another file, you need to require that module.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create Another File&lt;/strong&gt;: 'index.js'. (It's already there if you are using the sandbox. Simply replace the contents with the code provided below.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Require the Module&lt;/strong&gt;: Use the &lt;code&gt;require()&lt;/code&gt; function to import the module, passing the path to the module file as an argument:&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;const&lt;/span&gt; &lt;span class="nx"&gt;sayHello&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./myModule&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&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="nf"&gt;sayHello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;World&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Outputs: Hello, World!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;require('./myModule')&lt;/code&gt;: This line imports the module myModule.js. The &lt;code&gt;./&lt;/code&gt; indicates that the file is located in the same directory as index.js. Node.js reads myModule.js, executes it, and then module.exports from myModule.js is returned by require() and assigned to the variable sayHello in index.js.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sayHello('World')&lt;/code&gt;: This line calls the imported function with the argument 'World', and the function returns the string 'Hello, World!'.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multiple exports
&lt;/h2&gt;

&lt;p&gt;To perform multiple exports from a Node.js module, you can use either the &lt;code&gt;module.exports&lt;/code&gt; object or the &lt;code&gt;exports&lt;/code&gt; shorthand. Each approach allows you to export multiple items, such as functions, objects, or values, from a single module. Here's how you can do it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Using &lt;code&gt;module.exports&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can directly assign an object to &lt;code&gt;module.exports&lt;/code&gt; containing all the items you want to export. This approach is useful when you want to structure your exports clearly in one place. Create a 'multipleExport1.js' and paste the following code there:&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;func1&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&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="s1"&gt;Function 1&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;func2&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&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="s1"&gt;Function 2&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;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;func1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;func2&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;ol&gt;
&lt;li&gt;Using &lt;code&gt;exports&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;code&gt;exports&lt;/code&gt; shorthand allows you to attach exports directly to the &lt;code&gt;exports&lt;/code&gt; object. This is syntactically more concise for exporting multiple items. Create a 'multipleExport2.js' and paste the following code there:&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;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;func3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&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="s1"&gt;Function 3&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;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;func4&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&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="s1"&gt;Function 4&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;h2&gt;
  
  
  Multiple imports
&lt;/h2&gt;

&lt;p&gt;Similarly, you would import these in another file like 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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myModule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./multipleExport1&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;myModule2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./multipleExport2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, using destructuring:&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;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;func1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;func2&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./multipleExport1&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;func3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;func4&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./multipleExport2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace the contents of the 'index.js' file with this code:&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;const&lt;/span&gt; &lt;span class="nx"&gt;sayHello&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./myModule&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&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="nf"&gt;sayHello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;World&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Outputs: Hello, World!&lt;/span&gt;

&lt;span class="c1"&gt;// Approach 1&lt;/span&gt;
&lt;span class="nx"&gt;console&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="s1"&gt;------- Approach 1 -------&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;myModule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./multipleExport1&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;myModule2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./multipleExport2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;myModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;func1&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Calls 'Function 1'&lt;/span&gt;
&lt;span class="nx"&gt;myModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;func2&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Calls 'Function 2'&lt;/span&gt;

&lt;span class="nx"&gt;myModule2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;func3&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Calls 'Function 3'&lt;/span&gt;
&lt;span class="nx"&gt;myModule2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;func4&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Calls 'Function 4'&lt;/span&gt;

&lt;span class="c1"&gt;// Approach 2&lt;/span&gt;
&lt;span class="nx"&gt;console&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="s1"&gt;------- Approach 2 -------&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;func1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;func2&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./multipleExport1&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;func3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;func4&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./multipleExport2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;func1&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Calls 'Function 1'&lt;/span&gt;
&lt;span class="nf"&gt;func2&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Calls 'Function 2'&lt;/span&gt;
&lt;span class="nf"&gt;func3&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Calls 'Function 3'&lt;/span&gt;
&lt;span class="nf"&gt;func4&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Calls 'Function 4'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you should see the following logged in your terminal:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F133asmi2d05jvszdk3jf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F133asmi2d05jvszdk3jf.png" alt="Node.js modules" width="800" height="337"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You might have noticed that when functions are imported through destructuring, they are assigned unique identifiers like &lt;code&gt;func1&lt;/code&gt;, &lt;code&gt;func2&lt;/code&gt;, etc. This uniqueness is necessary because two functions with the same name cannot coexist in the same file (module). It requires us to use unique names in different modules, which is not convenient.&lt;/p&gt;

&lt;p&gt;However, there is a technique to rename these functions while importing them. This allows functions to have identical names across multiple modules and be renamed when imported into the same file to avoid name collisions. Here is how you do it:&lt;/p&gt;

&lt;p&gt;Create a new file named 'multipleExport3.js' and paste the following code there:&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;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;func3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&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="s1"&gt;Function 3 with an identical name from a different module&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;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;func4&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&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="s1"&gt;Function 4 with an identical name from a different module&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;p&gt;Then add this code to the bottom of your 'index.js' file:&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;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;func3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;renamedFunc3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;func4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;renamedFunc4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./multipleExport3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&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="s1"&gt;------- Destructuring with Renaming -------&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;renamedFunc3&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nf"&gt;renamedFunc4&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And your updated terminal output will look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpub4pnetn8bicsgu7usc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpub4pnetn8bicsgu7usc.png" alt="Destructuring with Renaming" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👉 Working code you can find &lt;a href="https://stackblitz.com/edit/node-js-modules-www-tutorialhell-dev?file=index.js"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing Between &lt;code&gt;module.exports&lt;/code&gt; and &lt;code&gt;exports&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Use &lt;code&gt;module.exports&lt;/code&gt; when you want to export a single item, like a function or an object, or when you prefer to define all exports at once in an object.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;exports&lt;/code&gt; to attach multiple exports individually. This can be more straightforward when exporting several items but remember that you cannot assign directly to &lt;code&gt;exports&lt;/code&gt; as it will break the reference to &lt;code&gt;module.exports&lt;/code&gt;. For example, doing &lt;code&gt;exports = { func1, func2 }&lt;/code&gt; will not work as expected.&lt;/p&gt;

&lt;p&gt;Both methods are commonly used and choosing between them often comes down to preference and the specific needs of your module.&lt;/p&gt;

&lt;h2&gt;
  
  
  Here’s a breakdown of the key feature that every beginner should grasp:
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;require()&lt;/code&gt; is Synchronous and Caches Modules
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Synchronous Loading:&lt;/strong&gt; When you use &lt;code&gt;require()&lt;/code&gt; to import a module, Node.js reads and executes the entire module file synchronously. This means that Node.js will halt the execution of your code until the entire module is loaded. This behaviour is particularly important to understand because it affects how and where you use require() in your application. Here's a simple &lt;a href="https://stackblitz.com/edit/synchronous-require-demo-nodejs?file=index.js"&gt;code example&lt;/a&gt; to demonstrate that require() is synchronous in Node.js, meaning Node.js waits for the module to be fully loaded and executed before moving on to the next line of code.&lt;/p&gt;

&lt;p&gt;Or try it out yourself by pasting the following code in the &lt;a href="https://stackblitz.com/fork/github/stackblitz/starters/tree/main/nodemon?title=Nodemon%20Starter&amp;amp;description=A%20simple%20starter%20for%20using%20nodemon%20printing%20no%20messages%20in%20a%20terminal"&gt;sandbox&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Create a file named 'greet.js' and paste the following code there:&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;console&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="s1"&gt;Loading greet.js...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&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="s1"&gt;Hello and welcome to learning web development with www.tutorialhell.dev!&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;console&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="s1"&gt;greet.js loaded successfully.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create or update (it's already there in the sandbox) the 'index.js' file and paste the following code there:&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;console&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="s1"&gt;Starting index.js...&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;greet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./greet&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Synchronously loads and executes "greet.js"&lt;/span&gt;

&lt;span class="nx"&gt;console&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="s1"&gt;greet.js has been required.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;console&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="s1"&gt;Ending index.js...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;br&gt;
When you run 'index.js' using the command &lt;code&gt;node index.js&lt;/code&gt;, you will see the logs in the following order, demonstrating the synchronous nature of &lt;code&gt;require()&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F06c8nw9uvqkpbhvsnhom.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F06c8nw9uvqkpbhvsnhom.png" alt="Demonstrating the synchronous nature of require()" width="800" height="212"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice how the execution of 'index.js' pauses until 'greet.js' is fully loaded and executed (evidenced by the logs from 'greet.js' appearing before the continuation of 'index.js').&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In simple terms, synchronous means doing things one by one in order, where you have to finish one thing before you can start the next thing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Caching Mechanism&lt;/strong&gt;: Node.js caches the first instance of each module you require. If you &lt;code&gt;require()&lt;/code&gt; the same module multiple times in different parts of your application, &lt;strong&gt;Node.js will not reload or re-execute the module file&lt;/strong&gt; after the first time it's loaded. Instead, &lt;strong&gt;it reuses the cached version&lt;/strong&gt;. This caching mechanism improves performance by avoiding the overhead of loading and compiling the module's code multiple times. However, it also means that any state maintained in the module is shared across all parts of your application that import it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In simple terms, caching means storing something in a place where it can be quickly accessed later. It's like saving a shortcut to find information faster without having to go through the whole search process again.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Why It Matters
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Performance Implications&lt;/strong&gt;: Knowing that &lt;code&gt;require()&lt;/code&gt; is synchronous helps in structuring your code for optimal performance, especially during the startup phase of an application. It encourages the practice of requiring modules at the beginning of files or in sections of the code where blocking the event loop has minimal impact on the application's responsiveness.&lt;/p&gt;

&lt;p&gt;Understanding JavaScript's synchronous, single-threaded nature is key in Node.js to avoid blocking code. Node.js uses techniques like &lt;strong&gt;callbacks&lt;/strong&gt;, &lt;strong&gt;promises&lt;/strong&gt;, and &lt;strong&gt;async/await&lt;/strong&gt; to enable asynchronous operations, preventing the event loop from being blocked. These methods allow Node.js applications to remain efficient and responsive, handling tasks in the background without slowing down the main execution flow.&lt;/p&gt;

&lt;p&gt;Single-threaded means that a program or environment, like JavaScript in Node.js, can only execute one operation at a time on a single path of execution. Imagine it as a single-lane road where cars (operations) must go one after another, not side by side. This setup ensures that operations are carried out in sequence, but it also means that one operation must complete before the next one starts, preventing simultaneous execution of multiple operations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Module State Sharing&lt;/strong&gt;: Awareness of the caching mechanism is important when designing modules. Since the same instance of a module is shared wherever it's required, any state stored in the module is also shared. This can be leveraged to maintain application-wide state but requires careful management to avoid unintended side effects.&lt;/p&gt;

&lt;p&gt;In programming, "state" refers to the current condition or data held by an application or a module at any given time. It's like remembering the score while playing a game; just as the score tells you how well you're doing at that moment, the state in programming keeps track of information like what's been clicked, entered into forms, or saved temporarily as the program runs. This state can change based on what actions are taken within the application, just like the game score changes when you score points or lose points.&lt;/p&gt;

&lt;p&gt;It's important to distinguish this concept of "state" from a database (DB). While a database can store data that might represent part of an application's state, such as user profiles or application settings, the "state" in the context of programming generally refers to the in-memory condition of an application at a specific moment. This includes data that the application is currently working with but hasn't necessarily saved to a database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Debugging&lt;/strong&gt;: Understanding how &lt;code&gt;require()&lt;/code&gt; works can aid in debugging issues related to module loading, such as why changes to a module don't seem to take effect (due to caching) or why an application may be blocking at startup (due to synchronous loading of modules).&lt;/p&gt;
&lt;h2&gt;
  
  
  Practical Exercises
&lt;/h2&gt;

&lt;p&gt;To reinforce your understanding of the &lt;code&gt;require()&lt;/code&gt; functionality and the caching mechanism, I recommend practising. Here is the Node.js &lt;a href="https://stackblitz.com/fork/github/stackblitz/starters/tree/main/nodemon?title=Nodemon%20Starter&amp;amp;description=A%20simple%20starter%20for%20using%20nodemon%20printing%20no%20messages%20in%20a%20terminal"&gt;sandbox&lt;/a&gt;, in case you need it..&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Objective:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Your task is to create a simple Node.js application that demonstrates how the state is shared and managed across modules due to Node.js's caching mechanism. You will create a shared state module and two additional modules that modify and display the shared state, respectively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Instructions:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Create the Shared State Module:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;File Name: 'counterState.js'&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Task: This module will maintain a counter. Write two functions within this module: one named &lt;code&gt;incrementCounter&lt;/code&gt; to increment the counter and another named &lt;code&gt;getCounter&lt;/code&gt; to retrieve the counter value. Export both functions.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The counter code will look something like 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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;count&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;incrementCounter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getCounter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;incrementCounter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getCounter&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Create the First Additional Module:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;File Name: 'incrementModule.js'&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Task: This module will require 'counterState.js' and use its &lt;code&gt;incrementCounter&lt;/code&gt; function to increment the counter, and the &lt;code&gt;getCounter&lt;/code&gt; function to display the counter's current value. It should then log a message showing the new counter value. Export a function named &lt;code&gt;increment&lt;/code&gt; that performs these tasks. You need to write this code yourself.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Create the Second Additional Module:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;File Name: 'displayModule.js'&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Task: This module will also require 'counterState.js' but will only use the &lt;code&gt;getCounter&lt;/code&gt; function to display the current value of the counter. Export a function named &lt;code&gt;display&lt;/code&gt; that logs the counter's current value. You need to write this code yourself.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Create the Entry Point File:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;File Name: 'index.js'&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Task: In this file, you will require both 'incrementModule.js' and 'displayModule.js'. Use their exported functions to increment the counter and display its value, demonstrating the shared state across modules. You need to write this code yourself.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Instructions: Increment the counter, display its value, and then increment it again to show the shared and persistent state. Then display the value again to see how it updates in the other file (module) that requires the 'counterState.js' module.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The working code should log the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxpb7hvr43pe6x7j6zua9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxpb7hvr43pe6x7j6zua9.png" alt="State Management Demo" width="800" height="307"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By completing this homework, you will understand how Node.js caches imported modules, allowing shared state between different parts of your application. This knowledge is fundamental for efficient Node.js development, especially for applications relying on shared data across modules.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stackblitz.com/edit/node-js-commonjs-modules-homework-solution?file=index.js"&gt;Here&lt;/a&gt; are the solutions to the above practical exercises.&lt;/p&gt;

&lt;h2&gt;
  
  
  Transferable Knowledge and Patterns
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Understanding of Synchronous Operations&lt;/strong&gt;: Recognising that &lt;code&gt;require()&lt;/code&gt; operates synchronously, halting execution until a module is fully loaded, &lt;strong&gt;teaches the general principle of synchronous execution in programming&lt;/strong&gt;. This concept is critical for understanding how different parts of a program can affect execution flow and performance, applicable in various programming languages and environments.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Importance of Caching for Performance&lt;/strong&gt;: &lt;strong&gt;The caching mechanism of &lt;code&gt;require()&lt;/code&gt; illustrates a fundamental performance optimisation technique used widely in computing.&lt;/strong&gt; Caching, or temporarily storing data for quick access upon repeated requests, is a concept that can be applied in numerous scenarios beyond module loading, such as web content delivery, database queries, and more.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Module State Sharing and Global State Management&lt;/strong&gt;: The idea that a module's state is shared across all parts of an application that import it underscores the broader concept of global state management. This is a key consideration in software design, relevant for &lt;strong&gt;understanding how state is managed across an application&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Performance Optimisation Practices&lt;/strong&gt;: The advice to require modules at the beginning of files or in strategic code sections to minimise performance impact &lt;strong&gt;teaches a broader lesson on resource management and optimisation in software development&lt;/strong&gt;. Prioritising resource-intensive operations and understanding their impact on the application's responsiveness and efficiency are valuable skills.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Debugging and Troubleshooting&lt;/strong&gt;: The emphasis on how understanding require()'s behaviour aids in debugging extends to a general principle in programming: &lt;strong&gt;knowing how your tools and frameworks work under the hood can significantly improve your ability to troubleshoot and solve problems&lt;/strong&gt;. This is a critical skill for any developer, as it applies to debugging in all programming languages and environments.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That said, understanding how to import modules in Node.js opens up a wealth of critical knowledge that you will learn once and use everywhere. It shows you how to avoid getting stuck. &lt;/p&gt;

&lt;p&gt;You are more than welcome to explore these topics independently because it’s the best way to develop problem-solving skills. But keep in mind that we will gradually cover all these topics through my tutorials because that’s why &lt;a href="https://www.tutorialhell.dev/"&gt;tutorialhell.dev&lt;/a&gt; was created.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learning path from here:
&lt;/h2&gt;

&lt;p&gt;The concepts mentioned in the examples, such as function definitions, template literals, and destructuring, are frequently used, and thus I strongly encourage you to engage in some self-learning as well. Yes, to develop problem-solving skills. I cannot emphasize this enough.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Introduction to Objects&lt;/strong&gt;: Learn about JavaScript objects, including creation, access, methods, property shorthand and computed properties.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Function Definitions&lt;/strong&gt;: Explore different ways to define functions, including function declarations, function expressions, and arrow functions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Template Literals&lt;/strong&gt;: Learn about string interpolation and multi-line strings in ES6, using backticks to define template literals and &lt;code&gt;${}&lt;/code&gt; for embedding expressions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Destructuring&lt;/strong&gt;: Understand the destructuring assignment syntax for arrays and objects.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;File Paths&lt;/strong&gt;: Learn about relative and absolute file paths to grasp how to navigate and reference files within your projects effectively.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;If you like this content, consider following me and subscribing to receive these cool tutorials as soon as they are ready.&lt;/p&gt;

&lt;p&gt;The sunfish on the post cover was sourced from the O'Rly Generator.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>node</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Must-Have Practical Tutorial on Node.js Modules for Badass Beginners</title>
      <dc:creator>Sandor | tutorialhell.dev</dc:creator>
      <pubDate>Mon, 19 Feb 2024 12:04:30 +0000</pubDate>
      <link>https://dev.to/sandorturanszky/must-have-practical-tutorial-on-nodejs-modules-for-badass-beginners-1njd</link>
      <guid>https://dev.to/sandorturanszky/must-have-practical-tutorial-on-nodejs-modules-for-badass-beginners-1njd</guid>
      <description>&lt;p&gt;It's a practical tutorial that will provide you with everything you need to understand how &lt;code&gt;require()&lt;/code&gt; works and how to use it efficiently. It includes detailed instructions and code examples that you can run either locally on your computer or in a prepared online Node environment to quickly get the hang of it. And there will be practical exercises waiting for you at the end of this tutorial.&lt;/p&gt;

&lt;p&gt;It's part of my &lt;a href="https://www.tutorialhell.dev/"&gt;tutorialhell.dev&lt;/a&gt; project, aimed at helping beginners learn easily without getting stuck.&lt;/p&gt;

&lt;p&gt;In Node.js, a module is a self-contained block of code that doesn't affect other code. JavaScript files are treated as modules in Node.js, where you can export functions, objects, or values from one file and use them in another.&lt;/p&gt;

&lt;p&gt;Node.js supports two module systems: CommonJS modules and ECMAScript modules. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;CommonJS modules&lt;/strong&gt; were the original method for packaging JavaScript code in Node.js. &lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;ECMAScript modules&lt;/strong&gt; standard is utilized by browsers and other JavaScript runtimes and is generally viewed as the preferred approach for being future-proof. However, to get started, we will focus on CommonJS modules since they are still widely used. As we progress, I will introduce you to ECMAScript modules in the upcoming tutorials.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's break down the basics, starting with how to create a module in Node.js, how to export from it, and how to use the exported entities in other files.&lt;/p&gt;

&lt;p&gt;If you don't have Node.js set up, you can get started using a ready-made Node.js environment in this &lt;a href="https://stackblitz.com/fork/github/stackblitz/starters/tree/main/nodemon?title=Nodemon%20Starter&amp;amp;description=A%20simple%20starter%20for%20using%20nodemon%20printing%20no%20messages%20in%20a%20terminal"&gt;sandbox&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a Simple Module
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Create a File&lt;/strong&gt;: Every module is a JavaScript file. Create a file named 'myModule.js'.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Write Some Code&lt;/strong&gt;: Inside 'myModule.js', define a function that you want to make available outside this file.&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;sayHello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`Hello, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Exporting from a Module
&lt;/h2&gt;

&lt;p&gt;To make functions, objects, or values available outside the module where they are defined, you need to export them using &lt;code&gt;module.exports&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;module.exports&lt;/code&gt; is an object included in every JavaScript file in Node.js by default. It is used to export functions, objects, or values from a module. Here is the full code:&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;sayHello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`Hello, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&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="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sayHello&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Importing a Module
&lt;/h2&gt;

&lt;p&gt;To use the exported entities from a module in another file, you need to require that module.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create Another File&lt;/strong&gt;: 'index.js'. (It's already there if you are using the sandbox. Simply replace the contents with the code provided below.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Require the Module&lt;/strong&gt;: Use the &lt;code&gt;require()&lt;/code&gt; function to import the module, passing the path to the module file as an argument:&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;const&lt;/span&gt; &lt;span class="nx"&gt;sayHello&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./myModule&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&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="nf"&gt;sayHello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;World&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Outputs: Hello, World!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;require('./myModule')&lt;/code&gt;: This line imports the module myModule.js. The &lt;code&gt;./&lt;/code&gt; indicates that the file is located in the same directory as index.js. Node.js reads myModule.js, executes it, and then module.exports from myModule.js is returned by require() and assigned to the variable sayHello in index.js.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sayHello('World')&lt;/code&gt;: This line calls the imported function with the argument 'World', and the function returns the string 'Hello, World!'.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multiple exports
&lt;/h2&gt;

&lt;p&gt;To perform multiple exports from a Node.js module, you can use either the &lt;code&gt;module.exports&lt;/code&gt; object or the &lt;code&gt;exports&lt;/code&gt; shorthand. Each approach allows you to export multiple items, such as functions, objects, or values, from a single module. Here's how you can do it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Using &lt;code&gt;module.exports&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can directly assign an object to &lt;code&gt;module.exports&lt;/code&gt; containing all the items you want to export. This approach is useful when you want to structure your exports clearly in one place. Create a 'multipleExport1.js' and paste the following code there:&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;func1&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&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="s1"&gt;Function 1&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;func2&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&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="s1"&gt;Function 2&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;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;func1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;func2&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;ol&gt;
&lt;li&gt;Using &lt;code&gt;exports&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;code&gt;exports&lt;/code&gt; shorthand allows you to attach exports directly to the &lt;code&gt;exports&lt;/code&gt; object. This is syntactically more concise for exporting multiple items. Create a 'multipleExport2.js' and paste the following code there:&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;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;func3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&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="s1"&gt;Function 3&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;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;func4&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&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="s1"&gt;Function 4&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;h2&gt;
  
  
  Multiple imports
&lt;/h2&gt;

&lt;p&gt;Similarly, you would import these in another file like 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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myModule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./multipleExport1&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;myModule2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./multipleExport2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, using destructuring:&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;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;func1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;func2&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./multipleExport1&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;func3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;func4&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./multipleExport2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace the contents of the 'index.js' file with this code:&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;const&lt;/span&gt; &lt;span class="nx"&gt;sayHello&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./myModule&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&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="nf"&gt;sayHello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;World&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Outputs: Hello, World!&lt;/span&gt;

&lt;span class="c1"&gt;// Approach 1&lt;/span&gt;
&lt;span class="nx"&gt;console&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="s1"&gt;------- Approach 1 -------&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;myModule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./multipleExport1&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;myModule2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./multipleExport2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;myModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;func1&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Calls 'Function 1'&lt;/span&gt;
&lt;span class="nx"&gt;myModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;func2&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Calls 'Function 2'&lt;/span&gt;

&lt;span class="nx"&gt;myModule2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;func3&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Calls 'Function 3'&lt;/span&gt;
&lt;span class="nx"&gt;myModule2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;func4&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Calls 'Function 4'&lt;/span&gt;

&lt;span class="c1"&gt;// Approach 2&lt;/span&gt;
&lt;span class="nx"&gt;console&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="s1"&gt;------- Approach 2 -------&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;func1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;func2&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./multipleExport1&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;func3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;func4&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./multipleExport2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;func1&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Calls 'Function 1'&lt;/span&gt;
&lt;span class="nf"&gt;func2&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Calls 'Function 2'&lt;/span&gt;
&lt;span class="nf"&gt;func3&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Calls 'Function 3'&lt;/span&gt;
&lt;span class="nf"&gt;func4&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Calls 'Function 4'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you should see the following logged in your terminal:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F133asmi2d05jvszdk3jf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F133asmi2d05jvszdk3jf.png" alt="Node.js modules" width="800" height="337"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You might have noticed that when functions are imported through destructuring, they are assigned unique identifiers like &lt;code&gt;func1&lt;/code&gt;, &lt;code&gt;func2&lt;/code&gt;, etc. This uniqueness is necessary because two functions with the same name cannot coexist in the same file (module). It requires us to use unique names in different modules, which is not convenient.&lt;/p&gt;

&lt;p&gt;However, there is a technique to rename these functions while importing them. This allows functions to have identical names across multiple modules and be renamed when imported into the same file to avoid name collisions. Here is how you do it:&lt;/p&gt;

&lt;p&gt;Create a new file named 'multipleExport3.js' and paste the following code there:&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;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;func3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&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="s1"&gt;Function 3 with an identical name from a different module&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;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;func4&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&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="s1"&gt;Function 4 with an identical name from a different module&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;p&gt;Then add this code to the bottom of your 'index.js' file:&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;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;func3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;renamedFunc3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;func4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;renamedFunc4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./multipleExport3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&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="s1"&gt;------- Destructuring with Renaming -------&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;renamedFunc3&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nf"&gt;renamedFunc4&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And your updated terminal output will look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpub4pnetn8bicsgu7usc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpub4pnetn8bicsgu7usc.png" alt="Destructuring with Renaming" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👉 Working code you can find &lt;a href="https://stackblitz.com/edit/node-js-modules-www-tutorialhell-dev?file=index.js"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing Between &lt;code&gt;module.exports&lt;/code&gt; and &lt;code&gt;exports&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Use &lt;code&gt;module.exports&lt;/code&gt; when you want to export a single item, like a function or an object, or when you prefer to define all exports at once in an object.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;exports&lt;/code&gt; to attach multiple exports individually. This can be more straightforward when exporting several items but remember that you cannot assign directly to &lt;code&gt;exports&lt;/code&gt; as it will break the reference to &lt;code&gt;module.exports&lt;/code&gt;. For example, doing &lt;code&gt;exports = { func1, func2 }&lt;/code&gt; will not work as expected.&lt;/p&gt;

&lt;p&gt;Both methods are commonly used and choosing between them often comes down to preference and the specific needs of your module.&lt;/p&gt;

&lt;h2&gt;
  
  
  Here’s a breakdown of the key feature that every beginner should grasp:
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;require()&lt;/code&gt; is Synchronous and Caches Modules
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Synchronous Loading:&lt;/strong&gt; When you use &lt;code&gt;require()&lt;/code&gt; to import a module, Node.js reads and executes the entire module file synchronously. This means that Node.js will halt the execution of your code until the entire module is loaded. This behaviour is particularly important to understand because it affects how and where you use require() in your application. Here's a simple &lt;a href="https://stackblitz.com/edit/synchronous-require-demo-nodejs?file=index.js"&gt;code example&lt;/a&gt; to demonstrate that require() is synchronous in Node.js, meaning Node.js waits for the module to be fully loaded and executed before moving on to the next line of code.&lt;/p&gt;

&lt;p&gt;Or try it out yourself by pasting the following code in the &lt;a href="https://stackblitz.com/fork/github/stackblitz/starters/tree/main/nodemon?title=Nodemon%20Starter&amp;amp;description=A%20simple%20starter%20for%20using%20nodemon%20printing%20no%20messages%20in%20a%20terminal"&gt;sandbox&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Create a file named 'greet.js' and paste the following code there:&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;console&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="s1"&gt;Loading greet.js...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&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="s1"&gt;Hello and welcome to learning web development with www.tutorialhell.dev!&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;console&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="s1"&gt;greet.js loaded successfully.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create or update (it's already there in the sandbox) the 'index.js' file and paste the following code there:&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;console&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="s1"&gt;Starting index.js...&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;greet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./greet&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Synchronously loads and executes "greet.js"&lt;/span&gt;

&lt;span class="nx"&gt;console&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="s1"&gt;greet.js has been required.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;console&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="s1"&gt;Ending index.js...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;br&gt;
When you run 'index.js' using the command &lt;code&gt;node index.js&lt;/code&gt;, you will see the logs in the following order, demonstrating the synchronous nature of &lt;code&gt;require()&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F06c8nw9uvqkpbhvsnhom.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F06c8nw9uvqkpbhvsnhom.png" alt="Demonstrating the synchronous nature of require()" width="800" height="212"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice how the execution of 'index.js' pauses until 'greet.js' is fully loaded and executed (evidenced by the logs from 'greet.js' appearing before the continuation of 'index.js').&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In simple terms, synchronous means doing things one by one in order, where you have to finish one thing before you can start the next thing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Caching Mechanism&lt;/strong&gt;: Node.js caches the first instance of each module you require. If you &lt;code&gt;require()&lt;/code&gt; the same module multiple times in different parts of your application, &lt;strong&gt;Node.js will not reload or re-execute the module file&lt;/strong&gt; after the first time it's loaded. Instead, &lt;strong&gt;it reuses the cached version&lt;/strong&gt;. This caching mechanism improves performance by avoiding the overhead of loading and compiling the module's code multiple times. However, it also means that any state maintained in the module is shared across all parts of your application that import it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In simple terms, caching means storing something in a place where it can be quickly accessed later. It's like saving a shortcut to find information faster without having to go through the whole search process again.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Why It Matters
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Performance Implications&lt;/strong&gt;: Knowing that &lt;code&gt;require()&lt;/code&gt; is synchronous helps in structuring your code for optimal performance, especially during the startup phase of an application. It encourages the practice of requiring modules at the beginning of files or in sections of the code where blocking the event loop has minimal impact on the application's responsiveness.&lt;/p&gt;

&lt;p&gt;Understanding JavaScript's synchronous, single-threaded nature is key in Node.js to avoid blocking code. Node.js uses techniques like &lt;strong&gt;callbacks&lt;/strong&gt;, &lt;strong&gt;promises&lt;/strong&gt;, and &lt;strong&gt;async/await&lt;/strong&gt; to enable asynchronous operations, preventing the event loop from being blocked. These methods allow Node.js applications to remain efficient and responsive, handling tasks in the background without slowing down the main execution flow.&lt;/p&gt;

&lt;p&gt;Single-threaded means that a program or environment, like JavaScript in Node.js, can only execute one operation at a time on a single path of execution. Imagine it as a single-lane road where cars (operations) must go one after another, not side by side. This setup ensures that operations are carried out in sequence, but it also means that one operation must complete before the next one starts, preventing simultaneous execution of multiple operations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Module State Sharing&lt;/strong&gt;: Awareness of the caching mechanism is important when designing modules. Since the same instance of a module is shared wherever it's required, any state stored in the module is also shared. This can be leveraged to maintain application-wide state but requires careful management to avoid unintended side effects.&lt;/p&gt;

&lt;p&gt;In programming, "state" refers to the current condition or data held by an application or a module at any given time. It's like remembering the score while playing a game; just as the score tells you how well you're doing at that moment, the state in programming keeps track of information like what's been clicked, entered into forms, or saved temporarily as the program runs. This state can change based on what actions are taken within the application, just like the game score changes when you score points or lose points.&lt;/p&gt;

&lt;p&gt;It's important to distinguish this concept of "state" from a database (DB). While a database can store data that might represent part of an application's state, such as user profiles or application settings, the "state" in the context of programming generally refers to the in-memory condition of an application at a specific moment. This includes data that the application is currently working with but hasn't necessarily saved to a database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Debugging&lt;/strong&gt;: Understanding how &lt;code&gt;require()&lt;/code&gt; works can aid in debugging issues related to module loading, such as why changes to a module don't seem to take effect (due to caching) or why an application may be blocking at startup (due to synchronous loading of modules).&lt;/p&gt;
&lt;h2&gt;
  
  
  Practical Exercises
&lt;/h2&gt;

&lt;p&gt;To reinforce your understanding of the &lt;code&gt;require()&lt;/code&gt; functionality and the caching mechanism, I recommend practising. Here is the Node.js &lt;a href="https://stackblitz.com/fork/github/stackblitz/starters/tree/main/nodemon?title=Nodemon%20Starter&amp;amp;description=A%20simple%20starter%20for%20using%20nodemon%20printing%20no%20messages%20in%20a%20terminal"&gt;sandbox&lt;/a&gt;, in case you need it..&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Objective:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Your task is to create a simple Node.js application that demonstrates how the state is shared and managed across modules due to Node.js's caching mechanism. You will create a shared state module and two additional modules that modify and display the shared state, respectively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Instructions:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Create the Shared State Module:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;File Name: 'counterState.js'&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Task: This module will maintain a counter. Write two functions within this module: one named &lt;code&gt;incrementCounter&lt;/code&gt; to increment the counter and another named &lt;code&gt;getCounter&lt;/code&gt; to retrieve the counter value. Export both functions.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The counter code will look something like 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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;count&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;incrementCounter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getCounter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;incrementCounter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getCounter&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Create the First Additional Module:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;File Name: 'incrementModule.js'&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Task: This module will require 'counterState.js' and use its &lt;code&gt;incrementCounter&lt;/code&gt; function to increment the counter, and the &lt;code&gt;getCounter&lt;/code&gt; function to display the counter's current value. It should then log a message showing the new counter value. Export a function named &lt;code&gt;increment&lt;/code&gt; that performs these tasks. You need to write this code yourself.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Create the Second Additional Module:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;File Name: 'displayModule.js'&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Task: This module will also require 'counterState.js' but will only use the &lt;code&gt;getCounter&lt;/code&gt; function to display the current value of the counter. Export a function named &lt;code&gt;display&lt;/code&gt; that logs the counter's current value. You need to write this code yourself.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Create the Entry Point File:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;File Name: 'index.js'&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Task: In this file, you will require both 'incrementModule.js' and 'displayModule.js'. Use their exported functions to increment the counter and display its value, demonstrating the shared state across modules. You need to write this code yourself.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Instructions: Increment the counter, display its value, and then increment it again to show the shared and persistent state. Then display the value again to see how it updates in the other file (module) that requires the 'counterState.js' module.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The working code should log the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxpb7hvr43pe6x7j6zua9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxpb7hvr43pe6x7j6zua9.png" alt="State Management Demo" width="800" height="307"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By completing this homework, you will understand how Node.js caches imported modules, allowing shared state between different parts of your application. This knowledge is fundamental for efficient Node.js development, especially for applications relying on shared data across modules.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stackblitz.com/edit/node-js-commonjs-modules-homework-solution?file=index.js"&gt;Here&lt;/a&gt; are the solutions to the above practical exercises.&lt;/p&gt;

&lt;h2&gt;
  
  
  Transferable Knowledge and Patterns
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Understanding of Synchronous Operations&lt;/strong&gt;: Recognising that &lt;code&gt;require()&lt;/code&gt; operates synchronously, halting execution until a module is fully loaded, &lt;strong&gt;teaches the general principle of synchronous execution in programming&lt;/strong&gt;. This concept is critical for understanding how different parts of a program can affect execution flow and performance, applicable in various programming languages and environments.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Importance of Caching for Performance&lt;/strong&gt;: &lt;strong&gt;The caching mechanism of &lt;code&gt;require()&lt;/code&gt; illustrates a fundamental performance optimisation technique used widely in computing.&lt;/strong&gt; Caching, or temporarily storing data for quick access upon repeated requests, is a concept that can be applied in numerous scenarios beyond module loading, such as web content delivery, database queries, and more.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Module State Sharing and Global State Management&lt;/strong&gt;: The idea that a module's state is shared across all parts of an application that import it underscores the broader concept of global state management. This is a key consideration in software design, relevant for &lt;strong&gt;understanding how state is managed across an application&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Performance Optimisation Practices&lt;/strong&gt;: The advice to require modules at the beginning of files or in strategic code sections to minimise performance impact &lt;strong&gt;teaches a broader lesson on resource management and optimisation in software development&lt;/strong&gt;. Prioritising resource-intensive operations and understanding their impact on the application's responsiveness and efficiency are valuable skills.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Debugging and Troubleshooting&lt;/strong&gt;: The emphasis on how understanding require()'s behaviour aids in debugging extends to a general principle in programming: &lt;strong&gt;knowing how your tools and frameworks work under the hood can significantly improve your ability to troubleshoot and solve problems&lt;/strong&gt;. This is a critical skill for any developer, as it applies to debugging in all programming languages and environments.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That said, understanding how to import modules in Node.js opens up a wealth of critical knowledge that you will learn once and use everywhere. It shows you how to avoid getting stuck. &lt;/p&gt;

&lt;p&gt;You are more than welcome to explore these topics independently because it’s the best way to develop problem-solving skills. But keep in mind that we will gradually cover all these topics through my tutorials because that’s why &lt;a href="https://www.tutorialhell.dev/"&gt;tutorialhell.dev&lt;/a&gt; was created.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learning path from here:
&lt;/h2&gt;

&lt;p&gt;The concepts mentioned in the examples, such as function definitions, template literals, and destructuring, are frequently used, and thus I strongly encourage you to engage in some self-learning as well. Yes, to develop problem-solving skills. I cannot emphasize this enough.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Introduction to Objects&lt;/strong&gt;: Learn about JavaScript objects, including creation, access, methods, property shorthand and computed properties.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Function Definitions&lt;/strong&gt;: Explore different ways to define functions, including function declarations, function expressions, and arrow functions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Template Literals&lt;/strong&gt;: Learn about string interpolation and multi-line strings in ES6, using backticks to define template literals and &lt;code&gt;${}&lt;/code&gt; for embedding expressions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Destructuring&lt;/strong&gt;: Understand the destructuring assignment syntax for arrays and objects.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;File Paths&lt;/strong&gt;: Learn about relative and absolute file paths to grasp how to navigate and reference files within your projects effectively.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;If you like this content, consider following me and subscribing to receive these cool tutorials as soon as they are ready.&lt;/p&gt;

&lt;p&gt;The sunfish on the post cover was sourced from the O'Rly Generator.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>node</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Don't use vanilla JavaScript! I am serious!</title>
      <dc:creator>Sandor | tutorialhell.dev</dc:creator>
      <pubDate>Wed, 28 Jul 2021 11:50:30 +0000</pubDate>
      <link>https://dev.to/sandorturanszky/don-t-use-vanilla-javascript-i-am-serious-56fm</link>
      <guid>https://dev.to/sandorturanszky/don-t-use-vanilla-javascript-i-am-serious-56fm</guid>
      <description>&lt;p&gt;People often claim that frameworks are evil. It's a horrible statement. &lt;/p&gt;

&lt;p&gt;Using vanilla you will find yourself doing things that frameworks have already done and tested for you. &lt;/p&gt;

&lt;p&gt;If you think vanilla is better for simple projects, then you are missing the point of Frontend tools.&lt;/p&gt;

&lt;p&gt;Any simple website is easy to build using a framework in a matter of minutes. Sure thing, you can do it with vanilla, but it will take x10+ longer.&lt;/p&gt;

&lt;p&gt;Take static site generators. Not only do they address all SEO requirements, but they are also blazing fast out of the box. They have tons of integrations only to save you hours, if not days of work and countless bug fixes.&lt;/p&gt;

&lt;p&gt;Easy and free deployment is a great bonus.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A good web developer knows the tools and uses them to be productive.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There is no way you can be productive reinventing the wheel. &lt;/p&gt;

&lt;p&gt;The best devs to work with are pips who found the &lt;strong&gt;balance between delivering and enjoying coding&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Another crazy statement is to push beginners to learn fundamentals without any context. Will cover this problem in my next post.&lt;/p&gt;

&lt;p&gt;Follow for more.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>programming</category>
    </item>
    <item>
      <title>You don't know Redis (Part 2)</title>
      <dc:creator>Sandor | tutorialhell.dev</dc:creator>
      <pubDate>Tue, 22 Jun 2021 19:26:32 +0000</pubDate>
      <link>https://dev.to/sandorturanszky/you-don-t-know-redis-part-2-2581</link>
      <guid>https://dev.to/sandorturanszky/you-don-t-know-redis-part-2-2581</guid>
      <description>&lt;p&gt;In the first part of &lt;a href="https://dev.to/sandorturanszky/you-don-t-know-redis-3onh"&gt;You don't know Redis&lt;/a&gt;, I built an app using Redis as a primary database. For most people, it might sound unusual simply because the key-value data structure seems suboptimal for handling complex data models.&lt;/p&gt;

&lt;p&gt;In practice, the choice of a database often depends on the application’s data-access patterns as well as the current and possible future requirements.&lt;/p&gt;

&lt;p&gt;Redis was a perfect database for a &lt;a href="https://techforitrecruiters.com/questions/active"&gt;Q&amp;amp;A board&lt;/a&gt;. I described how I took advantage of &lt;a href="https://redis.io/topics/data-types#sorted-sets"&gt;sorted sets&lt;/a&gt; and &lt;a href="https://redis.io/topics/data-types#hashes"&gt;hashes&lt;/a&gt; data types to build features efficiently with less code.&lt;/p&gt;

&lt;p&gt;Now I need to extend the Q&amp;amp;A board with registration/login functionality. &lt;/p&gt;

&lt;p&gt;I will use Redis again. There are two reasons for that.&lt;/p&gt;

&lt;p&gt;Firstly, I want to avoid the extra complexity that comes with adding yet another database.&lt;/p&gt;

&lt;p&gt;Secondly, based on the requirements that I have, Redis is suitable for the task. &lt;/p&gt;

&lt;p&gt;Important to note, that user registration and login is not always about only email and password handling. Users may have a lot of relations with other data which can grow complex over time. &lt;/p&gt;

&lt;p&gt;Despite Redis being suitable for my task, it may not be a good choice for other projects. &lt;/p&gt;

&lt;p&gt;Always define what data structure you need now and may need in the future to pick the right database. &lt;/p&gt;

&lt;h3&gt;
  
  
  Implementation
&lt;/h3&gt;

&lt;p&gt;I use serverless functions, the &lt;a href="https://github.com/luin/ioredis"&gt;ioredis&lt;/a&gt; library and &lt;a href="https://upstash.com/?utm_source=sndr_3"&gt;Upstash Serverless Redis&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I can’t help but talk about serverless all the time because it greatly simplifies development. I love when complexity is removed whenever possible and Upstash is doing just that for me. &lt;/p&gt;

&lt;p&gt;I have zero work with setting up Redis. Moreover, I am using Upstash both in development and production.&lt;/p&gt;

&lt;h4&gt;
  
  
  Registration flow
&lt;/h4&gt;

&lt;p&gt;During registration, we collect the user &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;email&lt;/code&gt; and &lt;code&gt;password&lt;/code&gt;. Before registering a user, we need to make sure that the email has not been registered already (is unique in the system).&lt;/p&gt;

&lt;p&gt;Redis does not support constraints. However, we can keep track of all registered emails using a sorted set named &lt;code&gt;emails&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;On every new registration, we can use the &lt;a href="https://redis.io/commands/zscore"&gt;ZSCORE&lt;/a&gt; command to check whether the provided email is already registered.&lt;/p&gt;

&lt;p&gt;If the email is taken, we need to notify the user about it. &lt;/p&gt;

&lt;p&gt;⚠️ Note, that this isn’t the best option because by telling that a given email is registered we provide a simple way for anyone to check whether someone is registered with a particular service, albeit it’s not a big security issue. &lt;/p&gt;

&lt;p&gt;Before we can save a new user, we need to: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Generate a unique user &lt;code&gt;ID&lt;/code&gt;.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can use the &lt;a href="https://redis.io/commands/incr"&gt;INCR&lt;/a&gt; command to always get a unique value by incrementing a number stored at a key by one. If the key does not exist, Redis will set it to &lt;code&gt;0&lt;/code&gt; before performing the operation. This means that the initial value will be &lt;code&gt;1&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;const id = await redis.incr('user_ids') // -&amp;gt; 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whenever you need to create a &lt;a href="https://redis.io/commands/incr#pattern-counter"&gt;counter&lt;/a&gt;, &lt;code&gt;INCR&lt;/code&gt; is a great choice. Or you can build a &lt;a href="https://redis.io/commands/incr#pattern-rate-limiter"&gt;rate-limiter&lt;/a&gt; to protect your API from being overwhelmed by using &lt;code&gt;INCR&lt;/code&gt; together with &lt;a href="https://redis.io/commands/expire"&gt;EXPIRE&lt;/a&gt;. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hash the password with the &lt;a href="https://github.com/kelektiv/node.bcrypt.js/"&gt;bcrypt&lt;/a&gt; library.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const hash = await bcrypt.hash(password, 10)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have the unique user &lt;code&gt;ID&lt;/code&gt; (e.g. user ID is 7) and the hashed password, we can:&lt;br&gt;
&lt;strong&gt;1. Store user details in a hash under the &lt;code&gt;user:{ID}&lt;/code&gt; key.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;redis.hmset('user:7', { 7, name, email, hash })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Knowing the &lt;code&gt;ID&lt;/code&gt;, we can easily get all user details using the &lt;a href="https://redis.io/commands/hgetall"&gt;HGETALL&lt;/a&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;redis.hgetall('user:7');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Add the user’s email to the &lt;code&gt;emails&lt;/code&gt; sorted set.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;redis.zadd('emails', -Math.abs(7), email)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows us to lookup emails to check if they are registered or get the user's &lt;code&gt;ID&lt;/code&gt; by &lt;code&gt;email&lt;/code&gt; which is exactly what we need for the login process. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;redis.zscore('emails', email)&lt;/code&gt; will return the score which is the &lt;code&gt;ID&lt;/code&gt; or &lt;code&gt;nil&lt;/code&gt; if the email is not found.&lt;/p&gt;

&lt;p&gt;Notice how we use this sorted set for two important features, namely ensuring unique emails and looking up users by email. &lt;/p&gt;

&lt;p&gt;But we are taking it one step further and set scores (which represent user &lt;code&gt;ID&lt;/code&gt;s) as negative numbers to mark emails as unverified: &lt;code&gt;-Math.abs(7)&lt;/code&gt;. Then, when the email is verified, we simply convert it to a positive number.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;redis.zadd('emails', Math.abs(7), email)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a specified &lt;code&gt;email&lt;/code&gt; is already a member of the &lt;code&gt;emails&lt;/code&gt; sorted set, Redis will update the score only.&lt;/p&gt;

&lt;p&gt;During the login process, we can always check for negative numbers and request users to verify their email instead of logging them in.&lt;/p&gt;

&lt;p&gt;Retrieving all unverified emails is a trivial operation done with the &lt;a href="https://redis.io/commands/zrangebyscore"&gt;ZRANGEBYSCORE&lt;/a&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;redis.zrangebyscore('emails', '-inf', -1, 'WITHSCORES');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Registration function &lt;a href="https://github.com/sandorTuranszky/questions-and-answers-board-built-with-redis/blob/main/lambda/register.js"&gt;source code&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Login flow
&lt;/h4&gt;

&lt;p&gt;Before logging in the user, we check if the provided email exists in our database. As mentioned before, the &lt;code&gt;score&lt;/code&gt; is the user &lt;code&gt;ID&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;const userId = await redis.zscore('emails', email);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If so, we first check if the email is verified by making sure the &lt;code&gt;ID&lt;/code&gt; is a positive number. If not, we ask users to verify their email.&lt;/p&gt;

&lt;p&gt;If the email is verified, we get the password hash that we stored for the user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const hash = await redis.hget('user:7', 'hash');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and check whether the password is correct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const match = await bcrypt.compare(password, hash);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the password is correct, we generate a token and return it to the client.&lt;/p&gt;

&lt;p&gt;And we are done.&lt;/p&gt;

&lt;p&gt;Login function &lt;a href="https://github.com/sandorTuranszky/questions-and-answers-board-built-with-redis/blob/main/lambda/login.js"&gt;source code&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;As you can see, we needed four Redis commands for registration and only two for login. &lt;/p&gt;

&lt;p&gt;Probably you noticed that while describing the registration and login process with Redis we also revealed two more use cases for Redis, namely counter and rate-limiting.&lt;/p&gt;

&lt;p&gt;Redis has a lot more &lt;a href="https://redislabs.com/redis-best-practices/introduction/"&gt;use cases&lt;/a&gt; beyond cache and learning about them will only make you even more efficient.&lt;/p&gt;

&lt;p&gt;Follow me to read about how I am implementing a secure production-ready registration flow with email verification and password recovery backed by Redis. &lt;/p&gt;




&lt;p&gt;Check out my article on how I implemented the &lt;a href="https://dev.to/sandorturanszky/how-to-create-linkedin-like-reactions-with-serverless-redis-4cad"&gt;LinkedIn-like reactions with Serverless Redis&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>tutorial</category>
      <category>database</category>
    </item>
    <item>
      <title>You don't know Redis</title>
      <dc:creator>Sandor | tutorialhell.dev</dc:creator>
      <pubDate>Wed, 02 Jun 2021 20:09:22 +0000</pubDate>
      <link>https://dev.to/sandorturanszky/you-don-t-know-redis-3onh</link>
      <guid>https://dev.to/sandorturanszky/you-don-t-know-redis-3onh</guid>
      <description>&lt;p&gt;In my previous &lt;a href="https://dev.to/sandorturanszky/how-to-create-linkedin-like-reactions-with-serverless-redis-4cad"&gt;post&lt;/a&gt;, I touched on the point that Redis is more than just an in-memory cache. &lt;/p&gt;

&lt;p&gt;Most people do not even consider Redis as a primary database. There are a lot of use cases where Redis is a perfect choice for non-cache related tasks. &lt;/p&gt;

&lt;p&gt;In this article, I will demonstrate how I built a fully functional Q&amp;amp;A board for asking and upvoting the most interesting questions. &lt;strong&gt;Redis will be used as a primary database&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I will use Gatsby (React), Netlify serverless functions and &lt;a href="https://upstash.com?utm_source=sndr_2"&gt;Upstash Serverless Redis&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Upstash has been a good choice so far and I decided to try it out in a more serious project. I love everything serverless and how it makes things simpler for me. &lt;/p&gt;

&lt;p&gt;Serverless will be a great choice for most tasks however you need to know the pros and cons of the tech you are using. I encourage you to learn more about serverless to get the most out of it. &lt;/p&gt;

&lt;h3&gt;
  
  
  Q&amp;amp;A board features
&lt;/h3&gt;

&lt;p&gt;As you may know, I run a tech newsletter for recruiters where I explain complex tech in simple terms. I have an idea to collect questions from recruiters using a Q&amp;amp;A board and let them vote for questions.&lt;/p&gt;

&lt;p&gt;All questions will eventually be answered in my newsletter, however, the most upvoted questions will be addressed first. &lt;/p&gt;

&lt;p&gt;Anyone can upvote a question and registration is not required.&lt;/p&gt;

&lt;p&gt;Questions will be listed in three tabs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Active - questions sorted by votes and available for voting.&lt;/li&gt;
&lt;li&gt;Most recent - questions sorted by date (newest first).&lt;/li&gt;
&lt;li&gt;Answered - only questions that have answers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Upvoting&lt;/strong&gt; will be one of the most frequently used features and Redis has a data type and optimized commands for it.&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://redis.io/topics/data-types#sorted-sets"&gt;Sorted set&lt;/a&gt; is ideal for this task because all its members are automatically sorted by the score.&lt;/p&gt;

&lt;p&gt;Scores are numeric values that we will associate with votes. It is very easy to increment a score (add a vote) by using the &lt;a href="https://redis.io/commands/zincrby"&gt;ZINCRBY&lt;/a&gt; command. &lt;/p&gt;

&lt;p&gt;We will also leverage scores for handling unmoderated questions by setting the score for them to &lt;code&gt;0&lt;/code&gt;. All approved questions will have a score of &lt;code&gt;1+&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;It allows us to fetch all unmoderated questions by simply using the &lt;a href="https://redis.io/commands/ZRANGEBYSCORE"&gt;ZRANGEBYSCORE&lt;/a&gt; command specifying the &lt;code&gt;min&lt;/code&gt; and &lt;code&gt;max&lt;/code&gt; arguments as &lt;code&gt;0&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;To fetch all approved questions sorted by the score (highest first) we can use the &lt;a href="https://redis.io/commands/ZREVRANGEBYSCORE"&gt;ZREVRANGEBYSCORE&lt;/a&gt; command setting the &lt;code&gt;min&lt;/code&gt; score argument to &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is great that by using just a few Redis commands we can also solve logical tasks along the way. &lt;strong&gt;Lower complexity is a huge benefit&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;We will also use sorted sets for sorting questions by date or filtering questions that have answers. I will explain it in more detail in a moment.&lt;/p&gt;

&lt;p&gt;Less frequent operations, namely creating, updating and deleting questions are also easy to accomplish using &lt;a href="https://redis.io/topics/data-types#hashes"&gt;hashes&lt;/a&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  Implementation details
&lt;/h3&gt;

&lt;p&gt;The most interesting part is always the actual implementation. I use serverless functions and the &lt;a href="https://github.com/luin/ioredis"&gt;ioredis&lt;/a&gt; library and I will link the source code explaining what it does.&lt;/p&gt;

&lt;p&gt;This article is dedicated to client-facing functionality. Although I will explain admin-related functions, in the final source code there will be no backend interface. You will need to use Postman or a similar tool to call the admin related endpoints.&lt;/p&gt;

&lt;p&gt;Let’s take a look at the API endpoints and what they do.&lt;/p&gt;

&lt;h4&gt;
  
  
  Add a question
&lt;/h4&gt;

&lt;p&gt;Users can create questions. All questions require moderation before they become visible.&lt;/p&gt;

&lt;p&gt;A question is an object and Redis hash is a perfect data type to represent objects. &lt;/p&gt;

&lt;p&gt;This is the structure of a questions:&lt;br&gt;
&lt;code&gt;{"datetime":"1633992009", "question":"What are Frontend technologies?", "author":"Alex", "email":"alex@email.com", “score:” “0”, “url”: “www.answer.com” }&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We will store questions in hashes using the &lt;a href="https://redis.io/commands/HMSET"&gt;HMSET&lt;/a&gt; command which takes a key and multiple key-value pairs.&lt;/p&gt;

&lt;p&gt;The key schema is &lt;code&gt;question:{ID}&lt;/code&gt; where &lt;code&gt;ID&lt;/code&gt; is the question ID generated using the &lt;a href="https://github.com/uuidjs/uuid"&gt;uuid&lt;/a&gt; library.&lt;/p&gt;

&lt;p&gt;This is a new question and there is no answer yet. We skip the &lt;code&gt;url&lt;/code&gt; property but it will be an easy task to add it later using the &lt;a href="https://redis.io/commands/hset"&gt;HSET&lt;/a&gt; command. &lt;/p&gt;

&lt;p&gt;The score for a newly created question is &lt;code&gt;0&lt;/code&gt; by default. By our design, it means that this question needs moderation and will not be listed because we only fetch questions with scores starting from &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Since we keep the score value in a hash, we’ll need to update it whenever it changes. There is a &lt;a href="https://redis.io/commands/hincrby"&gt;HINCRBY&lt;/a&gt; command that we can use to easily increment values in hashes.&lt;/p&gt;

&lt;p&gt;As you can see, using Redis hashes solves a lot more for us than just storing data.&lt;/p&gt;

&lt;p&gt;Now that we know how we’ll store questions, we also need to keep track of questions to be able to fetch them later.&lt;/p&gt;

&lt;p&gt;For that, we add the &lt;code&gt;ID&lt;/code&gt; of a question to a sorted set with a score of &lt;code&gt;0&lt;/code&gt; using the &lt;a href="https://redis.io/commands/zadd"&gt;ZADD&lt;/a&gt; command. A sorted set will allow us to fetch question IDs sorted by scores.&lt;/p&gt;

&lt;p&gt;As you can see, we are setting the score to &lt;code&gt;0&lt;/code&gt; just like we do it for the &lt;code&gt;score&lt;/code&gt; property in the hash above. The reason why we duplicate the score in a hash is that we need it when showing the most recent questions or questions that have answers. &lt;/p&gt;

&lt;p&gt;For instance, the most recent questions are stored in a separate sorted set with timestamp as a score hence the original score value is not available unless it’s duplicated in a hash.&lt;/p&gt;

&lt;p&gt;Since we store the score in two places, we need to make sure that values are updated both in a hash and in a sorted set. We use the &lt;a href="https://redis.io/commands/multi"&gt;MULTI&lt;/a&gt; command to execute commands in a manner where either all commands are executed successfully or they are rolled back. Check &lt;a href="https://redis.io/topics/transactions"&gt;Redis Transactions&lt;/a&gt; for more details.&lt;/p&gt;

&lt;p&gt;We will use this approach where applicable. For example, &lt;code&gt;HMSET&lt;/code&gt; and &lt;code&gt;ZADD&lt;/code&gt; will also be executed in a transaction (see source code below).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ZADD&lt;/code&gt; command takes a key and our schema for it is &lt;code&gt;questions:{boardID}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;All questions are mapped to a &lt;code&gt;boardID&lt;/code&gt;. For now, it’s a hardcoded value because I need one board only. In the future, I may decide to introduce more boards, for example, separately for Frontend, Backend, QA and so on. It’s good to have the needed structure in place.&lt;/p&gt;

&lt;p&gt;Endpoint:&lt;br&gt;
&lt;code&gt;POST /api/create_question&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here is the source code for the &lt;a href="https://github.com/sandorTuranszky/questions-and-answers-board-built-with-redis/blob/main/lambda/question_create.js"&gt;create_question&lt;/a&gt; serverless function.&lt;/p&gt;

&lt;h4&gt;
  
  
  Approve a question
&lt;/h4&gt;

&lt;p&gt;Before a question becomes available for voting, it needs to be approved. Approving a question means the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Update the score value in hash from &lt;code&gt;0&lt;/code&gt; to &lt;code&gt;1&lt;/code&gt; using &lt;a href="https://redis.io/commands/hincrby"&gt;HINCRBY&lt;/a&gt; command.&lt;/li&gt;
&lt;li&gt;Update the score value in the &lt;code&gt;questions:{boardID}&lt;/code&gt; sorted set from &lt;code&gt;0&lt;/code&gt; to &lt;code&gt;1&lt;/code&gt; using the &lt;a href="https://redis.io/commands/ZADD"&gt;ZADD&lt;/a&gt; command.&lt;/li&gt;
&lt;li&gt;Add the question &lt;code&gt;ID&lt;/code&gt; to the &lt;code&gt;questions:{boardID}:time&lt;/code&gt; sorted set with the timestamp as the score to fetch questions sorted by date (most recent questions) using the same &lt;code&gt;ZADD&lt;/code&gt; command.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We can get the timestamp by looking up the question by its &lt;code&gt;ID&lt;/code&gt; using the &lt;a href="https://redis.io/commands/hget"&gt;HGET&lt;/a&gt; command.&lt;/p&gt;

&lt;p&gt;Once we have it, we can execute the remaining three commands in a transaction. This will ensure that the score value is identical in the hash and the sorted set. &lt;/p&gt;

&lt;p&gt;To fetch all unapproved questions the &lt;a href="https://redis.io/commands/ZRANGEBYSCORE"&gt;ZRANGEBYSCORE&lt;/a&gt; command is used with the &lt;code&gt;min&lt;/code&gt; and &lt;code&gt;max&lt;/code&gt; values as &lt;code&gt;0&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;ZRANGEBYSCORE&lt;/code&gt; returns elements ordered by a score from low to high while &lt;code&gt;ZREVRANGEBYSCORE&lt;/code&gt; - from high to low. We’ll use the latter to fetch questions ordered by the number of votes. &lt;/p&gt;

&lt;p&gt;Endpoint for fetching all unapproved questions:&lt;br&gt;
&lt;code&gt;GET /api/questions_unapproved&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Endpoint for approving a question:&lt;br&gt;
&lt;code&gt;PUT: /api/question_approve&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here is the source code for the &lt;a href="https://github.com/sandorTuranszky/questions-and-answers-board-built-with-redis/blob/main/lambda/questions_unapproved.js"&gt;questions_unapproved&lt;/a&gt; serverless function. For the most part, this code is similar to other &lt;code&gt;GET&lt;/code&gt; endpoints and I will explain it in the next section.&lt;/p&gt;

&lt;p&gt;Here is the source code for the &lt;a href="https://github.com/sandorTuranszky/questions-and-answers-board-built-with-redis/blob/main/lambda/question_approve.js"&gt;question_approve&lt;/a&gt; serverless function.&lt;/p&gt;

&lt;h4&gt;
  
  
  Fetch approved questions
&lt;/h4&gt;

&lt;p&gt;To fetch all approved questions we use the &lt;code&gt;ZREVRANGEBYSCORE&lt;/code&gt; command setting the &lt;code&gt;min&lt;/code&gt; argument to &lt;code&gt;1&lt;/code&gt; in order to skip all unapproved questions.&lt;/p&gt;

&lt;p&gt;As a result, we get a list of IDs only. We will need to iterate over them to fetch question details using the &lt;a href="https://redis.io/commands/hgetall"&gt;HGETALL&lt;/a&gt; command. &lt;/p&gt;

&lt;p&gt;Depending on the number of questions fetched, this approach can become expensive and block the event loop in Node (I am using Node.js). There are a few ways to mitigate this potential problem. &lt;/p&gt;

&lt;p&gt;For example, we can use &lt;code&gt;ZREVRANGEBYSCORE&lt;/code&gt; with the optional &lt;code&gt;LIMIT&lt;/code&gt; argument to only get a range of elements. However, if the offset is large, &lt;a href="https://redis.io/commands/zrangebyscore"&gt;it can add up to O(N) time complexity&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Or we can use a Lua script to extend Redis by adding a custom command to fetch question details based on IDs from a stored set without us doing it manually in the application layer. &lt;/p&gt;

&lt;p&gt;In my opinion, it would be overhead in this case. Besides that, one must be very careful with Lua scripts because they block Redis and you can’t do expensive tasks with them without introducing performance degradation. This approach may be cleaner however we would still use the &lt;code&gt;LIMIT&lt;/code&gt; to avoid large amounts of data.&lt;/p&gt;

&lt;p&gt;Always research the pros and cons before the final implementation. As long as you understand the potential issues and have evaluated ways to mitigate them, you are safe.&lt;/p&gt;

&lt;p&gt;In my case, I know that it will take significant time before I will have enough questions to face this issue. No need for &lt;a href="https://en.wikipedia.org/wiki/Program_optimization"&gt;premature optimization&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Endpoint:&lt;br&gt;
&lt;code&gt;GET /api/questions&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here is the source code for the &lt;a href="https://github.com/sandorTuranszky/questions-and-answers-board-built-with-redis/blob/main/lambda/questions.js"&gt;questions&lt;/a&gt; serverless function.&lt;/p&gt;

&lt;h4&gt;
  
  
  Vote for a question
&lt;/h4&gt;

&lt;p&gt;The process of upvoting a question consists of two important steps that both need to be executed as a transaction.&lt;/p&gt;

&lt;p&gt;However, before manipulating the score, we need to check if this question has no answer (&lt;code&gt;url&lt;/code&gt; property). In other words, we do not allow anyone to vote for questions that have been answered. &lt;/p&gt;

&lt;p&gt;The vote button is disabled for such questions. But we do not trust anyone on the internet and therefore check on the server if a given &lt;code&gt;ID&lt;/code&gt; exists in the &lt;code&gt;questions:{boardID}:answered&lt;/code&gt; sorted set using the &lt;a href="https://redis.io/commands/zscore"&gt;ZSCORE&lt;/a&gt; command. If so, we do nothing.&lt;/p&gt;

&lt;p&gt;We use the &lt;a href="https://redis.io/commands/hincrby"&gt;HINCRBY&lt;/a&gt; command to increment the score in the hash by &lt;code&gt;1&lt;/code&gt; and the &lt;a href="https://redis.io/commands/zincrby"&gt;ZINCRBY&lt;/a&gt; command to increment the score in the sorted set by &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Endpoint:&lt;br&gt;
&lt;code&gt;PATCH /api/question_upvote&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here is the source code for the &lt;a href="https://github.com/sandorTuranszky/questions-and-answers-board-built-with-redis/blob/main/lambda/question_upvote.js"&gt;question_upvote&lt;/a&gt; serverless function.&lt;/p&gt;

&lt;h4&gt;
  
  
  Fetch most recent approved questions
&lt;/h4&gt;

&lt;p&gt;It’s very similar to how we fetch all approved questions with the only difference being that we read another sorted set where the key schema is &lt;code&gt;questions:{boardID}:time&lt;/code&gt;. Since we used the timestamp as a score, the &lt;code&gt;ZREVRANGEBYSCORE&lt;/code&gt; command returns IDs sorted in descending order.&lt;/p&gt;

&lt;p&gt;Endpoint:&lt;br&gt;
&lt;code&gt;PATCH /api/questions_recent&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here is the source code for the &lt;a href="https://github.com/sandorTuranszky/questions-and-answers-board-built-with-redis/blob/main/lambda/questions_recent.js"&gt;questions_recent&lt;/a&gt; serverless function.&lt;/p&gt;

&lt;h4&gt;
  
  
  Update a question with an answer
&lt;/h4&gt;

&lt;p&gt;Updating or adding new properties to hashes is simple with the &lt;code&gt;HSET&lt;/code&gt; command. However, when we add an answer, we move the question from the &lt;code&gt;questions:{boardID}&lt;/code&gt; sorted set to the &lt;code&gt;questions:{boardID}:answered&lt;/code&gt; one preserving the score.&lt;/p&gt;

&lt;p&gt;To do so, we need to know the score of the question and we obtain it using the &lt;a href="https://redis.io/commands/zscore"&gt;ZSCORE&lt;/a&gt; command. Answered questions will be sorted by score in descending order.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;update the hash with the &lt;code&gt;url&lt;/code&gt; property using the &lt;code&gt;HSET&lt;/code&gt; command; &lt;/li&gt;
&lt;li&gt;add the hash to the &lt;code&gt;questions:{boardID}:answered&lt;/code&gt; sorted set using &lt;code&gt;ZADD&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;remove the question from the &lt;code&gt;questions:{boardID}&lt;/code&gt; sorted set running the &lt;code&gt;ZREM&lt;/code&gt; command.&lt;/li&gt;
&lt;li&gt;remove the question from the &lt;code&gt;questions:{boardID}:time&lt;/code&gt; sorted set running the &lt;code&gt;ZREM&lt;/code&gt; command.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All four commands are executed in a transaction. &lt;/p&gt;

&lt;p&gt;Endpoint:&lt;br&gt;
&lt;code&gt;PATCH /api/question_add_answer&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here is the source code for the &lt;a href="https://github.com/sandorTuranszky/questions-and-answers-board-built-with-redis/blob/main/lambda/question_add_answer.js"&gt;question_add_answer&lt;/a&gt; serverless function.&lt;/p&gt;

&lt;h4&gt;
  
  
  Fetch questions with answers
&lt;/h4&gt;

&lt;p&gt;Again, the process is similar to fetching all approved questions. This time from the &lt;code&gt;questions:{boardID}:answered&lt;/code&gt; sorted set.&lt;/p&gt;

&lt;p&gt;Endpoint:&lt;br&gt;
&lt;code&gt;PATCH /api/questions_unswered&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here is the source code for the &lt;a href="https://github.com/sandorTuranszky/questions-and-answers-board-built-with-redis/blob/main/lambda/questions_unswered.js"&gt;questions_unswered&lt;/a&gt; serverless function.&lt;/p&gt;




&lt;p&gt;Full &lt;a href="https://github.com/sandorTuranszky/questions-and-answers-board-built-with-redis"&gt;source code&lt;/a&gt;.&lt;br&gt;
Working &lt;a href="https://techforitrecruiters.com/questions"&gt;DEMO&lt;/a&gt; on my website.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Redis has a lot of use-cases going way beyond cache. I’ve demonstrated only one of the multiple applications for Redis that one can consider instead of reaching for an SQL database right away.&lt;/p&gt;

&lt;p&gt;Of course, if you already use a database, adding yet another one may be an overhead. &lt;/p&gt;

&lt;p&gt;Redis is very fast and scales well. Most commercial projects have Redis in their tech stack and often use them as an auxiliary database, not just in-memory cache.&lt;/p&gt;

&lt;p&gt;I strongly recommend learning about &lt;a href="https://redislabs.com/redis-best-practices/introduction/"&gt;Redis data patterns and best practices&lt;/a&gt; to realize how powerful it is and benefit from this knowledge in the long run. &lt;/p&gt;

&lt;p&gt;Check my previous article where I created &lt;a href="https://dev.to/sandorturanszky/how-to-create-linkedin-like-reactions-with-serverless-redis-4cad"&gt;LinkedIn-like reactions with Serverless Redis&lt;/a&gt; if you haven’t already.&lt;/p&gt;

&lt;p&gt;Here is &lt;a href="https://dev.to/sandorturanszky/you-don-t-know-redis-part-2-2581"&gt;You don't know Redis (Part 2)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Follow for more.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>tutorial</category>
      <category>database</category>
    </item>
    <item>
      <title>How to create LinkedIn-like reactions with Serverless Redis</title>
      <dc:creator>Sandor | tutorialhell.dev</dc:creator>
      <pubDate>Wed, 19 May 2021 11:12:31 +0000</pubDate>
      <link>https://dev.to/sandorturanszky/how-to-create-linkedin-like-reactions-with-serverless-redis-4cad</link>
      <guid>https://dev.to/sandorturanszky/how-to-create-linkedin-like-reactions-with-serverless-redis-4cad</guid>
      <description>&lt;p&gt;As a side hustle, I teach tech recruiters web and software development technologies using plain English. It helps them with understanding job specs and resumes and it makes all of us, tech people, happier. &lt;/p&gt;

&lt;p&gt;I run a weekly newsletter and often get feedback from recruiters via email or LinkedIn DMs.&lt;/p&gt;

&lt;p&gt;I thought that I could try to collect feedback using the “Reactions” feature just like LinkedIn or Facebook does. It’s not as informative as personalised messages but is a simple feature that may incentivize more people to provide some general feedback.&lt;/p&gt;

&lt;p&gt;Either way, it’s worth trying and as a software developer, I can’t wait to implement it.&lt;/p&gt;

&lt;p&gt;This tutorial is about implementing a feature that will be used in real life on my project. &lt;/p&gt;

&lt;h3&gt;
  
  
  Planning
&lt;/h3&gt;

&lt;p&gt;As with any feature or project, we start with the planning phase.&lt;/p&gt;

&lt;p&gt;I am going to stick with LinkedIn-like reactions because they are more appropriate for the type of content I post. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbo72ffqe726lwm6oiuk5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbo72ffqe726lwm6oiuk5.png" alt="Alt Text" width="800" height="208"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I will use the Like, Insightful and Curious reactions and will substitute them with 👍, 💡 and 🤔 emojis respectively.&lt;/p&gt;

&lt;p&gt;My static website is built with Gatsby. I do not use a database but I need one to store reactions. I need to decide what database to use.&lt;/p&gt;

&lt;p&gt;I deploy with Netlify and I use functions for backend related functionality. That means that I go serverless. Ideally, I need a serverless database too to not have to deal with deploying my own DB or overpaying for PaaS with fixed plans.&lt;/p&gt;

&lt;p&gt;As of writing this article, I am using Netlify's free tier and can easily go for more features with a paid plan at a very good price. Using a reasonably priced database would be a perfect complement to my current tech stack.&lt;/p&gt;

&lt;p&gt;These are the tasks:&lt;/p&gt;

&lt;h5&gt;
  
  
  1. Research database options.
&lt;/h5&gt;

&lt;p&gt;I need to find out what serverless databases exist and choose one.&lt;/p&gt;

&lt;h5&gt;
  
  
  2. Create a Serverless Backend with functions for:
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;storing reactions and incrementing/decrementing the count&lt;/li&gt;
&lt;li&gt;fetching reactions by a post id&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  3. Create the "reactions" component.
&lt;/h5&gt;

&lt;p&gt;Gatsby is based on React and I will build a simple “reactions” component.&lt;/p&gt;

&lt;h5&gt;
  
  
  4. Put it all together to make my static website a little bit dynamic.
&lt;/h5&gt;

&lt;p&gt;Static websites can have dynamic features and it’s what makes them, static site generators so incredibly awesome. &lt;/p&gt;

&lt;p&gt;In this tutorial, I will focus on the first two tasks. The “reactions" component implementation you can check in the  &lt;a href="https://github.com/sandorTuranszky/gatsby-starter-blog-with-LinkedIn-like-reactions/tree/main/src/components"&gt;source code&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Research
&lt;/h3&gt;

&lt;p&gt;This second phase in Software Development Life Cycle (SDLC) is named Prototyping but I call it Research in this tutorial because I will skip the proof of concept (POC) part.&lt;/p&gt;

&lt;p&gt;Research is always fun because it provides a great opportunity to learn about new technologies. While this process is interesting, it can also take a lot of time if we do not make our research more specific. &lt;/p&gt;

&lt;p&gt;SQL and NoSQL are the most common database types. The choice isn’t difficult if we know what data will be stored. Let’s quickly take a look at what data structure we will have.&lt;/p&gt;

&lt;p&gt;Every &lt;strong&gt;post has a set of reactions&lt;/strong&gt; and we need to &lt;strong&gt;count&lt;/strong&gt; those &lt;strong&gt;reactions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Since I simply want to get an idea about how people react to my posts, I will not require them to log in or limit the types of reactions. &lt;/p&gt;

&lt;p&gt;Based on the above, our data structure could look as follows for a post with 3 likes, 12 insightful and 7 curious reactions: &lt;code&gt;{ "like":3, "insightful":12, "curious":7 }&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;A NoSQL database looks like a good choice for storing and manipulating a data structure like the above. &lt;/p&gt;

&lt;p&gt;I immediately thought of MongoDB and Redis. However, based on how we will manipulate data, namely persist it once and then increment or decrement certain values, I decide in favor of Redis.&lt;/p&gt;

&lt;p&gt;The reason being is that Redis has built-in and performance-optimized commands to support what we need.&lt;/p&gt;

&lt;p&gt;Besides that, I found a serverless Redis database &lt;a href="https://upstash.com?utm_source=sndr_1"&gt;Upstash&lt;/a&gt; which looks simple and has reasonable pricing including a free plan. I like starting free and paying as I scale.&lt;/p&gt;

&lt;p&gt;Note that we are using Redis as a &lt;strong&gt;primary database&lt;/strong&gt;. Redis can be configured to write data to disk which provides a degree of &lt;a href="https://redis.io/topics/persistence"&gt;data safety&lt;/a&gt; comparable to what PostgreSQL offers.&lt;/p&gt;

&lt;p&gt;Redis solves a much wider range of problems than just in-memory caching and can be used either as a primary database or as an additional database for solving problems that other databases struggle with.&lt;/p&gt;

&lt;p&gt;I like that Upstash &lt;a href="https://docs.upstash.com/overall/durability?utm_source=sndr_1"&gt;enables persistence&lt;/a&gt; by default keeping data both in memory and disk. This removes the headache of configuring things which would be an overhead for a task like this one. This is why I always use serverless and PaaS whenever possible.&lt;/p&gt;

&lt;p&gt;To sum up this part, let me share with you an interesting &lt;a href="https://redislabs.com/ebook/redis-in-action/"&gt;free e-book&lt;/a&gt; called “Redis in Action” packed with valuable information and use-cases for Redis.&lt;/p&gt;

&lt;h3&gt;
  
  
  Serverless Backend
&lt;/h3&gt;

&lt;p&gt;I will use Netlify’s serverless functions with Node instead of creating my own backend. You are free to use any backend architecture. &lt;/p&gt;

&lt;p&gt;The easiest way to connect Redis with Upstash is to use the &lt;a href="https://github.com/NodeRedis/node-redis"&gt;redis-client&lt;/a&gt; as described &lt;a href="https://docs.upstash.com/howto/connectwithtls?utm_source=sndr_1"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First things first, we need to create a Serverless Redis database following this &lt;a href="https://docs.upstash.com?utm_source=sndr_1"&gt;Getting Started guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Please, note that I leave out the &lt;a href="https://docs.upstash.com/overall/consistency?utm_source=sndr_1"&gt;Strong Consistency Mode&lt;/a&gt;, because Eventual Consistency is suitable for my task.&lt;/p&gt;

&lt;p&gt;This is the schema for naming the keys: &lt;strong&gt;post:{id}:{reaction}&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;post:{id}:like&lt;/strong&gt; -&amp;gt; &lt;code&gt;post:856f9d0a:like&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;post:{id}:insightful&lt;/strong&gt; -&amp;gt; &lt;code&gt;post:856f9d0a:insightful&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;post:{id}:curious&lt;/strong&gt; -&amp;gt; &lt;code&gt;post:856f9d0a:curious&lt;/code&gt; &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We could go with the most basic kind of Redis value known as &lt;a href="https://redis.io/topics/data-types#strings"&gt;Strings&lt;/a&gt;. But we’ll go with &lt;a href="https://redis.io/topics/data-types#hashes"&gt;hashes&lt;/a&gt; because we want to store objects as value and it is advisable to &lt;a href="https://redis.io/topics/memory-optimization"&gt;use hashes when possible&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is how we do it for a given post ID:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To increment a reaction by 1, for example, “Like”, we will use the &lt;a href="https://redis.io/commands/hincrby"&gt;HINCRBY&lt;/a&gt; command. If the key does not exist, it will create the key and set its value to 0 before incrementing. If the key does exist, it will simply increment by the value we provide. It allows us to reuse this function both for creating and updating reactions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;HINCRBY post:856f9d0a:reactions like 1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here is the &lt;a href="https://github.com/sandorTuranszky/gatsby-starter-blog-with-LinkedIn-like-reactions/blob/main/lambda/react.js"&gt;implementation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we had to decrement reactions for cases when a reaction is removed or changed, we would simply pass the value to be incremented by a negative number: -1&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To read reactions for a post ID we’ll use the &lt;a href="https://redis.io/commands/hgetall"&gt;HGETALL&lt;/a&gt; command which returns key-value pairs:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;HGETALL post:856f9d0a:reactions&lt;/code&gt; -&amp;gt; &lt;code&gt;// will return {"like":"3","insightful":"1","curious":"2"}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here is the &lt;a href="https://github.com/sandorTuranszky/gatsby-starter-blog-with-LinkedIn-like-reactions/blob/main/lambda/reactions.js"&gt;implementation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this approach, knowing the post ID, we can efficiently set, update and retrieve reactions for a post with a ridiculously small amount of code.&lt;/p&gt;

&lt;p&gt;Post IDs are not short but we will use them for naming keys because they will not cause any noticeable memory usage increase in our case. &lt;/p&gt;

&lt;p&gt;But you should always keep in mind that along with having a readable key naming schema, you need to control the length of keys. Long keys can use more memory and even cause performance implications as described in the &lt;a href="https://redis.io/topics/data-types-intro#redis-keys"&gt;Redis keys&lt;/a&gt; section.&lt;/p&gt;

&lt;h3&gt;
  
  
  Off-topic
&lt;/h3&gt;

&lt;p&gt;Even though this is not directly related to this tutorial, I know that later I will want to show the most popular posts based on reactions. &lt;/p&gt;

&lt;p&gt;To get posts with the most Likes, Insightful and Curious reactions, we need to track them using a &lt;a href="https://redis.io/topics/data-types#sorted-sets"&gt;sorted set&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For instance, this is how we store a post that received a Like reaction:&lt;br&gt;
&lt;a href="https://redis.io/commands/zincrby"&gt;ZINCRBY&lt;/a&gt; &lt;code&gt;reaction:like 1 post:856f9d0a&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And this is how we get the most liked 5 posts:&lt;br&gt;
&lt;a href="https://redis.io/commands/zrevrangebyscore"&gt;ZREVRANGEBYSCORE&lt;/a&gt; &lt;code&gt;reaction:like +INF -INF withscores LIMIT 0 5&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I will provide more details and a working implementation in one of my next posts.&lt;/p&gt;

&lt;p&gt;It’s always a good idea to design a system taking into account all known future requirements and choose technologies that will support them in the future.&lt;/p&gt;




&lt;p&gt;Here is a working implementation on my &lt;a href="https://techforitrecruiters.com/blog"&gt;website&lt;/a&gt;. Pick any &lt;a href="https://techforitrecruiters.com/blog/2021-05-02-what-is-a-content-management-system/"&gt;post&lt;/a&gt; and you will find reactions at the bottom. The source code you can find &lt;a href="https://github.com/sandorTuranszky/gatsby-starter-blog-with-LinkedIn-like-reactions"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe2j18fogq7ge56vjjouh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe2j18fogq7ge56vjjouh.png" alt="Alt Text" width="800" height="272"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;If you still perceive Redis as an in-memory cache, get ready because I have more posts coming up that cover powerful features backed by this amazing database. &lt;/p&gt;

&lt;p&gt;The next post will be about how I built a Q&amp;amp;A board for asking and upvoting the most interesting questions using Redis. &lt;/p&gt;

&lt;p&gt;Follow for more!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>tutorial</category>
      <category>database</category>
    </item>
    <item>
      <title>How to get out of tutorial hell and avoid the imposter syndrome</title>
      <dc:creator>Sandor | tutorialhell.dev</dc:creator>
      <pubDate>Thu, 28 Jan 2021 12:23:10 +0000</pubDate>
      <link>https://dev.to/sandorturanszky/how-to-get-out-of-tutorial-hell-and-avoid-the-imposter-syndrome-13i9</link>
      <guid>https://dev.to/sandorturanszky/how-to-get-out-of-tutorial-hell-and-avoid-the-imposter-syndrome-13i9</guid>
      <description>&lt;p&gt;Taking tutorials may be exciting at the beginning. But it doesn't take long to realize that what you've learned in a tutorial is hard to put into practice.&lt;/p&gt;

&lt;p&gt;The problem with most tutorials is that they teach you how to solve a specific small task, often a tiny one. It's impossible to translate such knowledge onto a larger task, let alone a project. &lt;/p&gt;

&lt;p&gt;Imagine if your driving instructor would tell you to look at a smashed insect on the windshield instead of looking through it to see the road ahead. You would end up in trouble in a matter of seconds. &lt;/p&gt;

&lt;p&gt;The same happens with tutorials. You can't learn how to build a project by taking tutorials that teach you how to create an HTML form and submit it using an API endpoint, which is magically available...&lt;/p&gt;

&lt;p&gt;It just doesn't work. &lt;/p&gt;

&lt;p&gt;Most of us have a vague idea about how exactly cars function, but we know about the engine, about the steering wheel, accelerator, breaks, gearshift, etc and what those things are for.&lt;/p&gt;

&lt;p&gt;If someone told you to repair a flat tyre, you'd either do it yourself or call a service to do it for you. Either way, you will get it done. Simply because you know what this part is and where to look for a solution. &lt;/p&gt;

&lt;p&gt;Similarly, in software development, you can figure out how to build a feature if you understand its role in the system. You can ask the right questions if you hesitate. You can develop it yourself or decide to go for a third-party solution. &lt;/p&gt;

&lt;p&gt;Learning web development should start with a general overview of all the building blocks. For example, Frontend, Backend, Databases, testing and deployment as well as the Software Development Life Cycle (SDLC).&lt;/p&gt;

&lt;p&gt;Knowing how software is developed gives a very good idea about the necessary steps to take to build reliable software and unlock Continuous Integration and Delivery (CI/CD) that all companies mention in job specs.&lt;/p&gt;

&lt;p&gt;With this knowledge, it will be so easy to develop the right mindset - the DevOps mindset. &lt;/p&gt;

&lt;p&gt;Why would a beginner need it? Those things are relatively easy to explain. They open up a whole different perspective. It's like seeing an ocean instead of a drop.&lt;/p&gt;

&lt;p&gt;Once you have a general understanding of where you are heading, you can start learning every bit in more detail. &lt;/p&gt;

&lt;p&gt;Another crucial point is to learn in a reverse order. First, you define what you want to build and then learn what is needed to build it. This way you can see how code maps to either what you see on the screen or the result you expect.&lt;/p&gt;

&lt;p&gt;The logical first step for a web developer is to learn Frontend technologies: HTML, CSS and JavaScript. &lt;/p&gt;

&lt;p&gt;Let's take HTML and CSS as an example. It's not only about the tags and styles. It's also about structures, performance, CSS preprocessors and CSS frameworks.&lt;/p&gt;

&lt;p&gt;Beginners tend to keep all styles in one file until it grows huge and becomes hard to maintain. &lt;/p&gt;

&lt;p&gt;Then they start to realize they are missing something. But what is it? &lt;br&gt;
If they were told about CSS methodologies that help with writing modular, reusable and scalable code, beginners would quickly understand the idea behind them. Instead of struggling, they would know what to learn next to fix the problem. &lt;/p&gt;

&lt;p&gt;Nobody explains to beginners, that animations can be done both with CSS and JavaScript and that depending on the task, one can be faster than the other. &lt;/p&gt;

&lt;p&gt;Just like with CSS methodologies, knowing that animation performance depends on the implementation will help beginners with finding a solution when they run into performance issues. Simply, by googling "CSS Versus JavaScript Animations" one can find useful resources that explain this topic in depth. &lt;/p&gt;

&lt;p&gt;The same is true with understanding reflow and repaint in the browser and how it's affecting web performance. When solving a performance problem, beginners will find the solution because they know that it can be one of the reasons causing the issue. &lt;/p&gt;

&lt;p&gt;Knowing the limitations of CSS, beginners will quickly get the idea of CSS preprocessors and appreciate the benefits they bring. This will naturally lead to learning the build tools and task runners.&lt;/p&gt;

&lt;p&gt;CSS frameworks are great but only when you understand when and why using them. &lt;/p&gt;

&lt;p&gt;It is so much easier to absorb knowledge when you see how it applies in practice by solving a real problem. &lt;/p&gt;

&lt;p&gt;When it comes to JavaScript, not knowing about security issues is the same as driving a car without learning the driving code. Many beginners think security is only Backend related...&lt;/p&gt;

&lt;p&gt;Algorithms are a must because they are closely related to the performance and reliability of the system. And they are easy to understand when used in practice.&lt;/p&gt;

&lt;p&gt;Design patterns are a battle-tested solution to common problems and beginners need to know what they are for to avoid reinventing the wheel. &lt;/p&gt;

&lt;p&gt;Beginners need to have a rough idea about all of that I mentioned above. When they need to deal with security, they will know about the OWASP top 10 and will explore it. Then put new knowledge into practice.&lt;/p&gt;

&lt;p&gt;When it comes to solving performance issues in javascript, they will doubt data structures they use and reach for resources on that topic. &lt;/p&gt;

&lt;p&gt;And instead of reinventing the wheel, they will first check if there are existing patterns that solve what they need.&lt;/p&gt;

&lt;p&gt;The bottom line is that it's not about how much you learn. It's about knowing what's out there that you may need to explore to solve the problem. &lt;/p&gt;

&lt;p&gt;The scope of web development is the critical knowledge the most beginners do not have, unfortunately.&lt;/p&gt;

&lt;p&gt;It is exactly what I am solving with my free beginner-friendly tutorial on creating Trello clone.&lt;/p&gt;

&lt;p&gt;I explain every step by providing details on what is needed to build large-scale apps. I let beginners run into issues and help them with finding a solution. &lt;/p&gt;

&lt;p&gt;I teach beginners web development concepts, best practices and patterns that they will apply in any project. &lt;/p&gt;

&lt;p&gt;I help to avoid or get out of tutorial hell as well as avoid the imposter syndrome. &lt;/p&gt;

&lt;p&gt;Comment if you'd like to take my tutorial. It's free. &lt;/p&gt;

</description>
      <category>javascript</category>
      <category>beginners</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>What is one of the most common mistakes beginner developers make</title>
      <dc:creator>Sandor | tutorialhell.dev</dc:creator>
      <pubDate>Mon, 18 Jan 2021 08:40:08 +0000</pubDate>
      <link>https://dev.to/sandorturanszky/what-is-one-of-the-most-common-mistakes-beginner-developers-make-20h4</link>
      <guid>https://dev.to/sandorturanszky/what-is-one-of-the-most-common-mistakes-beginner-developers-make-20h4</guid>
      <description>&lt;p&gt;It may seem that when you are a beginner, you'll do simple things only. No need to learn data structures and algorithms. No need to understand Big O notation, complexity and stuff like that. &lt;/p&gt;

&lt;p&gt;This couldn't be further away from the truth!&lt;/p&gt;

&lt;p&gt;In 2008, when I just started learning to program, I spent a lot of time reading books on PHP and MySQL. Months later, when I felt confident, I took my first freelance project. It was a real estate website. A simple one. I used a custom-made ORM and everything worked just fine!&lt;/p&gt;

&lt;p&gt;When I released it, the search feature quickly became sluggish and made the website unusable. &lt;/p&gt;

&lt;p&gt;I was wondering what the heck had happened. I figured out that database queries became very slow when there were over 200 real estate objects added to it. &lt;/p&gt;

&lt;p&gt;This is it. What worked fine during testing did not work in real life.&lt;/p&gt;

&lt;p&gt;I was a self-taught developer. I did not know how to measure if my project scaled well. I didn't even know that I had to do it.&lt;/p&gt;

&lt;p&gt;I thought algorithms mattered only for launching a spaceship.&lt;/p&gt;

&lt;p&gt;If I had some basic understanding of algorithms, I would have known that the more the input, the longer it takes. &lt;/p&gt;

&lt;p&gt;I am not saying I would have come up with a robust solution as a junior, but I would have looked for a solution because I knew there would be a problem. &lt;/p&gt;

&lt;p&gt;Please, don't make the same mistake!&lt;/p&gt;

&lt;p&gt;Of course, data structures and algorithms are much more than that and they apply differently depending on what you work on. &lt;/p&gt;

&lt;p&gt;But a basic understanding of data structures and algorithms is a must for every software developer. &lt;/p&gt;




&lt;p&gt;I am writing about my experience of what I wish I knew when I was a beginner and I review programming courses to find those that are worth learning. &lt;/p&gt;

&lt;p&gt;If you are a junior or a beginner and you want to know what you need to learn, I can help!&lt;/p&gt;

&lt;p&gt;I am sharing my knowledge in a newsletter here &lt;a href="https://ns.comparecourses.dev"&gt;https://ns.comparecourses.dev&lt;/a&gt;&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>programming</category>
      <category>javascript</category>
      <category>junior</category>
    </item>
    <item>
      <title>Please, don't make the same mistake</title>
      <dc:creator>Sandor | tutorialhell.dev</dc:creator>
      <pubDate>Thu, 14 Jan 2021 13:47:22 +0000</pubDate>
      <link>https://dev.to/sandorturanszky/please-don-t-make-the-same-mistake-3f1b</link>
      <guid>https://dev.to/sandorturanszky/please-don-t-make-the-same-mistake-3f1b</guid>
      <description>&lt;p&gt;It may seem that when you are a beginner, you'll do simple things only. No need to learn data structures and algorithms. No need to understand Big O notation, complexity and stuff like that. &lt;/p&gt;

&lt;p&gt;This couldn't be further away from the truth!&lt;/p&gt;

&lt;p&gt;In 2008, when I just started learning to program, I spent a lot of time reading books on PHP and MySQL. Months later, when I felt confident, I took my first freelance project. It was a real estate website. A simple one. I used a custom-made ORM and everything worked just fine!&lt;/p&gt;

&lt;p&gt;When I released it, the search feature quickly became sluggish and made the website unusable. &lt;/p&gt;

&lt;p&gt;I was wondering what the heck had happened. I figured out that database queries became very slow when there were over 200 real estate objects added to it. &lt;/p&gt;

&lt;p&gt;This is it. What worked fine during testing did not work in real life.&lt;/p&gt;

&lt;p&gt;I was a self-taught developer. I did not know how to measure if my project scaled well. I didn't even know that I had to do it.&lt;/p&gt;

&lt;p&gt;I thought algorithms mattered only for launching a spaceship.&lt;/p&gt;

&lt;p&gt;If I had some basic understanding of algorithms, I would have known that the more the input, the longer it takes. &lt;/p&gt;

&lt;p&gt;I am not saying I would have come up with a robust solution as a junior, but I would have looked for a solution because I knew there would be a problem. &lt;/p&gt;

&lt;p&gt;Please, don't make the same mistake!&lt;/p&gt;

&lt;p&gt;Of course, data structures and algorithms are much more than that and they apply differently depending on what you work on. &lt;/p&gt;

&lt;p&gt;But a basic understanding of data structures and algorithms is a must for every software developer. &lt;/p&gt;




&lt;p&gt;I am writing about my experience of what I wish I knew when I was a beginner and I review programming courses to find those that are worth learning. &lt;/p&gt;

&lt;p&gt;If you are a junior or a beginner and you want to know what you need to learn, you might find my newsletter useful &lt;a href="https://ns.comparecourses.dev"&gt;https://ns.comparecourses.dev&lt;/a&gt;&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>programming</category>
      <category>javascript</category>
      <category>junior</category>
    </item>
    <item>
      <title>How to track change to any content with FaunaDB's Temporality feature</title>
      <dc:creator>Sandor | tutorialhell.dev</dc:creator>
      <pubDate>Wed, 19 Aug 2020 18:36:48 +0000</pubDate>
      <link>https://dev.to/sandorturanszky/how-to-track-change-to-any-content-with-faunadb-s-temporality-feature-2ng8</link>
      <guid>https://dev.to/sandorturanszky/how-to-track-change-to-any-content-with-faunadb-s-temporality-feature-2ng8</guid>
      <description>&lt;p&gt;This is the third article from the series of articles on how I build an MVP with Gatsby and &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_MVPwithJamstack_STuranszky"&gt;FaunaDB&lt;/a&gt;. This time we will look at yet another FaunaDB’s built-in feature called &lt;a href="https://docs.fauna.com/fauna/current/tutorials/temporality.html"&gt;Temporality&lt;/a&gt;. In simple terms, it helps you track how exactly data has changed over time.&lt;/p&gt;

&lt;p&gt;The simplest use-cases are content moderation or versioning. In our MVP, authors can create and update courses and we want to see what changed and approve course content before it goes live. Luckily, we don't need to build our own implementation because &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_MVPwithJamstack_STuranszky"&gt;FaunaDB&lt;/a&gt; offers this feature on a database level out of the box.&lt;/p&gt;

&lt;p&gt;This article is based on the previous two articles. If you missed them, it’s best to start with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the &lt;a href="https://dev.to/sandorturanszky/how-to-launch-an-mvp-with-jamstack-faunadb-and-graphql-with-zero-running-costs-lfm"&gt;first article&lt;/a&gt; where we connect our starter project to &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_MVPwithJamstack_STuranszky"&gt;FaunaDB&lt;/a&gt; and fetch data at build time&lt;/li&gt;
&lt;li&gt;and the &lt;a href="https://dev.to/sandorturanszky/how-to-add-user-authentication-to-your-mvp-using-faunadb-g67"&gt;second article&lt;/a&gt; where we implement user authentication with &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_MVPwithJamstack_STuranszky"&gt;FaunaDB's&lt;/a&gt; built-in Login function, ABAC and also explore FQL together with user-defined functions (UDF).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Schema update
&lt;/h3&gt;

&lt;p&gt;Our schema has changed again. Here it is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Course&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="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;visible&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;bookmarks&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="n"&gt;Bookmark&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="n"&gt;relation&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="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Bookmark&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="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;private&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;course&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Course&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="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&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="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;courses&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="n"&gt;Course&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="n"&gt;relation&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;bookmarks&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="n"&gt;Bookmark&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="n"&gt;relation&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="k"&gt;input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CreateUserInput&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="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Role&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="k"&gt;input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LoginUserInput&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="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&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="k"&gt;input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LogoutInput&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="n"&gt;allTokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Boolean&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="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AuthPayload&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="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&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="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CourseUpdates&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;embedded&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="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;visible&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Boolean&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="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HistoryUpdate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;embedded&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="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CourseUpdates&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="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HistoryPage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;embedded&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="n"&gt;data&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="n"&gt;HistoryUpdate&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="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Query&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="n"&gt;allCourses&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="n"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;!]&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;allBookmarks&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="n"&gt;Bookmark&lt;/span&gt;&lt;span class="p"&gt;!]&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;allUsers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Role&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="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;!]&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;allCoursesInReview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;visible&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Boolean&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="kc"&gt;false&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="n"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;!]&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;courseUpdateHistory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HistoryPage&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;resolver&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="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;course_update_history&lt;/span&gt;&lt;span class="err"&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;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Mutation&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="n"&gt;createUser&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="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CreateUserInput&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&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="n"&gt;resolver&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="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;create_user&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;loginUser&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="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LoginUserInput&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AuthPayload&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="n"&gt;resolver&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="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;login_user&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;logoutUser&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="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LogoutInput&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Boolean&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="n"&gt;resolver&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="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;logout_user&lt;/span&gt;&lt;span class="err"&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;span class="k"&gt;enum&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Role&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="n"&gt;AUTHOR&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;DEVELOPER&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;MANAGER&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;p&gt;We added:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the &lt;code&gt;visible&lt;/code&gt; property to &lt;code&gt;Course&lt;/code&gt; type to make sure that only visible (approved) courses will be listed;&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;allCoursesInReview&lt;/code&gt; query to list all courses that require approval based on the value of the &lt;code&gt;visible&lt;/code&gt; property;&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;courseUpdateHistory&lt;/code&gt; query to list all changes made to a particular course that we can look up by its &lt;code&gt;_id&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;MANAGER&lt;/code&gt; role&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We also use the &lt;code&gt;@embedded&lt;/code&gt; directive on the return type for the &lt;code&gt;courseUpdateHistory&lt;/code&gt; query so that &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_MVPwithJamstack_STuranszky"&gt;FaunaDB&lt;/a&gt; does not create collections for those types.&lt;/p&gt;

&lt;p&gt;Copy the above schema into a file named &lt;code&gt;schema.gql&lt;/code&gt; and apply it with &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_MVPwithJamstack_STuranszky"&gt;FaunaDB&lt;/a&gt; as shown on the screenshot below:&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F8cbaagng6x055izje4mp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F8cbaagng6x055izje4mp.png" alt="Alt Text" width="800" height="324"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since we added the &lt;code&gt;visible&lt;/code&gt; property as non-nullable (&lt;code&gt;visible: Boolean!&lt;/code&gt; - the exclamation mark means that the field is non-nullable, meaning that there must be always a value) and none of our courses has it, we need to set it for all courses to avoid getting the &lt;code&gt;"Cannot return null for non-nullable type"&lt;/code&gt; GraphQL error. We need to make a small change to our existing data to accommodate the schema update which we can do in pure FQL. Copy and paste the following code into the Shell and run 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="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nc"&gt;Paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;Match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;allCourses&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="nc"&gt;Lambda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;X&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="nc"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ref&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="nc"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;X&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;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;visible&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2acee5frwwdyen08fib9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2acee5frwwdyen08fib9.png" alt="Alt Text" width="800" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Author role
&lt;/h3&gt;

&lt;p&gt;Before we see how FaunaB’s temporal feature works, we need a way to create and update courses as authors would. While we could do it via the GraphQL playground, it’s always better to see a real-life example.&lt;/p&gt;

&lt;p&gt;Authors can log in, but we do not have an Author role yet. Let’s create it and define what privileges we will give to authors.&lt;/p&gt;

&lt;p&gt;Here is our FQL for the AUTHOR role:&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="nc"&gt;CreateRole&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;Author&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;membership&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Collection&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&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;predicate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nc"&gt;Lambda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;userRef&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nc"&gt;Equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&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;role&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nc"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;userRef&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;AUTHOR&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="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;privileges&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Collection&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&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bookmark&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nc"&gt;Lambda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bookmarkRef&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nc"&gt;Let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                  &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;bookmark&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bookmarkRef&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                    &lt;span class="na"&gt;private&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&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;private&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bookmark&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="nc"&gt;Equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;private&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="kc"&gt;false&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="p"&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;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Course&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;write&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nc"&gt;Lambda&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;oldData&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;newData&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="nc"&gt;And&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nc"&gt;Equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Identity&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&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;author&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;oldData&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))),&lt;/span&gt;
              &lt;span class="nc"&gt;Equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&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;visible&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;newData&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="nc"&gt;Equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&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;author&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;oldData&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&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;author&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;newData&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="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nc"&gt;Lambda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nc"&gt;And&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nc"&gt;Equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Identity&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&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;author&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))),&lt;/span&gt;
              &lt;span class="nc"&gt;Equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&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;visible&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="kc"&gt;false&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="p"&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;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;allUsers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;logout_user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;call&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;allCourses&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;course_author_by_user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bookmark_user_by_user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We allow the AUTHOR role to create and edit own courses only, see all courses and public bookmarks and log out. &lt;/p&gt;

&lt;p&gt;What’s interesting to note is that the &lt;code&gt;visible&lt;/code&gt; property in the &lt;code&gt;Course&lt;/code&gt; type is &lt;code&gt;false&lt;/code&gt; by default for new and updated courses which means that the course is “In review”. The author can’t override this value. We are checking for it in the predicate function in these lines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;for “Create” action:
&lt;code&gt;Equals(Select(["data", "visible"], Var("data")), false)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;and for the “Write” action:
&lt;code&gt;Equals(Select(["data", "visible"], Var("newData")), false)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can safely set the &lt;code&gt;visible&lt;/code&gt; property to &lt;code&gt;false&lt;/code&gt; on the client-side because we know that even if the value is changed intentionally, it will not pass the ABAC. This means that we do not need to create a custom resolver or a backend function to set this value manually. &lt;/p&gt;

&lt;p&gt;We will test it in a minute.&lt;/p&gt;

&lt;p&gt;Copy and paste the above FQL into the Shell and run it.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fk81z72ha1s09gh2k52g6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fk81z72ha1s09gh2k52g6.png" alt="Alt Text" width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we need to clone the repository with the latest changes for this article.&lt;br&gt;
Note that you will need the &lt;code&gt;.env.development&lt;/code&gt; and &lt;code&gt;.env.production&lt;/code&gt; files containing the bootstrap key and other variables that we added in the &lt;a href="https://dev.to/sandorturanszky/how-to-add-user-authentication-to-your-mvp-using-faunadb-g67"&gt;previous article&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Follow these steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;git clone --single-branch --branch article-3/temporality git@github.com:sandorTuranszky/Gatsby-FaunaDB-GraphQL.git gatsby-fauna-db&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cd gatsby-fauna-db&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;copy the &lt;code&gt;.env.development&lt;/code&gt; and &lt;code&gt;.env.production&lt;/code&gt; files&lt;/li&gt;
&lt;li&gt;&lt;code&gt;npm install&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gatsby develop&lt;/code&gt; to start the project in development mode&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You should see the MVP up and running.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you see an error, make sure that the &lt;code&gt;token&lt;/code&gt; cookie and the &lt;code&gt;user_data&lt;/code&gt; in local storage have been removed or remove them manually.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Log in as an author using the following credentials:&lt;br&gt;
Email: &lt;code&gt;johns.austin@email.com&lt;/code&gt;&lt;br&gt;
Password: &lt;code&gt;password&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And you will be redirected to the following page &lt;code&gt;/app/courses&lt;/code&gt;:&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ft5k5doqygq60loyzc0z6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ft5k5doqygq60loyzc0z6.png" alt="Alt Text" width="800" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we can create new courses or update an existing one. &lt;/p&gt;

&lt;p&gt;Let’s create a new course:&lt;br&gt;
Title:  “Node.js Masterclass”&lt;br&gt;
Description: “We’ll cover some of the topics including integrating Node.js with Express and asynchronous code”&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F8e22e93h22890ikz7oco.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F8e22e93h22890ikz7oco.png" alt="Alt Text" width="800" height="231"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can see that the newly created course is marked “In review”. As it was mentioned before, all new or updated courses have a default &lt;code&gt;false&lt;/code&gt; value for the &lt;code&gt;visible&lt;/code&gt; property. This means that the course needs to be reviewed. &lt;/p&gt;

&lt;p&gt;The default &lt;code&gt;false&lt;/code&gt; value is set on the client and is controlled by ABAC. You can test it by changing the default value to &lt;code&gt;true&lt;/code&gt; in the mutation in &lt;code&gt;/src/components/updateCourse.js file&lt;/code&gt;:&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fb0mcq59gekpuxkt1xa0p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fb0mcq59gekpuxkt1xa0p.png" alt="Alt Text" width="800" height="339"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and try to update the “React for beginners” course. You will get an error:&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fos0ylo42qvlv29x4vo8j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fos0ylo42qvlv29x4vo8j.png" alt="Alt Text" width="800" height="269"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, revert the default value of the &lt;code&gt;visible&lt;/code&gt; prop to &lt;code&gt;false&lt;/code&gt; and click “Update”&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fw3jfes3rbltgebweqz2b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fw3jfes3rbltgebweqz2b.png" alt="Alt Text" width="800" height="242"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see that the updated course is now marked as (In review) too.&lt;/p&gt;

&lt;p&gt;Great, we have created and updated courses using the author account. This is what we needed to be able to test out the &lt;a href="https://docs.fauna.com/fauna/current/tutorials/temporality.html"&gt;temporal&lt;/a&gt; feature.&lt;/p&gt;
&lt;h3&gt;
  
  
  Temporality
&lt;/h3&gt;

&lt;p&gt;FaunaDB’s &lt;a href="https://docs.fauna.com/fauna/current/tutorials/temporality.html"&gt;Temporality&lt;/a&gt; has two features: Snapshots and Events.&lt;/p&gt;
&lt;h4&gt;
  
  
  Snapshots
&lt;/h4&gt;

&lt;p&gt;Snapshots allow us to see the state of our data at a particular point in time. We have just added two new courses and updated one course above. If you run the following FQL in the Shell, you will see all the 7 courses in their latest state including the newly created “Node.js Masterclass” course:&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="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nc"&gt;Paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;Match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;allCourses&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="nc"&gt;Lambda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;X&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="nc"&gt;Let&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;X&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))),&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;X&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="na"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&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="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Faii48wbqr286lq79ipq0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Faii48wbqr286lq79ipq0.png" alt="Alt Text" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How can we learn which articles we just created and updated? We will use the Snapshots feature to travel back in time to see our data before the updates that we made.&lt;/p&gt;

&lt;p&gt;To use the &lt;a href="https://docs.fauna.com/fauna/current/api/fql/functions/at"&gt;At&lt;/a&gt; function we need a timestamp. We can get it from the above query which returns a &lt;code&gt;ts&lt;/code&gt; property for each course (see the first arrow on the screenshot above). &lt;/p&gt;

&lt;p&gt;We know that we did not update the course with the title “NodeJS Tips &amp;amp; Tricks” (we added it when following the previous article) hence we can assume that its timestamp represents a state before the changes that we just made. Let’s check. &lt;/p&gt;

&lt;p&gt;Run the following FQL in the Shell.&lt;/p&gt;

&lt;p&gt;Note that you need to copy the timestamp from your list in the Shell above (&lt;code&gt;ts&lt;/code&gt; property is above the &lt;code&gt;data&lt;/code&gt; property for each course)&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="nc"&gt;At&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="mi"&gt;1558455784100000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;Paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nc"&gt;Match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;allCourses&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="nc"&gt;Lambda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;X&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;X&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you will see a list of courses without the newly added  “Node.js Masterclass” course.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4wz5bq9m8w3dyyhff9f1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4wz5bq9m8w3dyyhff9f1.png" alt="Alt Text" width="800" height="337"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We remember that we created the “Node.js Masterclass” course first and then updated the “React for beginners” course. This means that with the timestamp of the  “Node.js Masterclass” course, we can get the state for the “React for beginners” course, where the &lt;code&gt;visible&lt;/code&gt; property is &lt;code&gt;true&lt;/code&gt; and without the &lt;code&gt;description&lt;/code&gt; property. &lt;/p&gt;

&lt;p&gt;Copy the timestamp for the “Node.js Masterclass” course from the query result that we made to list all courses and run the following FQL in the Shell:&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="nc"&gt;At&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="mi"&gt;1594566013530000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;Paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nc"&gt;Match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;allCourses&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="nc"&gt;Lambda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;X&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;X&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will see that the &lt;code&gt;visible&lt;/code&gt; property is &lt;code&gt;true&lt;/code&gt; and the &lt;code&gt;description&lt;/code&gt; property is nowhere to be seen for the “React for beginners” course. The “Node.js Masterclass” course is also listed.&lt;/p&gt;

&lt;p&gt;Cool, right? &lt;/p&gt;

&lt;h4&gt;
  
  
  Events
&lt;/h4&gt;

&lt;p&gt;FaunaDB creates a new copy of the document containing all the changes we’ve made. The original document is never changed. It means that FaunaDB has at least two copies of the “React for beginners” course, one where the &lt;code&gt;visible&lt;/code&gt; property is &lt;code&gt;true&lt;/code&gt; and one where it’s &lt;code&gt;false&lt;/code&gt; and the &lt;code&gt;description&lt;/code&gt; property is available.&lt;/p&gt;

&lt;p&gt;Since we ran a script to update the &lt;code&gt;visible&lt;/code&gt; property for all courses, we will have more copies. To see them for the “React for beginners” course, run the following FQL:&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="nc"&gt;Paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nc"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ref&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nc"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;course_by_title&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;React for beginners&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above query will list all copies created as a result of changes that were made to the above-mentioned course. I have four copies. The first change has an action “create” when the document was created and the other three have action “update”. &lt;/p&gt;

&lt;p&gt;You should see the first item in the list without the &lt;code&gt;visible&lt;/code&gt; property. Then, one copy with the &lt;code&gt;visible&lt;/code&gt; property set to &lt;code&gt;true&lt;/code&gt; and the last copy with the &lt;code&gt;visible&lt;/code&gt; property set to &lt;code&gt;false&lt;/code&gt; and the &lt;code&gt;description&lt;/code&gt; property as well. This is exactly how we changed it. &lt;/p&gt;

&lt;p&gt;I have one more copy which does not represent any meaningful change and the timing of the change tells me that the copy was created when we tried to manually set the default value for the &lt;code&gt;visible&lt;/code&gt; property to &lt;code&gt;true&lt;/code&gt; and got an error response. &lt;/p&gt;

&lt;p&gt;Note that here we looked up the course by the name for simplicity. In our UDF we will look up courses by their &lt;code&gt;_id&lt;/code&gt; field.&lt;/p&gt;

&lt;p&gt;Now, when we’ve seen how the temporal features work, let’s use it for our MVP.&lt;/p&gt;

&lt;h3&gt;
  
  
  UDF for listing changes for a particular course
&lt;/h3&gt;

&lt;p&gt;We need a custom resolver to return all changes that have been made to any given course. Copy and paste the following FQL and run it in the Shell:&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="nc"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;course_update_history&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;body&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nc"&gt;Lambda&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="nc"&gt;Let&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nc"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ref&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nc"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Course&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&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="p"&gt;},&lt;/span&gt;
          &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;page&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="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;Now the &lt;code&gt;courseUpdateHistory&lt;/code&gt; mutation has a revolver. To test it out in our MVP, we need to create a MANAGER role and a few manager users.&lt;/p&gt;

&lt;p&gt;The MANAGER role will have the same rights as the GUEST role with a few extra privileges, namely to read the history for courses, update courses and call the &lt;code&gt;logout_user&lt;/code&gt; UDF. Run the following FQL in the Shell:&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="nc"&gt;CreateRole&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;Manager&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;privileges&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Course&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;write&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;history_read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;allCourses&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;course_update_history&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;call&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;logout_user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;call&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;allCoursesInReview&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bookmark&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nc"&gt;Lambda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bookmarkRef&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nc"&gt;Let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;bookmark&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bookmarkRef&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                &lt;span class="na"&gt;private&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&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;private&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bookmark&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="nc"&gt;Equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;private&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="kc"&gt;false&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="p"&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;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;allBookmarks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bookmark_user_by_user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Collection&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&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;allUsers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="na"&gt;membership&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Collection&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&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;predicate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nc"&gt;Lambda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;userRef&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nc"&gt;Equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&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;role&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nc"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;userRef&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;MANAGER&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="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;Run the following FQL in the Shell to create a couple of manager users:&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="nc"&gt;Map&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="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;Manager 1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;manager1@email.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MANAGER&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;Manager 2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;manager2@email.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MANAGER&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="nc"&gt;Lambda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nc"&gt;Let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;userRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ref&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nc"&gt;Call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;create_user&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
              &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
              &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
              &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;role&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&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="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Success&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we are ready to test how a course review process might look like in real life. This is simply an example showing what data is available.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that mixing manager (admin) functionality with the client-facing app is a bad idea and we do it for simplicity reasons only. It’s much better to have a dedicated app for administration purposes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Head over to our MVP and log in using the following credentials:&lt;br&gt;
Email: &lt;code&gt;manager1@email.com&lt;/code&gt;&lt;br&gt;
Password: &lt;code&gt;password&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You will be redirected to the following page &lt;code&gt;/app/courses/review&lt;/code&gt; where you will see courses for review.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmm3dmd6xuxikv9g685a8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmm3dmd6xuxikv9g685a8.png" alt="Alt Text" width="800" height="198"&gt;&lt;/a&gt;&lt;br&gt;
Click on the ”See details” link for the first course.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fvbtituj62bkpqoxmy5sm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fvbtituj62bkpqoxmy5sm.png" alt="Alt Text" width="800" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the first box, you can see the current state of the course. &lt;br&gt;
In the second box - how the course was updated. First, it was created, then all other changes are listed (you may see a different list of changes depending on what you changed and in what order).&lt;/p&gt;

&lt;p&gt;If you like the changes, you can click ”Approve”&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;This is how you can leverage the &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_MVPwithJamstack_STuranszky"&gt;FaunaDB's&lt;/a&gt; &lt;a href="https://docs.fauna.com/fauna/current/tutorials/temporality.html"&gt;Temporality&lt;/a&gt; feature to track changes to any document.&lt;/p&gt;

&lt;p&gt;We could create a fancy UI to allow an interactive time travel but this sounds like another project!&lt;/p&gt;

&lt;p&gt;With this article finished, we have addressed all the challenges I listed in the &lt;a href="https://dev.to/sandorturanszky/how-to-launch-an-mvp-with-jamstack-faunadb-and-graphql-with-zero-running-costs-lfm"&gt;first&lt;/a&gt; one. We have proved that using &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_MVPwithJamstack_STuranszky"&gt;FaunaDB&lt;/a&gt; built-in features combined with Gatsby removes the need to reinvent the wheel and allows us to concentrate on the idea.&lt;/p&gt;

&lt;p&gt;This is the approach I am taking with my project. I am using &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_MVPwithJamstack_STuranszky"&gt;FaunaDB&lt;/a&gt; and will share more insights once I release it. Stay tuned.&lt;/p&gt;

</description>
      <category>jamstack</category>
      <category>fauna</category>
      <category>mvp</category>
    </item>
    <item>
      <title>How to add user authentication to your MVP using FaunaDB</title>
      <dc:creator>Sandor | tutorialhell.dev</dc:creator>
      <pubDate>Tue, 04 Aug 2020 13:27:20 +0000</pubDate>
      <link>https://dev.to/sandorturanszky/how-to-add-user-authentication-to-your-mvp-using-faunadb-g67</link>
      <guid>https://dev.to/sandorturanszky/how-to-add-user-authentication-to-your-mvp-using-faunadb-g67</guid>
      <description>&lt;p&gt;It is the second article in the series of articles covering &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_MVPwithJamstack_STuranszky"&gt;FaunaDB&lt;/a&gt; with Jamstack. The first article is about &lt;a href="https://dev.to/sandorturanszky/how-to-launch-an-mvp-with-jamstack-faunadb-and-graphql-with-zero-running-costs-lfm"&gt;How to launch an MVP with Jamstack, FaunaDB and GraphQL with zero operational costs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As the title suggests, we will be talking about an important feature that most MVPs need - user authentication. Building your own reliable and secure solution is time-consuming. Using a third-party solution with a free tier is often expensive to scale.&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://dev.to/sandorturanszky/how-to-launch-an-mvp-with-jamstack-faunadb-and-graphql-with-zero-running-costs-lfm"&gt;first article&lt;/a&gt; where I share how I build my MVP with Jamstack and &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_MVPwithJamstack_STuranszky"&gt;FaunaDB&lt;/a&gt;, I mentioned that &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_MVPwithJamstack_STuranszky"&gt;FaunaDB&lt;/a&gt; offers secure user authentication and attribute-based access control (ABAC) out of the box.&lt;/p&gt;

&lt;p&gt;What makes FaunaDB’s ABAC even more powerful is that changes to access rights are reflected immediately because ABAC is evaluated for every query. This means that access can be granted or revoked without requiring a user to re-login. &lt;/p&gt;

&lt;p&gt;In this article, we will see how easy it is to add user authentication features to a website using &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_MVPwithJamstack_STuranszky"&gt;FaunaDB&lt;/a&gt;. We will also allow authenticated users to bookmark courses. All bookmarks are public by default so anyone can see them. To see FaunaDB’s ABAC in action, we will add a feature to make bookmarks private. Here I'd like to stress that nothing is public in &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_MVPwithJamstack_STuranszky"&gt;FaunaDB&lt;/a&gt;. All data that unauthenticated users will see still needs to be made accessible using either a Role or a Key system. &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_MVPwithJamstack_STuranszky"&gt;FaunaDB&lt;/a&gt; is secure by default!&lt;/p&gt;

&lt;p&gt;I assume that you have followed the first article and have the starter project with data coming from &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_MVPwithJamstack_STuranszky"&gt;FaunaDB&lt;/a&gt;. If not, the best is to start &lt;a href="https://dev.to/sandorturanszky/how-to-launch-an-mvp-with-jamstack-faunadb-and-graphql-with-zero-running-costs-lfm"&gt;here&lt;/a&gt;. Alternatively, checkout the branch with the finished implementation of what was covered in the first article running the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &lt;span class="nt"&gt;--single-branch&lt;/span&gt; &lt;span class="nt"&gt;--branch&lt;/span&gt; article-1/source-data-from-FaunaDB git@github.com:sandorTuranszky/Gatsby-FaunaDB-GraphQL.git gatsby-fauna-db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_MVPwithJamstack_STuranszky"&gt;FaunaDB&lt;/a&gt; provides &lt;a href="https://docs.fauna.com/fauna/current/api/fql/functions/login"&gt;Login&lt;/a&gt; and &lt;a href="https://docs.fauna.com/fauna/current/api/fql/functions/logout"&gt;Logout&lt;/a&gt; built-in functions that can be used to create and invalidate user authentication tokens.&lt;/p&gt;

&lt;p&gt;For example, we can pass an email and password to the &lt;a href="https://docs.fauna.com/fauna/current/api/fql/functions/login"&gt;Login&lt;/a&gt; function to get an authentication token or an error, if credentials are invalid.&lt;/p&gt;

&lt;p&gt;In the first article we didn't have to create resolvers, as &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_MVPwithJamstack_STuranszky"&gt;FaunaDB&lt;/a&gt; did it for us and we were fine with the logic. However, the login process might vary depending on the requirements, For example, we might want to use a username instead of an email. This is why &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_MVPwithJamstack_STuranszky"&gt;FaunaDB&lt;/a&gt; gives us the flexibility to create our own resolvers.&lt;/p&gt;

&lt;p&gt;Let’s take a look at our current schema:&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fhbrsslnlq2bjscdfxly2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fhbrsslnlq2bjscdfxly2.png" alt="Alt Text" width="800" height="390"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It allows us to create authors with the &lt;code&gt;name&lt;/code&gt; field only (see 3 - &lt;code&gt;AuthorInput&lt;/code&gt;). To log in, an author will need to have an email and password. Not a big deal that we did not do it right away, because modifying schema with &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_MVPwithJamstack_STuranszky"&gt;FaunaDB&lt;/a&gt; is very easy. All we need is to upload an updated schema. &lt;/p&gt;

&lt;p&gt;Here it is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Course&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="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;bookmarks&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="n"&gt;Bookmark&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="n"&gt;relation&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="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Bookmark&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="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;private&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;course&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Course&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="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&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="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;courses&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="n"&gt;Course&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="n"&gt;relation&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;bookmarks&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="n"&gt;Bookmark&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="n"&gt;relation&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="k"&gt;input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CreateUserInput&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="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Role&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="k"&gt;input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LoginUserInput&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="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&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="k"&gt;input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LogoutInput&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="n"&gt;allTokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Boolean&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="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AuthPayload&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="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&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="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Query&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="n"&gt;allCourses&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="n"&gt;Course&lt;/span&gt;&lt;span class="p"&gt;!]&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;allBookmarks&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="n"&gt;Bookmark&lt;/span&gt;&lt;span class="p"&gt;!]&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;allUsers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Role&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="n"&gt;User&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="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Mutation&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="n"&gt;createUser&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="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CreateUserInput&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&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="n"&gt;resolver&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="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;create_user&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;loginUser&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="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LoginUserInput&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AuthPayload&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="n"&gt;resolver&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="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;login_user&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;logoutUser&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="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LogoutInput&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Boolean&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="n"&gt;resolver&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="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;logout_user&lt;/span&gt;&lt;span class="err"&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;span class="k"&gt;enum&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Role&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="n"&gt;AUTHOR&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="n"&gt;DEVELOPER&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;p&gt;Notice that the &lt;code&gt;Author&lt;/code&gt; type was renamed to &lt;code&gt;User&lt;/code&gt;. The project requirements have changed and we want to have different types of users depending on their roles. This is why we added the &lt;code&gt;role&lt;/code&gt; field and the &lt;code&gt;Role enum&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We also added the &lt;code&gt;bookmarks&lt;/code&gt; relation for the “Bookmarks” feature and the &lt;code&gt;email&lt;/code&gt; and &lt;code&gt;password&lt;/code&gt; fields to the &lt;code&gt;User&lt;/code&gt; type for login purposes.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;allAuthors&lt;/code&gt; query was renamed to a generic &lt;code&gt;allUsers&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There is also a relation added between &lt;code&gt;Courses&lt;/code&gt; and &lt;code&gt;Bookmarks&lt;/code&gt; types that will allow us to highlight which courses have been bookmarked by an authenticated user.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;createUser&lt;/code&gt;, &lt;code&gt;loginUser&lt;/code&gt; and the &lt;code&gt;logoutUser&lt;/code&gt; mutations are self-explanatory. Notice the &lt;a href="https://docs.fauna.com/fauna/current/api/graphql/directives/d_resolver"&gt;@resolver&lt;/a&gt; directive. It allows us to define our custom logic for resolvers. The provided &lt;code&gt;create_user&lt;/code&gt;, &lt;code&gt;login_user&lt;/code&gt; and &lt;code&gt;logout_user&lt;/code&gt; names are how we will name our custom resolver functions. In FaunaDB’s documentation, they are referred to as &lt;a href="https://docs.fauna.com/fauna/current/api/graphql/functions"&gt;User-defined functions (UDF)&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Schema update
&lt;/h3&gt;

&lt;p&gt;We have two options to update the existing schema:&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F8yas6l84khvguqpwowz2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F8yas6l84khvguqpwowz2.png" alt="Alt Text" width="800" height="308"&gt;&lt;/a&gt;&lt;br&gt;
Option 1: &lt;strong&gt;UPDATE SCHEMA&lt;/strong&gt; creates any missing collections, indexes, and functions and overrides existing ones. All other elements of the existing schema remain the same.&lt;/p&gt;

&lt;p&gt;Option 2: &lt;strong&gt;OVERRIDE SCHEMA&lt;/strong&gt; removes all database elements, such as collections, indexes and functions and creates new once. This may result in loss of data this is why it’s not suitable for production apps. The GraphQL schema evolution is a much better and safer approach. &lt;/p&gt;

&lt;p&gt;You can read about updating schema in more details &lt;a href="https://fauna.com/blog/getting-started-with-graphql-part-4-updating-your-schema"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since we are at an early stage in our project and changes to our schema are quite significant, we will go with option 2 and override our existing schema. This will require us to repopulate the database with test data and it’s acceptable for us. &lt;/p&gt;

&lt;p&gt;Copy the above schema and paste inside a &lt;code&gt;schema-courses.gql&lt;/code&gt; file (you can name it whatever you want)&lt;/p&gt;

&lt;p&gt;Click the &lt;strong&gt;OVERRIDE SCHEMA&lt;/strong&gt; link, choose the file, click &lt;strong&gt;Open&lt;/strong&gt; and wait 1 min. As explained &lt;a href="https://docs.fauna.com/fauna/current/api/graphql/endpoints#modes"&gt;here&lt;/a&gt;, there is a 60-second pause to allow all cluster nodes to process the schema changes. &lt;/p&gt;

&lt;p&gt;Note that fields in the database collections that are no longer declared in the schema are not accessible via GraphQL queries. It might make sense to clean them up before overriding the schema. You can do it with &lt;code&gt;delete*&lt;/code&gt; mutations in the GraphQL playground.&lt;/p&gt;

&lt;p&gt;Before the override, we had two collections populated with some data. &lt;br&gt;
The &lt;code&gt;Author&lt;/code&gt; collection:&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fhz6gl0vd1cfm88b1w65v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fhz6gl0vd1cfm88b1w65v.png" alt="Alt Text" width="800" height="248"&gt;&lt;/a&gt;&lt;br&gt;
And the &lt;code&gt;Course&lt;/code&gt; collection:&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0xr005imrkpzs8b7ws39.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0xr005imrkpzs8b7ws39.png" alt="Alt Text" width="800" height="333"&gt;&lt;/a&gt;&lt;br&gt;
After the schema update, we have two newly created &lt;code&gt;Course&lt;/code&gt; and &lt;code&gt;User&lt;/code&gt; collections, that are both empty. &lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fe97p1kwdgcolo6lypzto.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fe97p1kwdgcolo6lypzto.png" alt="Alt Text" width="800" height="260"&gt;&lt;/a&gt;&lt;br&gt;
As expected, everything was removed and we will need to repopulate our collections with test data, but this time users will have emails, passwords and roles.&lt;/p&gt;
&lt;h3&gt;
  
  
  UDF to create a user
&lt;/h3&gt;

&lt;p&gt;Before we can create users, we need to define the &lt;code&gt;create_user&lt;/code&gt; resolver function and use FaunaDB’s built-in authentication feature to ensure passwords are hashed.&lt;/p&gt;

&lt;p&gt;Although the create_user (UDF) has been created by &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_MVPwithJamstack_STuranszky"&gt;FaunaDB&lt;/a&gt; automatically as a "template" UDF based on the &lt;a href="https://docs.fauna.com/fauna/current/api/graphql/directives/d_resolver"&gt;@resolver&lt;/a&gt; directive, we will get an error if we try to run the &lt;code&gt;createUser&lt;/code&gt; mutation now. This is because no logic has been implemented yet.&lt;/p&gt;

&lt;p&gt;We will use &lt;a href="https://docs.fauna.com/fauna/current/api/fql/"&gt;Fauna Query Language (FQL)&lt;/a&gt; to update the &lt;code&gt;createUser&lt;/code&gt; UDF. If you prefer, you can create and modify UDF via the dashboard under the &lt;strong&gt;FUNCTIONS&lt;/strong&gt; menu.&lt;/p&gt;

&lt;p&gt;Here is the FQL code:&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="nc"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;create_user&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;body&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nc"&gt;Lambda&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="nc"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nc"&gt;Collection&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&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;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&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;data&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="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
              &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
              &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;role&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What’s happening in this FQL code.&lt;br&gt;
As mentioned before, the &lt;code&gt;create_user&lt;/code&gt; UDF has already been created by &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_MVPwithJamstack_STuranszky"&gt;FaunaDB&lt;/a&gt;. It looks like 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="nc"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nc"&gt;Lambda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;Abort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Function create_user was not implemented yet. Please access your database and provide an implementation for the create_user function.&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To add custom logic to an existing UDF, we use the &lt;a href="https://docs.fauna.com/fauna/current/api/fql/functions/update"&gt;Update&lt;/a&gt; function. It accepts two arguments: &lt;code&gt;ref&lt;/code&gt; and &lt;code&gt;param_object&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As the first argument, we pass in the reference to our existing &lt;code&gt;create_user&lt;/code&gt; UDF with the help of the &lt;a href="https://docs.fauna.com/fauna/current/api/fql/functions/function"&gt;Function&lt;/a&gt; function.&lt;/p&gt;

&lt;p&gt;As the second argument, we provide an object with a single key &lt;code&gt;body&lt;/code&gt;, that holds a query to be run when the function is executed. This query must be wrapped in a &lt;a href="https://docs.fauna.com/fauna/current/api/fql/functions/query"&gt;Query&lt;/a&gt; function, which takes a &lt;a href="https://docs.fauna.com/fauna/current/api/fql/functions/lambda"&gt;Lambda&lt;/a&gt; function and defers its execution because we want this Lambda function to only run when the mutation is called. &lt;/p&gt;

&lt;p&gt;The Lambda function is used to execute custom code - in our case, the custom code is how we create a new user.&lt;/p&gt;

&lt;p&gt;The UDF accepts an array of arguments (&lt;code&gt;Lambda(["data"],...&lt;/code&gt;), the same arguments as defined in the GraphQL schema. We have an argument &lt;code&gt;data&lt;/code&gt; in &lt;code&gt;createUser(data: CreateUserInput)&lt;/code&gt; mutation with values defined in &lt;code&gt;CreateUserInput&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.fauna.com/fauna/current/api/fql/functions/create"&gt;Create&lt;/a&gt; function is used to create a document in a collection. It takes two arguments: &lt;code&gt;collection&lt;/code&gt; and &lt;code&gt;param_object&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As the first argument, we pass in the references to the &lt;code&gt;User&lt;/code&gt; collection with the help of the &lt;a href="https://docs.fauna.com/fauna/current/api/fql/functions/collection"&gt;Collection&lt;/a&gt; function.&lt;/p&gt;

&lt;p&gt;As the second argument, we provide and object with two keys:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;data&lt;/code&gt; key is an object that holds fields to be stored in the document. In our case, those fields are &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;email&lt;/code&gt; and &lt;code&gt;role&lt;/code&gt;. The &lt;code&gt;password&lt;/code&gt; will NOT be stored here!&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;credentials&lt;/code&gt; key is an object that encrypts values and this is why we use it to store the password. Once created, this value can’t be retrieved anymore. This means that passwords or other sensitive data can’t be leaked accidentally. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;a href="https://docs.fauna.com/fauna/current/api/fql/functions/var"&gt;Var&lt;/a&gt; statement returns a value stored in a named variable - in the &lt;code&gt;data&lt;/code&gt; object in our case and the &lt;a href="https://docs.fauna.com/fauna/current/api/fql/functions/select"&gt;Select&lt;/a&gt; function extracts a single value by the key name. For example, the following &lt;code&gt;Select("name", Var("data"))&lt;/code&gt; is same as &lt;code&gt;Select("name", {name: "Johns Austin", email: "johns.austin@email.com", password: "password", role: AUTHOR})&lt;/code&gt; that will return the value of the &lt;code&gt;name&lt;/code&gt; key:  &lt;code&gt;"Johns Austin"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now that it’s clear how exactly our custom &lt;code&gt;createUser&lt;/code&gt; UDF works, navigate to the Shell (1) page, copy and paste (2) the FQL code and click &lt;strong&gt;Run Query&lt;/strong&gt; (3) as shown on the following screenshot:&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F585jlkghlrk9bya2e54f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F585jlkghlrk9bya2e54f.png" alt="Alt Text" width="800" height="387"&gt;&lt;/a&gt;&lt;br&gt;
Now, go to the &lt;strong&gt;Functions&lt;/strong&gt; page, and you should see the &lt;code&gt;create_user&lt;/code&gt; function there:&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ff26naq0qq8chgo2kp86r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ff26naq0qq8chgo2kp86r.png" alt="Alt Text" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Create test users
&lt;/h3&gt;

&lt;p&gt;Now we can repopulate our database running two simple FQL queries.&lt;br&gt;
The first one is an index that we will need to set up relations between bookmarks and courses. Copy, paste it into the Shell and run it (similarly like we did above)&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="nc"&gt;CreateIndex&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;course_by_title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Course&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;terms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;field&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;data&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;title&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;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;field&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;data&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;title&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next script will add all the test data we need. It will create authors with courses, developers and bookmarks. Copy, paste it into the Shell and run 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="nc"&gt;Map&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="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;Johns Austin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;johns.austin@email.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AUTHOR&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;courses&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="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;React for beginners&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="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;Andrews Winters&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;andrews.winters@email.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AUTHOR&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;courses&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="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Advanced React&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="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;Wiley Cardenas&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wiley.cardenas@email.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AUTHOR&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;courses&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="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;NodeJS Tips &amp;amp; Tricks&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;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Build your first JAMstack site with FaunaDB&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;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;VueJS best practices&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="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;Blake Fletcher&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;blake.fletcher@email.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AUTHOR&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;courses&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="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Mastering Vue 3&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="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;Hamilton Lowe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hamilton.lowe@email.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DEVELOPER&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;bookmarks&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="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;React for beginners&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;private&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;VueJS best practices&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;private&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="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;Melinda Haynes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;melinda.haynes@email.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DEVELOPER&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;bookmarks&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="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Advanced React&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;private&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Build your first JAMstack site with FaunaDB&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;private&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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="nc"&gt;Lambda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;Let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;userRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ref&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nc"&gt;Call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;create_user&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
              &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
              &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
              &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;role&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&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="na"&gt;courses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;courses&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&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="nc"&gt;Lambda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;course&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nc"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Course&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;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;course&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;userRef&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="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;bookmarks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bookmarks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&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="nc"&gt;Lambda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bookmark&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nc"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bookmark&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;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bookmark&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                &lt;span class="na"&gt;private&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;private&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bookmark&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;userRef&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="na"&gt;course&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ref&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;course_by_title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bookmark&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="p"&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;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Success&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Rebuilding the app
&lt;/h3&gt;

&lt;p&gt;If you rebuild our project with &lt;code&gt;gatsby develop&lt;/code&gt;, you will see an error:&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;There was an error in your GraphQL query: Insufficient privileges to perform the action&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because we replaced the &lt;code&gt;Author&lt;/code&gt; collection with the new &lt;code&gt;User&lt;/code&gt; collection, we need to update our &lt;code&gt;Guest&lt;/code&gt; role privileges that we created in the previous article. We have seen how to manage roles and privileges using the UI - it’s very comfy and easy to understand. In this article, I will use FQL for brevity reasons and will explain what changed.&lt;/p&gt;

&lt;p&gt;This is what our &lt;code&gt;Guest&lt;/code&gt; role privileges look like now. &lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgjkjd8kyaa69luwibo3v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgjkjd8kyaa69luwibo3v.png" alt="Alt Text" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Author&lt;/code&gt; collection and &lt;code&gt;allAuthors&lt;/code&gt; index do not exist anymore - we need to fix it. &lt;/p&gt;

&lt;p&gt;Run the following FQL query from the Shell to update the &lt;code&gt;Guest&lt;/code&gt; role privileges.&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="nc"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Guest&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;privileges&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Collection&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&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Course&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bookmark&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;allUsers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;allCourses&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;allBookmarks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bookmark_user_by_user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1xzvup7uulrsssmh5q9b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1xzvup7uulrsssmh5q9b.png" alt="Alt Text" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Head over to the role management section and select the &lt;code&gt;Guest&lt;/code&gt; role. You should see this:&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fd8y74glfyy7z2old2hrm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fd8y74glfyy7z2old2hrm.png" alt="Alt Text" width="800" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Author&lt;/code&gt; collection is gone and the &lt;code&gt;Guest&lt;/code&gt; role can read the new &lt;code&gt;User&lt;/code&gt; collection and &lt;code&gt;allUsers&lt;/code&gt; index.&lt;/p&gt;

&lt;p&gt;Since bookmarks can be public, unauthenticated users can see them. We need to allow the &lt;code&gt;Guest&lt;/code&gt; role to read the &lt;code&gt;Bookmark&lt;/code&gt; collection, the &lt;code&gt;allBookmarks&lt;/code&gt; and the &lt;code&gt;bookmark_user_by_user&lt;/code&gt; indexes.&lt;/p&gt;

&lt;p&gt;If you rebuild our project with &lt;code&gt;gatsby develop&lt;/code&gt; now, you will see that the list of courses and authors looks exactly as it was before we updated the schema. &lt;/p&gt;
&lt;h3&gt;
  
  
  UDF for user login
&lt;/h3&gt;

&lt;p&gt;To manage the Bookmarks feature, we need to allow users to log in. Let’s create the &lt;code&gt;login_user&lt;/code&gt; UDF. Here is the code:&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="nc"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;login_user&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;body&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nc"&gt;Lambda&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;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="nc"&gt;Let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nc"&gt;Match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Index&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_by_email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&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;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&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="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;secret&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;response&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
              &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;instance&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;response&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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It uses FaunaDB’s built-in &lt;a href="https://docs.fauna.com/fauna/current/api/fql/functions/login"&gt;Login&lt;/a&gt; function, which takes two arguments: &lt;code&gt;identity&lt;/code&gt; and &lt;code&gt;param_object&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For the &lt;code&gt;identity&lt;/code&gt; argument, we pass in a reference to a &lt;code&gt;User&lt;/code&gt; document which we look up by the provided email. To find a user by an email the &lt;code&gt;user_by_email&lt;/code&gt; index is used (we will create it soon).&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.fauna.com/fauna/current/api/fql/functions/match"&gt;Match&lt;/a&gt; function finds the exact match in the given index for the provided search terms. &lt;/p&gt;

&lt;p&gt;As the second argument, we pass in the provided password.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.fauna.com/fauna/current/api/fql/functions/login"&gt;Login&lt;/a&gt; function will return an object containing the &lt;code&gt;secret&lt;/code&gt; under the &lt;code&gt;token&lt;/code&gt; key, the reference to the document (user) and some other data.&lt;/p&gt;

&lt;p&gt;However, we need to have a custom response structure with the &lt;code&gt;token&lt;/code&gt; and &lt;code&gt;user&lt;/code&gt; keys. To restructure the response, we first store the &lt;code&gt;Login&lt;/code&gt; function response in the &lt;code&gt;response&lt;/code&gt; variable using the &lt;a href="https://docs.fauna.com/fauna/current/api/fql/functions/let"&gt;Let&lt;/a&gt; statement and then shape the response object to match what we need and wrap it in the &lt;code&gt;data&lt;/code&gt; object because a UDF must return a GraphQL-compatible type.&lt;/p&gt;

&lt;p&gt;When authentication fails, the &lt;a href="https://docs.fauna.com/fauna/current/api/fql/functions/login"&gt;Login&lt;/a&gt; function returns an error.&lt;/p&gt;

&lt;p&gt;Before we add the &lt;code&gt;login_user&lt;/code&gt; UDF, we need to create the &lt;code&gt;user_by_email&lt;/code&gt; index. Click on the &lt;strong&gt;Indexes&lt;/strong&gt; menu item and then click &lt;strong&gt;NEW INDEX&lt;/strong&gt; link as shown on the next screenshot:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2xgx3p650fn4gokksdt8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2xgx3p650fn4gokksdt8.png" alt="Alt Text" width="800" height="295"&gt;&lt;/a&gt;&lt;br&gt;
Fill in the form as shown in the following screenshot:&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2ufynuabdbnffo851e2a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2ufynuabdbnffo851e2a.png" alt="Alt Text" width="800" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Choose the &lt;code&gt;User&lt;/code&gt; collection&lt;/li&gt;
&lt;li&gt;Add index name&lt;/li&gt;
&lt;li&gt;Type &lt;code&gt;email&lt;/code&gt; in the “Terms” input and click the “+” icon on the right - it will automatically prefix the &lt;code&gt;data.&lt;/code&gt; part -&amp;gt; &lt;code&gt;data.email&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Check "Unique" checkbox (we need an exact match)&lt;/li&gt;
&lt;li&gt;Save it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now we can test the newly created index on the Index page (&lt;strong&gt;Indexes&lt;/strong&gt; menu item)&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsvf6tg9u1kdjfmhvbfc6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsvf6tg9u1kdjfmhvbfc6.png" alt="Alt Text" width="800" height="261"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Paste the following email &lt;code&gt;wiley.cardenas@email.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click the "Search" button &lt;/li&gt;
&lt;li&gt;Get the result&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One more thing is left - we need to allow the &lt;code&gt;Guest&lt;/code&gt; role to read the &lt;code&gt;user_by_email&lt;/code&gt; index and call the &lt;code&gt;login_user&lt;/code&gt; UDF.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftnoivem5irsvco59xy7d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftnoivem5irsvco59xy7d.png" alt="Alt Text" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to the Security page and click &lt;strong&gt;MANAGE ROLES&lt;/strong&gt;, then choose the &lt;code&gt;Guest&lt;/code&gt; role. &lt;/li&gt;
&lt;li&gt;Select the &lt;code&gt;user_by_email&lt;/code&gt; index from the dropdown&lt;/li&gt;
&lt;li&gt;Add “Read” action &lt;/li&gt;
&lt;li&gt;Select the &lt;code&gt;login_user&lt;/code&gt; UDF from the dropdown&lt;/li&gt;
&lt;li&gt;Add “Call” action and save&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now we are ready to update the &lt;code&gt;login_user&lt;/code&gt; UDF the same way as we added the &lt;code&gt;create_user&lt;/code&gt; one. Copy the &lt;code&gt;login_user&lt;/code&gt; UDF code, paste it into the Shell and run the query:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7o95afxzjihvz2v19drt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7o95afxzjihvz2v19drt.png" alt="Alt Text" width="800" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Head over to the GraphQL Playground to test the &lt;code&gt;Login&lt;/code&gt; mutation:&lt;br&gt;
Run the following mutation and you will receive the token and the user details:&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;mutation&lt;/span&gt; &lt;span class="nx"&gt;Login&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;loginUser&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="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wiley.cardenas@email.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&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;token&lt;/span&gt;
    &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;name&lt;/span&gt;
      &lt;span class="nx"&gt;email&lt;/span&gt;
      &lt;span class="nx"&gt;role&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;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fz5s74ng35ncwwu8nqzg3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fz5s74ng35ncwwu8nqzg3.png" alt="Alt Text" width="800" height="339"&gt;&lt;/a&gt;&lt;br&gt;
Try to provide invalid credentials and you will get an error: &lt;code&gt;authentication failed&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  UDF for user logout
&lt;/h3&gt;

&lt;p&gt;We also need to create a &lt;code&gt;logout_user&lt;/code&gt; UDF to allow users to invalidate their login sessions. Here is the UDF code:&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="nc"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;logout_user&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;body&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Lambda&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nc"&gt;Logout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;allTokens&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="kc"&gt;false&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;Let’s quickly add it the same way as we did with the &lt;code&gt;login_user&lt;/code&gt; UDF - via the Shell. See the screenshot:&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Faiiflo10ucas3fnrgqsu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Faiiflo10ucas3fnrgqsu.png" alt="Alt Text" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Testing how it works on a real app
&lt;/h3&gt;

&lt;p&gt;The best way to test out the login and logout, as well as the ABAC features, is to see how it works on a real app. I’ve added features to our starter and you can clone the source code from a branch in the same repo. This way, we will save time on copy and pasting a lot of code.&lt;/p&gt;

&lt;p&gt;Before you clone, note that you will need the &lt;code&gt;.env.development&lt;/code&gt; and &lt;code&gt;.env.production&lt;/code&gt; files from the &lt;a href="https://dev.to/sandorturanszky/how-to-launch-an-mvp-with-jamstack-faunadb-and-graphql-with-zero-running-costs-lfm"&gt;first article&lt;/a&gt; containing the bootstrap key and other variables.&lt;/p&gt;

&lt;p&gt;Follow these steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;git clone --single-branch --branch article-2/authentication-ABAC git@github.com:sandorTuranszky/Gatsby-FaunaDB-GraphQL.git gatsby-fauna-db&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cd gatsby-fauna-db&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;npm install&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;copy the &lt;code&gt;.env.development&lt;/code&gt; and &lt;code&gt;.env.production&lt;/code&gt; files into the root of the project&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gatsby develop&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You should see our updated app with a few menu options including the Login.&lt;/p&gt;

&lt;p&gt;Note if you get any &lt;code&gt;TypeErrors&lt;/code&gt;, delete the &lt;code&gt;token&lt;/code&gt; cookie and the &lt;code&gt;user_data&lt;/code&gt; object from the localStorage.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fynfyytbuvlq55b9c4i3t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fynfyytbuvlq55b9c4i3t.png" alt="Alt Text" width="800" height="300"&gt;&lt;/a&gt;&lt;br&gt;
The main page contains the same static data as before. If you navigate to the &lt;strong&gt;Developers&lt;/strong&gt; page, you will see all public bookmarks being loaded dynamically.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcaefuoz3dds7437g9be8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcaefuoz3dds7437g9be8.png" alt="Alt Text" width="800" height="304"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you reload this page multiple times, you will see the &lt;strong&gt;Loading...&lt;/strong&gt; message for a while before the bookmarks are listed. And if you navigate to the devs tools, you’ll notice the &lt;code&gt;/graphql&lt;/code&gt; request that is made to load bookmarks dynamically.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4c8vaki1phg5t3b7q84q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4c8vaki1phg5t3b7q84q.png" alt="Alt Text" width="800" height="386"&gt;&lt;/a&gt;&lt;br&gt;
This is how easy it is to have a mix of static and dynamic pages with Gatsby.&lt;/p&gt;

&lt;p&gt;There is one issue with bookmarks though. If you take a closer look, you will see that one private bookmark is listed together with the public once for unauthenticated users:&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsvdvo62jfw5lus87k7dt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsvdvo62jfw5lus87k7dt.png" alt="Alt Text" width="800" height="165"&gt;&lt;/a&gt;&lt;br&gt;
This is wrong and we will fix it using FaunaDB’s ABAC.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to the role management page and select the &lt;code&gt;Guest&lt;/code&gt; role.&lt;/li&gt;
&lt;li&gt;Add the following FQL code as shown on the screenshot and save it.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nc"&gt;Lambda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bookmarkRef&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nc"&gt;Let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;bookmark&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bookmarkRef&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
      &lt;span class="na"&gt;private&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&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;private&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bookmark&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="nc"&gt;Equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;private&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="kc"&gt;false&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;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F077jkeaubvg1qjs91rhu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F077jkeaubvg1qjs91rhu.png" alt="Alt Text" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The FQL code above makes sure that only public bookmarks are accessible for the &lt;code&gt;Guest&lt;/code&gt; role by checking whether the &lt;code&gt;private&lt;/code&gt; property on a bookmark is &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Reload the &lt;code&gt;/developers&lt;/code&gt; page, and you should see no private bookmarks listed. No need to rebuild the app, because bookmarks are loaded dynamically. &lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2nn9wvg04nryzh265gkd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2nn9wvg04nryzh265gkd.png" alt="Alt Text" width="800" height="229"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is how easy it is to manage what users can access and what not using FaunaDB's ABAC.&lt;/p&gt;
&lt;h3&gt;
  
  
  Testing the login feature
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Security notice:&lt;/strong&gt; In our example app, we will call a FaunaDB’s GraphQL endpoint right from the client-side. &lt;strong&gt;I strongly discourage you from taking this approach if you will manage sensitive data in your application.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The right way is to call an endpoint on your server under the same domain which would then communicate with third-party APIs. It will also allow you to implement security techniques and best practices to mitigate common client-side vulnerabilities.&lt;/p&gt;

&lt;p&gt;In our example app, we deal with bookmarks which aren't sensitive information at all and this is why we can go with this simplified approach without a backend although it would be easy to implement thanks to Netlify functions. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Token invalidation notice:&lt;/strong&gt; Tokens created by FaunaDB’s &lt;a href="https://docs.fauna.com/fauna/current/api/fql/functions/login"&gt;Login&lt;/a&gt; function do not have a default Time-To-Live (TTL) value. You can set TTL optionally, however, at the time of the writing of this article, the TTL does not guarantee the token removal. The good news is that a reliable token invalidation is being developed by FaunaDB’s dev team and will become available soon. I will update this article once the feature is released.&lt;/p&gt;

&lt;p&gt;It is not an issue for our MVP (users can stay logged-in indefinitely) therefore we will not address token invalidation in this tutorial.&lt;/p&gt;

&lt;p&gt;More on authentication-related security you can read &lt;a href="https://forums.fauna.com/t/do-i-need-a-backend-api-between-faunadb-and-my-app-what-are-the-use-cases-of-an-api/95"&gt;here&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;To test out how the login/logout works, we need to create a new, &lt;code&gt;Developer&lt;/code&gt; role and define privileges for it.&lt;/p&gt;

&lt;p&gt;Run the following FQL query using the Shell to create the &lt;code&gt;Developer&lt;/code&gt; role with required privileges.&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="nc"&gt;CreateRole&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;Developer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;membership&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Collection&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&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;predicate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nc"&gt;Lambda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;userRef&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
          &lt;span class="nc"&gt;Equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&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;role&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nc"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;userRef&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;DEVELOPER&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="p"&gt;],&lt;/span&gt; 
  &lt;span class="na"&gt;privileges&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Collection&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&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bookmark&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nc"&gt;Lambda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bookmarkRef&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nc"&gt;Let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;bookmark&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bookmarkRef&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                &lt;span class="na"&gt;userRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&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;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bookmark&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                &lt;span class="na"&gt;private&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&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;private&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bookmark&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="nc"&gt;Or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nc"&gt;Equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;userRef&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;Identity&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
                &lt;span class="nc"&gt;Equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;private&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="kc"&gt;false&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="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;write&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nc"&gt;Lambda&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;oldData&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;newData&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="nc"&gt;And&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nc"&gt;Equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Identity&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&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;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;oldData&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))),&lt;/span&gt;
              &lt;span class="nc"&gt;Equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&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;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;oldData&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&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;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;newData&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="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nc"&gt;Lambda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nc"&gt;Equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Identity&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&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;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&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="na"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nc"&gt;Lambda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ref&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nc"&gt;Equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Identity&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nc"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&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;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nc"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ref&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="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;allUsers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;allBookmarks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bookmark_user_by_user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;logout_user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;call&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Course&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;allCourses&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bookmark_course_by_course&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;course_author_by_user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s inspect the Developer role:&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0wo2esnndx52wr0pqjjd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0wo2esnndx52wr0pqjjd.png" alt="Alt Text" width="800" height="423"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Just like the &lt;code&gt;Guest&lt;/code&gt; role, &lt;code&gt;Developer&lt;/code&gt; role needs to read the &lt;code&gt;User&lt;/code&gt;, &lt;code&gt;Course&lt;/code&gt; and &lt;code&gt;Bookmark&lt;/code&gt; collections&lt;/li&gt;
&lt;li&gt;Privileges to read indexes are needed to query data on collections&lt;/li&gt;
&lt;li&gt;Logged-in users need to call the &lt;code&gt;logout_user&lt;/code&gt; function to invalidate the auth token.&lt;/li&gt;
&lt;li&gt;For authenticated users, ABAC gets more complex. We need to control not only what users can read, but also what resources they can change and in what way.
Our logged-in user can read, create, update and delete bookmarks.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s look at the “Read” action:&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fwbncakgo7eup6wz049k6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fwbncakgo7eup6wz049k6.png" alt="Alt Text" width="800" height="439"&gt;&lt;/a&gt;&lt;br&gt;
Unlike the &lt;code&gt;Guest&lt;/code&gt; role, the &lt;code&gt;Developer&lt;/code&gt; role can see its own private bookmarks. The code above (the predicate function) makes sure that if the bookmark is private, the user defined on the bookmark is the same user that is logged-in. &lt;/p&gt;

&lt;p&gt;We can see it in action. Navigate to the &lt;code&gt;/app/login&lt;/code&gt; page and log in with the following credentials:&lt;br&gt;
&lt;code&gt;email&lt;/code&gt;: &lt;code&gt;hamilton.lowe@email.com&lt;/code&gt;&lt;br&gt;
&lt;code&gt;password&lt;/code&gt;: &lt;code&gt;password&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You will be redirected to the following page:&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5qz56wlqgxifqfebtny8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5qz56wlqgxifqfebtny8.png" alt="Alt Text" width="800" height="184"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now navigate to the &lt;code&gt;/developers&lt;/code&gt; page where the logged-in user can still see his own bookmarks including private once. However, for the developer Melinda Haynes only public bookmarks are listed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fnsamjgdb20d2qsw7trku.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fnsamjgdb20d2qsw7trku.png" alt="Alt Text" width="800" height="283"&gt;&lt;/a&gt;&lt;br&gt;
Now log out and log in with &lt;code&gt;melinda.haynes@email.com&lt;/code&gt; email and &lt;code&gt;password&lt;/code&gt; password. Note that password is the same for all users to keep things simple. &lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqyc389wqfgm50roiv0d0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqyc389wqfgm50roiv0d0.png" alt="Alt Text" width="800" height="176"&gt;&lt;/a&gt;&lt;br&gt;
It turns out, Melinda has no private bookmarks. Navigate to &lt;code&gt;/developers&lt;/code&gt; page:&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fwpxf30jeagqieasywj7a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fwpxf30jeagqieasywj7a.png" alt="Alt Text" width="800" height="229"&gt;&lt;/a&gt;&lt;br&gt;
We know that Hamilton has a private bookmark, but Melinda can’t see it. &lt;br&gt;
You can add bookmarks for Melinda and then log in as Hamilton to see that it will work as expected. Or you can mark all bookmarks as private and they will not be visible for other logged-in or unauthenticated users. &lt;/p&gt;

&lt;p&gt;You can also remove the FQL code that controls the “Read” action for bookmarks to see how things will get messed up.&lt;/p&gt;

&lt;p&gt;While we were testing, we logged out users successfully that proves that logged-in users are allowed to call the &lt;code&gt;logout_user&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;The “Create” action is also controlled by a predicate function.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fofpd01sq6f2737kdhxw2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fofpd01sq6f2737kdhxw2.png" alt="Alt Text" width="800" height="398"&gt;&lt;/a&gt;&lt;br&gt;
The idea here is to control that bookmarks are created by users for themselves only. This is more a business logic rather than a security consideration in our case. We can easily imagine a content manager or an admin creating content for others. &lt;/p&gt;

&lt;p&gt;The “Delete” action is similar by logic to the “Create” action - allowing you to delete only your own bookmarks.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Febp6hvin9b3d6fd5vg4g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Febp6hvin9b3d6fd5vg4g.png" alt="Alt Text" width="800" height="313"&gt;&lt;/a&gt;&lt;br&gt;
The “Write” action is controlled by a predicate function to make sure users can only update their own bookmarks and that the original bookmark owner does not change during an update.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fg3oukf27g71mm4aaqc9b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fg3oukf27g71mm4aaqc9b.png" alt="Alt Text" width="800" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now let’s look at the membership. By adding the &lt;code&gt;User&lt;/code&gt; collection, we state that all users who are members for the &lt;code&gt;User&lt;/code&gt; collection will be granted the privileges we’ve defined for this role, once they obtain a valid &lt;code&gt;token&lt;/code&gt; using the &lt;a href="https://docs.fauna.com/fauna/current/api/fql/functions/login"&gt;Login&lt;/a&gt; function. &lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fn8avl6lr4yb6pus4lpvd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fn8avl6lr4yb6pus4lpvd.png" alt="Alt Text" width="800" height="275"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;As you can see, it is really easy to create user authentication flow using &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_MVPwithJamstack_STuranszky"&gt;FaunaDB&lt;/a&gt; built-in Auth and ABAC features. Although we have barely scratched the surface, it was still sufficient enough to demonstrate how powerful and flexible the &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_MVPwithJamstack_STuranszky"&gt;FaunaDB&lt;/a&gt; ABAC is. &lt;/p&gt;

&lt;p&gt;I mentioned in the &lt;a href="https://dev.to/sandorturanszky/how-to-launch-an-mvp-with-jamstack-faunadb-and-graphql-with-zero-running-costs-lfm"&gt;first article&lt;/a&gt;, that ABAC rules can also check for date or time for each transaction offering simple solutions to common problems most web apps have - for example, the need to control free trials periods or manage access to paid content.&lt;/p&gt;

&lt;p&gt;With that said, the second part of the challenge is done:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Users can authenticate and access data depending on the privileges they have.&lt;/li&gt;
&lt;li&gt;Bookmarks are loaded dynamically on pages where some of the data is static - a mix of static and dynamic content.&lt;/li&gt;
&lt;li&gt;We reflect what courses have been bookmarked by the logged-in user on static courses data so they can’t bookmark them twice.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now we have a half static and half dynamic website powered by &lt;a href="https://dashboard.fauna.com/accounts/register?utm_source=DevTo&amp;amp;utm_medium=referral&amp;amp;utm_campaign=WritewithFauna_MVPwithJamstack_STuranszky"&gt;FaunaDB&lt;/a&gt;. We still have a lot of room for optimization, and I will cover it in future posts. &lt;/p&gt;

&lt;p&gt;But now, the last thing that is left to do is to explore the &lt;a href="https://docs.fauna.com/fauna/current/tutorials/temporality.html"&gt;Temporality&lt;/a&gt; feature and see how it can be applied in real-life examples. I will do so in the next article.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt; Here is the &lt;a href="https://dev.to/sandorturanszky/how-to-track-change-to-any-content-with-faunadb-s-temporality-feature-2ng8"&gt;third&lt;/a&gt; article.&lt;/p&gt;

&lt;p&gt;The live demo of the app is available &lt;a href="https://thirsty-kalam-33eb5f.netlify.app/"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>jamstack</category>
      <category>fauna</category>
      <category>auth</category>
      <category>abac</category>
    </item>
  </channel>
</rss>
