<?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: Hoon Wee</title>
    <description>The latest articles on DEV Community by Hoon Wee (@hoonweedev).</description>
    <link>https://dev.to/hoonweedev</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%2F779908%2F48438e92-9077-4870-ab2e-758b0e3e8263.jpeg</url>
      <title>DEV Community: Hoon Wee</title>
      <link>https://dev.to/hoonweedev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hoonweedev"/>
    <language>en</language>
    <item>
      <title>Every Neovim, Every Config, All At Once</title>
      <dc:creator>Hoon Wee</dc:creator>
      <pubDate>Wed, 06 Mar 2024 00:26:24 +0000</pubDate>
      <link>https://dev.to/hoonweedev/every-neovim-every-config-all-at-once-578p</link>
      <guid>https://dev.to/hoonweedev/every-neovim-every-config-all-at-once-578p</guid>
      <description>&lt;h2&gt;
  
  
  Neovim is hot 🔥 right now
&lt;/h2&gt;

&lt;p&gt;Neovim, a successful fork of Vim, is a text editor that is gaining popularity. I won't link David Heinemeier Hansson's blog post again since everyone has done it already.&lt;br&gt;&lt;br&gt;
I also won't list all the good stuff about Neovim, since you can find them on the official website and tons of blog posts.  &lt;/p&gt;
&lt;h2&gt;
  
  
  You can build your own IDE, or should I say, own &lt;strong&gt;IDEs&lt;/strong&gt;(plural)
&lt;/h2&gt;

&lt;p&gt;Just by learning a super-easy Lua language, you can build your own IDE with Neovim. If you're a busy person, you can just copy and paste someone else's config.&lt;br&gt;&lt;br&gt;
You can also find a fully-plugged IDE level config like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.lazyvim.org/"&gt;LazyVim&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://astronvim.com/"&gt;AstroNvim&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.lunarvim.org/"&gt;LunarVim&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nvchad.com/"&gt;NvChad&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All you have to do is to clone the repo and paste it in your &lt;code&gt;~/.config/nvim&lt;/code&gt; directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Backup your current config&lt;/span&gt;
&lt;span class="nb"&gt;mv&lt;/span&gt; ~/.config/nvim ~/.config/nvim.bak

&lt;span class="c"&gt;# Clone the repo&lt;/span&gt;
git clone &amp;lt;remote repo&amp;gt; ~/.config/nvim &lt;span class="nt"&gt;--depth&lt;/span&gt; 1

&lt;span class="c"&gt;# Run neovim&lt;/span&gt;
nvim
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But what if you want to use multiple configs at the same time?  &lt;/p&gt;

&lt;p&gt;The answer is simple. All you need to set is the &lt;strong&gt;&lt;code&gt;NVIM_APPNAME&lt;/code&gt;&lt;/strong&gt; environment variable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: Using LunarVim and NvChad at the same time
&lt;/h3&gt;

&lt;p&gt;Let's say you want to use LunarVim for your web development and NvChad for your data science work.&lt;br&gt;
You should first clone both repos with their custom directory names.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Clone LunarVim&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;git clone git@github.com:LunarVim/LunarVim.git ~/.config/lunarvim &lt;span class="nt"&gt;--depth&lt;/span&gt; 1

&lt;span class="c"&gt;# Clone NvChad&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;git clone https://github.com/NvChad/NvChad ~/.config/nvchad &lt;span class="nt"&gt;--depth&lt;/span&gt; 1

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

&lt;/div&gt;



&lt;p&gt;Then, you can set the &lt;code&gt;NVIM_APPNAME&lt;/code&gt; environment variable to use each config.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run neovim with LunarVim&lt;/span&gt;
&lt;span class="nv"&gt;$ NVIM_APPNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;lunarvim nvim

&lt;span class="c"&gt;# Run neovim with NvChad&lt;/span&gt;
&lt;span class="nv"&gt;$ NVIM_APPNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;nvchad nvim
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can still use the default config by just running &lt;code&gt;nvim&lt;/code&gt; without setting the &lt;code&gt;NVIM_APPNAME&lt;/code&gt; environment variable. (This reads the config from &lt;code&gt;~/.config/nvim&lt;/code&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run neovim with the default config&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;nvim
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example: Make aliases for each config
&lt;/h3&gt;

&lt;p&gt;You can also make aliases for each config.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Add these lines to your .bashrc or .zshrc&lt;/span&gt;

&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;lunarvim&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"NVIM_APPNAME=lunarvim nvim"&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;nvchad&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"NVIM_APPNAME=nvchad nvim"&lt;/span&gt;

&lt;span class="c"&gt;# Run neovim with LunarVim&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;lunarvim
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;That's it! You can use multiple Neovim configs at the same time, with just a simple environment variable &lt;code&gt;NVIM_APPNAME&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, some might say &lt;em&gt;"Why don't you just use Docker?"&lt;/em&gt;. Well, not everyone is comfortable with Docker, and since Neovim just supports this feature, why not use it?&lt;/p&gt;

&lt;p&gt;I hope this tip helps you to use Neovim more effectively. Happy hacking! 🚀&lt;/p&gt;

</description>
      <category>neovim</category>
      <category>config</category>
      <category>development</category>
    </item>
    <item>
      <title>How to cast &amp; validate map-typed Ecto schema field</title>
      <dc:creator>Hoon Wee</dc:creator>
      <pubDate>Fri, 01 Mar 2024 15:04:09 +0000</pubDate>
      <link>https://dev.to/hoonweedev/how-to-cast-validate-map-typed-ecto-schema-field-1p8o</link>
      <guid>https://dev.to/hoonweedev/how-to-cast-validate-map-typed-ecto-schema-field-1p8o</guid>
      <description>&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;Let's go straight to the problem. Suppose you have a &lt;code&gt;Post&lt;/code&gt; schema, defined as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/my_app/post.ex&lt;/span&gt;

&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Post&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;
  &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Changeset&lt;/span&gt;

  &lt;span class="n"&gt;schema&lt;/span&gt; &lt;span class="s2"&gt;"posts"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:map&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have a &lt;code&gt;location&lt;/code&gt; field in &lt;code&gt;Post&lt;/code&gt; schema, which is a &lt;code&gt;:map&lt;/code&gt; type. &lt;code&gt;:map&lt;/code&gt; is often used when you want to store a simple set of key-value pairs, but don't want to create a separate database table for it.   &lt;/p&gt;

&lt;p&gt;But how do we cast and validate the &lt;code&gt;location&lt;/code&gt; field? We can't use &lt;code&gt;Ecto.Changeset.cast/3&lt;/code&gt; directly, because it only works with primitive types like &lt;code&gt;:string&lt;/code&gt;, &lt;code&gt;:integer&lt;/code&gt;, &lt;code&gt;:boolean&lt;/code&gt;, etc. Of course we can check that the &lt;code&gt;location&lt;/code&gt; field is a map, but how do we validate the keys and values of the map?   &lt;/p&gt;

&lt;h2&gt;
  
  
  Solution: &lt;code&gt;Ecto.Changeset.cast_embed/3&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;We can use &lt;code&gt;Ecto.Changeset.cast_embed/3&lt;/code&gt; to cast and validate the &lt;code&gt;location&lt;/code&gt; field. &lt;code&gt;cast_embed/3&lt;/code&gt; is used to cast and validate embedded associations, but it can also be used to cast and validate map-typed fields.&lt;/p&gt;

&lt;p&gt;Let's see how we can use &lt;code&gt;cast_embed/3&lt;/code&gt; to cast and validate the &lt;code&gt;location&lt;/code&gt; field. Suppose our location map has to follow these rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It can only have keys: &lt;code&gt;:latitude&lt;/code&gt;, &lt;code&gt;:longitude&lt;/code&gt; and &lt;code&gt;:address&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;:latitude&lt;/code&gt; and &lt;code&gt;:longitude&lt;/code&gt; is required and should be a float&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;:address&lt;/code&gt; is optional and should be a string&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now we need to change something from the code above - we need to change the type of &lt;code&gt;location&lt;/code&gt; field from &lt;code&gt;:map&lt;/code&gt; to &lt;code&gt;Location&lt;/code&gt; schema. Here's how we can define the &lt;code&gt;Location&lt;/code&gt; schema and use &lt;code&gt;cast_embed/3&lt;/code&gt; to cast and validate the &lt;code&gt;location&lt;/code&gt; field:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/my_app/post.ex&lt;/span&gt;

&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Post&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;
  &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Changeset&lt;/span&gt;

  &lt;span class="n"&gt;schema&lt;/span&gt; &lt;span class="s2"&gt;"posts"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;

    &lt;span class="n"&gt;embeds_one&lt;/span&gt; &lt;span class="ss"&gt;:location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Location&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:latitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:float&lt;/span&gt;
      &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:longitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:float&lt;/span&gt;
      &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;changeset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:content&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cast_embed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;with:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;location_changeset&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;location_changeset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;location&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:latitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:longitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:address&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;validate_required&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="ss"&gt;:latitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:longitude&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To use &lt;code&gt;cast_embed/3&lt;/code&gt;, we need to define the &lt;code&gt;Location&lt;/code&gt; schema and use &lt;code&gt;embeds_one/3&lt;/code&gt; to define the embedded schema. We also need to define a &lt;code&gt;location_changeset/2&lt;/code&gt; function to cast and validate the &lt;code&gt;location&lt;/code&gt; field.  &lt;/p&gt;

&lt;p&gt;For migration, you don't have to change anything. The migration will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;CreatePost&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Migration&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;change&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;create&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:posts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;add&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;
      &lt;span class="n"&gt;add&lt;/span&gt; &lt;span class="ss"&gt;:content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;
      &lt;span class="n"&gt;add&lt;/span&gt; &lt;span class="ss"&gt;:location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:map&lt;/span&gt;

      &lt;span class="n"&gt;timestamps&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, we still use &lt;code&gt;:map&lt;/code&gt; type for the &lt;code&gt;location&lt;/code&gt; field in the migration. &lt;/p&gt;

&lt;p&gt;Now let's test this on &lt;code&gt;iex&lt;/code&gt; by with some happy and sad paths (I've omitted the printed output for brevity):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Happy path&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;title:&lt;/span&gt; &lt;span class="s2"&gt;"Hello"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;content:&lt;/span&gt; &lt;span class="s2"&gt;"World"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;location:&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;latitude:&lt;/span&gt; &lt;span class="mf"&gt;1.23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;longitude:&lt;/span&gt; &lt;span class="mf"&gt;4.56&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;address:&lt;/span&gt; &lt;span class="s2"&gt;"123 Main St"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;changeset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;changeset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;changeset&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;valid?&lt;/span&gt;
&lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="c1"&gt;# Sad path - missing latitude&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;title:&lt;/span&gt; &lt;span class="s2"&gt;"Hello"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;content:&lt;/span&gt; &lt;span class="s2"&gt;"World"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;location:&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;longitude:&lt;/span&gt; &lt;span class="mf"&gt;4.56&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;address:&lt;/span&gt; &lt;span class="s2"&gt;"123 Main St"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;changeset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;changeset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;changeset&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;valid?&lt;/span&gt;
&lt;span class="no"&gt;false&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;changeset&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;latitude:&lt;/span&gt; &lt;span class="s2"&gt;"can't be blank"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What if we want to make location type reusable?
&lt;/h2&gt;

&lt;p&gt;If you want to make the &lt;code&gt;Location&lt;/code&gt; type reusable, you can define it as a separate schema and use it in multiple schemas. Here's how you can define the &lt;code&gt;Location&lt;/code&gt; schema and use it in the &lt;code&gt;Post&lt;/code&gt; schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/my_app/location.ex&lt;/span&gt;

&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Location&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;
  &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Changeset&lt;/span&gt;

  &lt;span class="n"&gt;embedded_schema&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:latitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:longitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="nv"&gt;@doc&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;changeset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;location&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:latitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:longitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:address&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;validate_required&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="ss"&gt;:latitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:longitude&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# lib/my_app/post.ex&lt;/span&gt;

&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Post&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;
  &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Changeset&lt;/span&gt;

  &lt;span class="n"&gt;schema&lt;/span&gt; &lt;span class="s2"&gt;"posts"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;
    &lt;span class="n"&gt;embeds_one&lt;/span&gt; &lt;span class="ss"&gt;:location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Location&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;changeset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;post&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:content&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cast_embed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:location&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, we no longer need to reference any &lt;code&gt;location_changeset/2&lt;/code&gt; function in the &lt;code&gt;cast_embed/3&lt;/code&gt; function. It will automatically use the &lt;code&gt;MyApp.Location.changeset/2&lt;/code&gt; function to cast and validate the &lt;code&gt;location&lt;/code&gt; field, which is pretty neat.&lt;/p&gt;

&lt;p&gt;You can now reuse the &lt;code&gt;Location&lt;/code&gt; schema in other schemas as well, for example in a &lt;code&gt;User&lt;/code&gt; schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/my_app/user.ex&lt;/span&gt;

&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;
  &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;Ecto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Changeset&lt;/span&gt;

  &lt;span class="n"&gt;schema&lt;/span&gt; &lt;span class="s2"&gt;"users"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;
    &lt;span class="n"&gt;embeds_one&lt;/span&gt; &lt;span class="ss"&gt;:location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Location&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;changeset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cast_embed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;required:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to make location be a mandatory field, you can use &lt;code&gt;cast_embed/3&lt;/code&gt; with &lt;code&gt;required: true&lt;/code&gt; option like above. This will make sure that the &lt;code&gt;location&lt;/code&gt; field is mandatory and will raise an error if it's missing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this article, we learned how to cast and validate map-typed Ecto schema field using &lt;code&gt;Ecto.Changeset.cast_embed/3&lt;/code&gt;. We also learned how to make the embedded schema reusable in multiple schemas. This is a very powerful feature of Ecto, and it can help you to keep your code DRY and maintainable.&lt;/p&gt;

&lt;p&gt;To learn more about embedded schemas in Ecto, you can refer to the official Ecto documentation: &lt;a href="https://hexdocs.pm/ecto/embedded-schemas.html#content"&gt;Ecto - Embedded schemas&lt;/a&gt;.   &lt;/p&gt;

&lt;p&gt;I hope you find this article helpful. If you have any questions or feedback, feel free to reach out to me. Happy coding! 🚀&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>ecto</category>
      <category>tricks</category>
    </item>
    <item>
      <title>Top 3 Elixir books that will make you love Elixir even more</title>
      <dc:creator>Hoon Wee</dc:creator>
      <pubDate>Wed, 28 Feb 2024 12:44:12 +0000</pubDate>
      <link>https://dev.to/hoonweedev/top-3-elixir-books-that-will-make-you-love-elixir-even-more-2bi6</link>
      <guid>https://dev.to/hoonweedev/top-3-elixir-books-that-will-make-you-love-elixir-even-more-2bi6</guid>
      <description>&lt;h2&gt;
  
  
  Introduction: Beyond the basics
&lt;/h2&gt;

&lt;p&gt;I've been using Elixir for about 2 years now and so far I can confidently say that it's one of the most practical languages I've ever used. It has a lot of features that make it a joy to work with. It's a functional language, it's concurrent, it's distributed, it's fault-tolerant, and it's easy to learn. Whenever my colleagues ask me about Elixir, I always tell them that it's a language that's worth learning. I also recommend they read some of the introductory books about Elixir, like &lt;em&gt;Elixir in Action&lt;/em&gt; by Saša Jurić, &lt;em&gt;Programming Elixir&lt;/em&gt; by Dave Thomas, and &lt;em&gt;The Little Elixir &amp;amp; OTP Guidebook&lt;/em&gt; by Benjamin Tan Wei Hao. These books are great for getting started with Elixir and they cover the basics of the language and the ecosystem. &lt;/p&gt;

&lt;p&gt;But what about the next step? What about the books that will take you beyond the basics? What about the books that will make you love Elixir even more? In this article, I'll share with you the top 3 books that every Elixir developer should read. These books will help you to deepen your understanding of Elixir and to become a better Elixir developer.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I'm not affiliated with any of the authors or publishers of the books mentioned in this article. I'm just a fan of Elixir and I want to share my love for the language with others.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;em&gt;Real-Time Phoenix&lt;/em&gt; by Stephen Bussey
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpragprog.com%2Ftitles%2Fsbsockets%2Freal-time-phoenix%2Fsbsockets.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpragprog.com%2Ftitles%2Fsbsockets%2Freal-time-phoenix%2Fsbsockets.jpg" alt="Real-Time Phoenix"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So you've learned the basics of Elixir and you've built a few web applications with Phoenix. You've learned how to use Ecto, how to use Phoenix LiveView, and how to use Phoenix PubSub. You've also learned how to use Phoenix Channels to build real-time web applications. But you want to go deeper. You want to learn how to build real-time web applications that are fast, reliable, and scalable. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://pragprog.com/titles/sbsockets/real-time-phoenix/" rel="noopener noreferrer"&gt;Real-Time Phoenix&lt;/a&gt; is a book that will teach you to understand and how to build &lt;strong&gt;real&lt;/strong&gt; real-time web applications with Phoenix. The book covers topics like WebSockets, Phoenix PubSub, Phoenix Presence, and (a little bit of) Phoenix LiveView. The best part (I think) is that the book covers how to test Phoenix sockets and channels, which is something that many developers struggle or ignore with. The book also covers how to deploy real-time Phoenix applications to production, and some considerations for scaling them.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;em&gt;Metaprogramming Elixir&lt;/em&gt; by Chris McCord
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpragprog.com%2Ftitles%2Fcmelixir%2Fmetaprogramming-elixir%2Fcmelixir.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpragprog.com%2Ftitles%2Fcmelixir%2Fmetaprogramming-elixir%2Fcmelixir.jpg" alt="Metaprogramming Elixir"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pragprog.com/titles/cmelixir/metaprogramming-elixir/" rel="noopener noreferrer"&gt;Metaprogramming Elixir&lt;/a&gt; is a book that will teach you how to write &lt;strong&gt;code that writes code&lt;/strong&gt;. Metaprogramming is one of the most powerful features of Elixir and it's what makes the language so extensible and flexible. Inspired by Lisp, Elixir's metaprogramming is easier than many other languages (Yes, I talking about you Rust).&lt;/p&gt;

&lt;p&gt;As many Elixir devs know, the author Chris McCord is the creator of the Phoenix web framework. He was once a Ruby developer and there were times when Ruby was not good enough for his needs. He then discovered Elixir and he was amazed by its features. He was particularly impressed by the metaprogramming capabilities of Elixir, which led him to create powerful macros and DSLs in Phoenix. &lt;/p&gt;

&lt;p&gt;In this book, Chris McCord explains how metaprogramming works in Elixir and how you can use it to solve real-world problems. The book covers topics like macros, quote and unquote, code evaluation, and code generation. It also covers the use cases of metaprogramming, like building DSLs, writing code generators, and creating domain-specific abstractions. Once you understand metaprogramming, you'll be able to write more expressive and concise code, and you'll be able to create your libraries and frameworks. &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;em&gt;Concurrent Data Processing in Elixir&lt;/em&gt; by Svilen Gospodinov
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpragprog.com%2Ftitles%2Fsgdpelixir%2Fconcurrent-data-processing-in-elixir%2Fsgdpelixir.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpragprog.com%2Ftitles%2Fsgdpelixir%2Fconcurrent-data-processing-in-elixir%2Fsgdpelixir.jpg" alt="Concurrent Data Processing in Elixir"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Elixir is known for its concurrency primitives. It has lightweight processes, message passing, and supervision trees, which make it easy to write concurrent code. But how do you write concurrent code in Elixir? How do you build concurrent data processing pipelines? How do you handle backpressure and fault tolerance?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pragprog.com/titles/sgdpelixir/concurrent-data-processing-in-elixir/" rel="noopener noreferrer"&gt;Concurrent Data Processing in Elixir&lt;/a&gt; is a book that will answer these questions. The book covers topics like processes, message passing, supervision trees, and fault tolerance. It also covers how to use GenStage, Flow, and Broadway to build concurrent data processing pipelines. &lt;/p&gt;

&lt;p&gt;One thing I love about this book is that it starts with a very basic element, the process, and then it builds up carefully to more complex topics like GenStage, Flow, and Broadway. Not only will you get a solid understanding of the basics of concurrent programming in Elixir, but you'll also be able to choose what libraries would be best for your use case. &lt;/p&gt;

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

&lt;p&gt;Elixir is a language that's worth learning. It's a language that's worth mastering. It's a language that's worth &lt;strong&gt;loving&lt;/strong&gt;. The books I've mentioned in this article will help you to deepen your understanding of Elixir and to become a better Elixir developer. They will help you to build real-time web applications, write expressive and concise code, and build concurrent data processing pipelines. They will make you love Elixir even more.&lt;/p&gt;

&lt;p&gt;I hope you find these books as useful as I did. What are your favorite Elixir books? Let me know in the comments below.&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>phoenix</category>
      <category>books</category>
      <category>concurrency</category>
    </item>
    <item>
      <title>What's the use of zero-sized types in Rust?</title>
      <dc:creator>Hoon Wee</dc:creator>
      <pubDate>Mon, 21 Feb 2022 11:55:36 +0000</pubDate>
      <link>https://dev.to/hoonweedev/whats-the-use-of-zero-sized-types-in-rust-4e83</link>
      <guid>https://dev.to/hoonweedev/whats-the-use-of-zero-sized-types-in-rust-4e83</guid>
      <description>&lt;h2&gt;
  
  
  A struct type without any fields
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Rust's modern type system
&lt;/h3&gt;

&lt;p&gt;Rust is a modern language that has adopted lot's of good features from senior languages.&lt;br&gt;
For example, &lt;strong&gt;Traits&lt;/strong&gt; in Rust has a concept that resembles both Haskell's &lt;em&gt;Type Classes&lt;/em&gt; and Java/Typescript's &lt;em&gt;Interfaces&lt;/em&gt;.&lt;br&gt;
And special types like &lt;code&gt;Option&amp;lt;T&amp;gt;&lt;/code&gt; and &lt;code&gt;Result&amp;lt;T,E&amp;gt;&lt;/code&gt; are inspired from lot's of functional programming languages.&lt;br&gt;
Thanks to those adopted concepts, Rust's type system gives outstanding developer experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Zero-sized type
&lt;/h3&gt;

&lt;p&gt;Today I want to introduce one of the most underrated type pattern called "zero-sized type(shortened as ZST)".&lt;br&gt;
We can declare them like this.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;

&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;ZeroSizedType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;ZST doesn't contain any fields, which means there's no need to allocate any memory space for this type - makes sense for the name 'zero-sized'!&lt;br&gt;
Question is, what's the use of some struct type if it isn't constructed with fields? Let's find out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage of Zero-sized type - a state machine
&lt;/h2&gt;

&lt;p&gt;The most straighforward example to understand the usage of zero-sized type is a state-machine.&lt;br&gt;
But what is a "state machine"? According to &lt;em&gt;Wikipedia&lt;/em&gt;,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A finite-state machine (FSM) or finite-state automaton (FSA, plural:&lt;br&gt;
automata), finite automaton, or simply a &lt;strong&gt;state machine&lt;/strong&gt;, is a mathematical&lt;br&gt;
model of computation. It is an abstract machine that can be in exactly one of&lt;br&gt;
a finite number of states at any given time. The FSM can change from one state&lt;br&gt;
to another in response to some inputs; the change from one state to another is&lt;br&gt;
called a transition.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Looks pretty academic, but state machines can be easily found in real life.&lt;br&gt;
An example image below, we have a simple process of delivering food for our customer.&lt;br&gt;
Since it has states(reserved, cooking, delivering and arrived) and transitions between them, it's a state machine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flb2roa9hm0iki8tdax0w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flb2roa9hm0iki8tdax0w.png" alt="State Transition in Food delivery"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are 4 states - &lt;code&gt;Reserved, Cooking, Delivering&lt;/code&gt; and &lt;code&gt;Arrived&lt;/code&gt;.&lt;br&gt;
And there are 6 possible transitions from one state to another intercepted state. By connecting transitions, we can create various scenarios(or paths).&lt;/p&gt;

&lt;p&gt;Let me show you one of the paths - the most optimistic food delivery scenario.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;Reserved&lt;/code&gt;: Our customer ask for a food delivery, then we tell the customer that the delivery reservation is confirmed if there's enough food.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Reserved&lt;/code&gt; → &lt;code&gt;Cooking&lt;/code&gt; : Once the delivery is reserved, the chef should start cooking the food.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Cooking&lt;/code&gt; → &lt;code&gt;Delivering&lt;/code&gt; : Once food is ready, chef sends the food to our delivery man/woman.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Delivering&lt;/code&gt; → &lt;code&gt;Arrived&lt;/code&gt; : Food has arrived to our customer. Hooray!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So far we've seen the state transitions with the green path, which is a &lt;em&gt;happy path&lt;/em&gt;. But in real life, this might go red, which is &lt;em&gt;sad path&lt;/em&gt; ...&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;Arrived&lt;/code&gt; → &lt;code&gt;Delivering&lt;/code&gt; : Oh no! We delivered different food to our customer. We should go back to the kitchen with this food.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Delivering&lt;/code&gt; → &lt;code&gt;Cooking&lt;/code&gt; : We tell our chef that we gave a wrong food to our customer, so we should cook a correct one.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Cooking&lt;/code&gt; → &lt;code&gt;Reserved&lt;/code&gt; : Our chef choose not to cook, because the kitchen is out of ingredients. We should inform our customer about this.
(Alternatively, we can just cook it and do &lt;code&gt;Cooking&lt;/code&gt; → &lt;code&gt;Delivery&lt;/code&gt; → &lt;code&gt;Arrived&lt;/code&gt; again.)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Cooking&lt;/code&gt; → &lt;code&gt;Reserved&lt;/code&gt; : We tell our excuse to the customer, and cancel the reservation and refund the money.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What a dramatic turn of event! There are tons of possible combinations we can accomplish. You name one!&lt;/p&gt;

&lt;p&gt;For further details about state machines, visit &lt;a href="https://en.wikipedia.org/wiki/Finite-state_machine" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building food delivery state machine with ZST
&lt;/h2&gt;

&lt;p&gt;We can build an exact same food delivery state machine with Rust's ZST.&lt;br&gt;
The full code can be seen &lt;a href="https://gist.github.com/MRGRAVITY817/3fe92b209ac32d93e6fc0ebaf647288b" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To start a new Rust project to follow along my code, run &lt;code&gt;cargo new &amp;lt;project-name&amp;gt;&lt;/code&gt;.&lt;br&gt;
Once the project has been set, head to &lt;code&gt;main.rs&lt;/code&gt; file and let's begin!&lt;/p&gt;

&lt;h3&gt;
  
  
  1. States and Default
&lt;/h3&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;marker&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;PhantomData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Reserved&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Cooking&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Delivering&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Arrived&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;FoodDelivery&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;State&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Reserved&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PhantomData&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="nb"&gt;Default&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;FoodDelivery&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Reserved&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PhantomData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;state&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;This is how our code starts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We created 4 zero-side structs, which will act as &lt;code&gt;State&lt;/code&gt;s for our &lt;code&gt;FoodDelivery&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;FoodDelivery&lt;/code&gt; is a state machine. It receives generic parameter &lt;code&gt;State&lt;/code&gt; which obviously holds current state.&lt;/li&gt;
&lt;li&gt;By assigning &lt;code&gt;&amp;lt;State = Reserved&amp;gt;&lt;/code&gt;, we designate our default state as &lt;code&gt;Reserved&lt;/code&gt;.

&lt;ul&gt;
&lt;li&gt;Since there's a &lt;em&gt;default&lt;/em&gt; generic type, we should implement &lt;code&gt;Default&lt;/code&gt; for &lt;code&gt;Reserved&lt;/code&gt; state.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;The &lt;code&gt;state&lt;/code&gt;'s type should be &lt;code&gt;PhantomData&lt;/code&gt; since our states are &lt;em&gt;zero-sized&lt;/em&gt;.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Now we have our starting state &lt;code&gt;Reserved&lt;/code&gt;, let's add green/red paths for 4 states.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Green/Red paths - State transitions
&lt;/h3&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;FoodDelivery&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Reserved&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;reserved&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;FoodDelivery&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Cooking&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Your delivery has been successfully placed!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Now we'll start cooking your food.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.proceed&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Cooking&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;cancel_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Order canceled - money refunded."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"We hope you to see in another service.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.stay&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;Both methods above starts &lt;em&gt;from&lt;/em&gt; &lt;code&gt;Reserved&lt;/code&gt;. &lt;code&gt;fn reserved()&lt;/code&gt; is a green path and &lt;code&gt;fn cancel_order()&lt;/code&gt; is a path that points itself(cause there's no state to go before this level).&lt;/p&gt;

&lt;p&gt;Let continue with other states.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;FoodDelivery&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Cooking&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;cooking_finished&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;FoodDelivery&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Delivering&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Your food is successfully cooked!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Now we'll send your food via delivery guy.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.proceed&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Delivering&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;cannot_cook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;FoodDelivery&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Reserved&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Cannot cook your food - ingredients are out of stock."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"We'll guide you to cancel your order and get refunded.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.proceed&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Reserved&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;FoodDelivery&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Delivering&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;delivered&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;FoodDelivery&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Arrived&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Delivery guy finished delivering your food.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.proceed&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Arrived&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;taking_food_back&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;FoodDelivery&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Cooking&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"We are taking your food back to the kitchen."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"We will tell chef to cook your food again.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.proceed&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Cooking&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;FoodDelivery&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Arrived&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;confirm_arrival&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Food has arrived! Have a nice meal :)&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.stay&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;cancel_arrival&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;FoodDelivery&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Delivering&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Oh no! Wrong food :("&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"We will take your food again via delivery man/woman&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.proceed&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Delivering&lt;/span&gt;&lt;span class="o"&gt;&amp;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;Same, the each methods starts &lt;em&gt;from&lt;/em&gt; each states. By now you'd notice some interesting parts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Transition methods are separated for each states: Even though we're implementing the same &lt;code&gt;FoodDelivery&lt;/code&gt; struct,
methods are implemented in separated manner for each &lt;code&gt;FoodDelivery&amp;lt;State&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;self.proceed()&lt;/code&gt; and &lt;code&gt;self.stay()&lt;/code&gt;: For transition from state A to B, we can move forward(&lt;code&gt;proceed&lt;/code&gt;), otherwises we need to &lt;code&gt;stay&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The reason for the first one is because the underlying generic parameter is filled with different type.&lt;br&gt;
And that makes our Rust compiler to consider &lt;code&gt;FoodDelivery&amp;lt;Reserved&amp;gt;&lt;/code&gt; and &lt;code&gt;FoodDelivery&amp;lt;Delivering&amp;gt;&lt;/code&gt; as completely different type in binary level.&lt;br&gt;
For us, however, it feels like the type hasn't been changed but somehow Rust managed to separate the state transition methods - like magic!&lt;br&gt;
This was possible because we used ZSTs - they are &lt;strong&gt;real&lt;/strong&gt; types, doing a useful job like this even without any memory allocation.&lt;/p&gt;

&lt;p&gt;One thing to consider - although transition methods are separated for each states, &lt;code&gt;self.proceed()&lt;/code&gt; and &lt;code&gt;self.stay()&lt;/code&gt; seems to be shared. How can this be?&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Shared methods
&lt;/h3&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;FoodDelivery&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;proceed&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;NextState&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;FoodDelivery&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;NextState&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;FoodDelivery&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PhantomData&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;stay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PhantomData&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 can simply make methods sharable by implement methods not for the statically typed ones, but for the generics.&lt;br&gt;
All we need to use is to implement with generic type parameter.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Test scenarios
&lt;/h3&gt;

&lt;p&gt;Now let's test some scenarios! In this post I'll just repeat the two scenarios I described above(the &lt;em&gt;happy path&lt;/em&gt; and the &lt;em&gt;sad path&lt;/em&gt;),&lt;br&gt;
but you can build any state flows you can possibly create.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;{:=^50}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;" Happy Path "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;happy_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;FoodDelivery&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.reserved&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.cooking_finished&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.delivered&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.confirm_arrival&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;{:=^50}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;" Turns out Sad Path "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_turns_out_sad&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;happy_path&lt;/span&gt;
        &lt;span class="nf"&gt;.cancel_arrival&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.taking_food_back&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.cannot_cook&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.cancel_order&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;Splendid isn't it? Let's try out.&lt;/p&gt;

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

&lt;span class="nv"&gt;$ &lt;/span&gt;cargo run

&lt;span class="o"&gt;[&lt;/span&gt;Output]

&lt;span class="o"&gt;===================&lt;/span&gt; Happy Path &lt;span class="o"&gt;===================&lt;/span&gt;

Your delivery has been successfully placed!
Now we&lt;span class="s1"&gt;'ll start cooking your food.

Your food is successfully cooked!
Now we'&lt;/span&gt;ll send your food via delivery guy.

Delivery guy finished delivering your food.

Food has arrived! Have a &lt;span class="nb"&gt;nice &lt;/span&gt;meal :&lt;span class="o"&gt;)&lt;/span&gt;


&lt;span class="o"&gt;===============&lt;/span&gt; Turns out Sad Path &lt;span class="o"&gt;===============&lt;/span&gt;

Oh no! Wrong food :&lt;span class="o"&gt;(&lt;/span&gt;
We will take your food again via delivery man/woman

We are taking your food back to the kitchen.
We will tell chef to cook your food again.

Cannot cook your food - ingredients are out of stock.
We&lt;span class="s1"&gt;'ll guide you to cancel your order and get refunded.

Order canceled - money refunded.
We hope you to see in another service.



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

&lt;/div&gt;

&lt;p&gt;We've built our own state machine with Rust's amazing type system. Hooray!&lt;/p&gt;

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

&lt;p&gt;In this post we've discovered about what is a &lt;em&gt;zero-sized type&lt;/em&gt; in Rust, and how should we use it.&lt;br&gt;
One thing to clarify is that actually we've seen only &lt;strong&gt;one of&lt;/strong&gt; the ZSTs.&lt;br&gt;
There are others like &lt;code&gt;()&lt;/code&gt;, &lt;code&gt;!&lt;/code&gt; and much more, and yet we've seen the &lt;code&gt;struct&lt;/code&gt; version of it.&lt;br&gt;
Those also have an interesting usage to discuss about, but I am gonna leave them for future posts.&lt;br&gt;
For further details about ZSTs, visit &lt;a href="https://runrust.miraheze.org/wiki/Zero-sized_type" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks for reading my post! I'll see you later. Until then, &lt;strong&gt;Happy Coding!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>designpattern</category>
      <category>programming</category>
    </item>
    <item>
      <title>Rust structs - How well are you using it?</title>
      <dc:creator>Hoon Wee</dc:creator>
      <pubDate>Mon, 14 Feb 2022 16:21:24 +0000</pubDate>
      <link>https://dev.to/hoonweedev/rust-structs-how-well-are-you-using-it-nk7</link>
      <guid>https://dev.to/hoonweedev/rust-structs-how-well-are-you-using-it-nk7</guid>
      <description>&lt;h2&gt;
  
  
  How well are you using &lt;code&gt;struct&lt;/code&gt;s?
&lt;/h2&gt;

&lt;p&gt;There are several ways to use structs, and we will see some of them and analyze the pros and cons.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method 1: Public struct with public fields
&lt;/h2&gt;

&lt;p&gt;For public struct with public fields, the easiest way to create struct instance is to do like &lt;code&gt;StructName { field1: .., field2: .. }&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Student&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;friends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;harry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Student&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="s"&gt;"Harry Potter"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&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="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Ron Wealsey"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="cm"&gt;/* using '.' operator */&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_harry_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;harry&lt;/span&gt;&lt;span class="py"&gt;.name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_harry_age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;harry&lt;/span&gt;&lt;span class="py"&gt;.age&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_harry_friends&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;harry&lt;/span&gt;&lt;span class="py"&gt;.friends&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="cm"&gt;/* struct destructuring */&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;Student&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_harry_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_harry_age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_harry_friends&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;harry&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;
  
  
  Pros: Super Simple
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Super simple to define struct.

&lt;ul&gt;
&lt;li&gt;You don't need to create any other methods to get and set the field data.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Getting the field value out of the struct is very easy.

&lt;ul&gt;
&lt;li&gt;You can use &lt;code&gt;.&lt;/code&gt; operator to take the field value out. (ex) &lt;code&gt;harry.name&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;You can also use &lt;strong&gt;struct destructuring&lt;/strong&gt; by &lt;code&gt;StructName { field1, field2, .. }&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons: Too easy to access
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;There's no private data for this struct.

&lt;ul&gt;
&lt;li&gt;If you need any encapsulation for these data fields, this method is not for you.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;Getting a field like this will give a &lt;code&gt;owned value&lt;/code&gt;, which is normally unwanted for most of the case.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rust has a special rule called 'ownership', which means that if the value doesn't implement &lt;code&gt;Copy&lt;/code&gt; trait,
the value moves to new variable.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;harry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Student&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="s"&gt;"Harry Potter"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="c1"&gt;// This field is `String` type, which doesn't implement a `Copy` trait.&lt;/span&gt;
      &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&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="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Ron Wealsey"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;harry_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;harry&lt;/span&gt;&lt;span class="py"&gt;.name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// `harry.name` is moved into a variable `harry_name`&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;another_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;harry&lt;/span&gt;&lt;span class="py"&gt;.name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// COMPILE ERROR, since now `harry` doesn't have a value of `name` field.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;You can fix this with using &lt;code&gt;&amp;amp;&lt;/code&gt; operator by &lt;em&gt;borrowing&lt;/em&gt; the value, yet the better way is make a &lt;em&gt;getter&lt;/em&gt; method.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;harry_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;harry&lt;/span&gt;&lt;span class="py"&gt;.name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;another_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;harry&lt;/span&gt;&lt;span class="py"&gt;.name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;It can be dangerous to allow both reading and writing by just getting field value with &lt;code&gt;.&lt;/code&gt; operator.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;  &lt;span class="cm"&gt;/* Reading the value */&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;harry_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;harry&lt;/span&gt;&lt;span class="py"&gt;.name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="cm"&gt;/* By adding a `mut` keyword, you can write the value of the field */&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;write_harry_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;harry&lt;/span&gt;&lt;span class="py"&gt;.name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;write_harry_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"James Potter"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only difference that separates from reading to writing the field value is whether there's a &lt;code&gt;mut&lt;/code&gt; keyword or not, which is prone to mistakes.&lt;br&gt;
  For simple programs this would be no problem, but still it's important not to make situations that accidentally writes a value in context where we shouldn't.&lt;/p&gt;
&lt;h2&gt;
  
  
  Method 2: Using Getter and Setter
&lt;/h2&gt;

&lt;p&gt;The method above is the simplest, yet it's maybe too publicly accessible. You may want to encapsulate the struct by customizing&lt;br&gt;
which field can be read or written from public context. The most common way to accomplish this is to make a &lt;strong&gt;getter and setter&lt;/strong&gt; methods.&lt;/p&gt;

&lt;p&gt;You can implement the methods for &lt;code&gt;struct&lt;/code&gt;s using &lt;code&gt;impl&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* This struct is public, yet it's fields are private */&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Student&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;friends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Student&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.name&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;age&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;i32&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.age&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;friends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.friends&lt;/span&gt;&lt;span class="nf"&gt;.as_slice&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;harry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Student&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="s"&gt;"Harry Potter"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;friends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Ron Weasley"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Hermione Granger"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;harry_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;harry&lt;/span&gt;&lt;span class="nf"&gt;.name&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;harry_age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;harry&lt;/span&gt;&lt;span class="nf"&gt;.age&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;harry_friends&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;harry&lt;/span&gt;&lt;span class="nf"&gt;.friends&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{harry_name}, {harry_age}, {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;harry_friends&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;
  
  
  Pros: Nicely encapsulated
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;No we have some level of encapsulation.

&lt;ul&gt;
&lt;li&gt;With the code like above, we provide only two functionalities - building a struct instance, and reading each fields.
Our code won't let others to set different value in the field.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;Segregating the role for read/writing delivers better understanding while reading the code.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;By getting the value with method, it will give a better understanding whether this is reading or writing it.
While in the &lt;em&gt;public struct and public fields&lt;/em&gt; method, it was hard to know whether the code is reading or writing the value.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;harry_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;harry&lt;/span&gt;&lt;span class="py"&gt;.name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// This is easier to understand the intention of the code.&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;harry_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;harry&lt;/span&gt;&lt;span class="nf"&gt;.name&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;harry_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;harry&lt;/span&gt;&lt;span class="nf"&gt;.set_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Harry"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;You can choose a custom type for getting a field value.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Defining the method gives you whole another power of control.
You can choose whether to return the borrowed value or owned value.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;  &lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Student&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Returning the borrowed value - this will live until the struct instance is alive&lt;/span&gt;
      &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;borrowed_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.name&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="c1"&gt;// Returning the owned and cloned value - this will live as long as it's now moved.&lt;/span&gt;
      &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;owned_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.name&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;harry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Student&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="s"&gt;"Harry potter"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
      &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;borrowed_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;harry&lt;/span&gt;&lt;span class="nf"&gt;.borrowed_name&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;owned_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;harry&lt;/span&gt;&lt;span class="nf"&gt;.owned_name&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{borrowed_name}, {owned_name}"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="nf"&gt;drop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;harry&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// now you can't access to borrowed value&lt;/span&gt;

      &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{owned_name}"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cons: Verbose
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Bunch of codes to write!

&lt;ul&gt;
&lt;li&gt;If you have 3 fields in your struct, then you will have to write
3 getter methods and 3 setter methods if you want to make a full Read/Write API. And that can be a burden for the developer.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  For the lazy developers - use &lt;code&gt;getset&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;If you are too lazy for writing all those methods, try using &lt;a href="https://docs.rs/getset/latest/getset/"&gt;getset&lt;/a&gt; crate.&lt;br&gt;
It provides several macros which is really easy and intuitive to use.&lt;/p&gt;

&lt;p&gt;Here's a quick example how to use it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;getset&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Getters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Setters&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nd"&gt;#[derive(Getters,&lt;/span&gt; &lt;span class="nd"&gt;Setters)]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Student&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;#[getset(get,&lt;/span&gt; &lt;span class="nd"&gt;set)]&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;harry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Student&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="s"&gt;"Harry Potter"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_get_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;harry&lt;/span&gt;&lt;span class="nf"&gt;.name&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// returns &amp;amp;String type&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_set_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;harry&lt;/span&gt;&lt;span class="nf"&gt;.set_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Harry"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// returns &amp;amp;mut Student type&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Method 3: Builder Pattern
&lt;/h2&gt;

&lt;p&gt;For those who have studied enough about object-oriented programming, you might have heard of &lt;em&gt;Design Patterns&lt;/em&gt;.&lt;br&gt;
Simply put, it's a collections of idiomatic solution for solving problems in OOP project.&lt;br&gt;
From one of the design patterns, there's a &lt;strong&gt;builder pattern&lt;/strong&gt;, which you build a struct instance by setting the field values step-by-step.&lt;/p&gt;

&lt;p&gt;Ok...talk is cheap, I'll show you the code. Here's how you implement builder pattern in Rust.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Student&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;friends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Student&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;StudentBuilder&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;StudentBuilder&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// This can be used after the building process is complete&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&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;struct&lt;/span&gt; &lt;span class="n"&gt;StudentBuilder&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;friends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;StudentBuilder&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;Self&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="s"&gt;""&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;friends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&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="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// `..self` is a syntax sugar for `age: self.age, friends: self.friends`&lt;/span&gt;
        &lt;span class="k"&gt;Self&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="o"&gt;..&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;age&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;friends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;friends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;friends&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Student&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;StudentBuilder&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="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;friends&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;Student&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="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;friends&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;harry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Student&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Harry Potter"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="nf"&gt;.age&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.friends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Ron Weasley"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Hermione Granger"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="nf"&gt;.build&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;With builder pattern, we use intermediary type called &lt;code&gt;StudentBuilder&lt;/code&gt;(or it should be any &lt;code&gt;[StructName]Builder&lt;/code&gt;) to assign field types step-by-step.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros: Complete Segregation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;It provides a segragation between intializer and getter/setter.

&lt;ul&gt;
&lt;li&gt;Initializer is a special kind of method - it doesn't use any self data, yet it creates them.
The role of the initializer and the setter methods should not be confused, while the latter is just a mutator for the previously initialized value.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;By using intermediary &lt;code&gt;Builder&lt;/code&gt; struct, we restrict the access of struct fields until it's intialized.

&lt;ul&gt;
&lt;li&gt;Before finalizing the construct of &lt;code&gt;Student&lt;/code&gt; struct with &lt;code&gt;build()&lt;/code&gt; method, it doesn't give you an exact &lt;code&gt;Student&lt;/code&gt; struct,
so we cannot use any getters and setters. This will prevent our code from making errors of using unset values.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons: Even longer code
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Implementing a builder pattern gives a better encapsulation than &lt;strong&gt;Method 2&lt;/strong&gt;, which results in creating more code as a trade-off.&lt;/li&gt;
&lt;li&gt;We have to set all the field values in single chain like &lt;code&gt;builder().field_one(val).field_two(val).field_three(val).build()&lt;/code&gt;,
which can be restrictive because there might be some situations that we cannot afford all the field values beforehand.

&lt;ul&gt;
&lt;li&gt;This can be solved with implementing &lt;em&gt;ergonomic version&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Ergonomic Builder Pattern
&lt;/h3&gt;

&lt;p&gt;The restrictiveness of 2nd problem we had in builder pattern can be solved by providing &lt;strong&gt;mutability&lt;/strong&gt; in builder methods.&lt;br&gt;
The implementation code should be changed like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Student&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;friends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Student&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;StudentBuilder&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;StudentBuilder&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&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;struct&lt;/span&gt; &lt;span class="n"&gt;StudentBuilder&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;friends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;StudentBuilder&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;Self&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="s"&gt;""&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;friends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&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="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;age&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;friends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;friends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.friends&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;friends&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Student&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;StudentBuilder&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="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;friends&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;Student&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="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;friends&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;harry_builder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Student&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Harry Potter"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;harry_builder&lt;/span&gt;&lt;span class="nf"&gt;.name&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="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;harry_builder&lt;/span&gt;&lt;span class="nf"&gt;.age&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;friends&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Ron Weasley"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Hermione Granger"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="n"&gt;harry_builder&lt;/span&gt;&lt;span class="nf"&gt;.friends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;friends&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;harry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;harry_builder&lt;/span&gt;&lt;span class="nf"&gt;.build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way, we can place the code for setting the field values in place where they are affordable.&lt;br&gt;
This small fix hasn't even increased any code lengths, so that's super-awesome!&lt;/p&gt;

&lt;p&gt;For the detailed explanation for builder patterns in Rust, find it &lt;a href="https://rust-unofficial.github.io/patterns/patterns/creational/builder.html"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;So, what method should we choose for our project? It's hard to answer, but there's an old wisdom for any kind of craftsmanship.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;" The more difficult for the maker, the better for the user. "&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Three methods that we've seen in this article shows the best example for the&lt;br&gt;
quote above. More you write your code, you provide better developer experience for&lt;br&gt;
you and others who use your crate. It's completely up to you, but here I give you&lt;br&gt;
some obvious recommendations.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For projects which needs rapid development, go for
the easiest first, and then the more difficult one when refactoring.&lt;/li&gt;
&lt;li&gt;For projects that should have solid foundations from the beginning,

&lt;ul&gt;
&lt;li&gt;Implement getter/setter or builder pattern by your own, if you have a lot of time.&lt;/li&gt;
&lt;li&gt;Or use helper crates like &lt;code&gt;getset&lt;/code&gt; to quickly implement getter/setter, if you have less time to finish.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One thing you should remember is that using &lt;code&gt;getset&lt;/code&gt; is great, but for better customization &lt;em&gt;(like configuring the return types of methods)&lt;/em&gt; , you should implement it by yourself.&lt;/p&gt;

&lt;p&gt;I hope this article will guide you feel lost when using &lt;code&gt;struct&lt;/code&gt; in Rust. I'll come back with more Rust-related posts!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Until then, happy coding :)&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>designpatterns</category>
      <category>oop</category>
    </item>
    <item>
      <title>Build comments section with Next.js and Supabase</title>
      <dc:creator>Hoon Wee</dc:creator>
      <pubDate>Mon, 07 Feb 2022 23:32:21 +0000</pubDate>
      <link>https://dev.to/hoonweedev/build-comments-section-with-nextjs-and-supabase-1o6c</link>
      <guid>https://dev.to/hoonweedev/build-comments-section-with-nextjs-and-supabase-1o6c</guid>
      <description>&lt;h2&gt;
  
  
  Let's build our own comments section because... why not?
&lt;/h2&gt;

&lt;p&gt;Hi yall folks! In this series, we will use sexy combination - Next.js + Supabase - to build a comment section for blogs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F57s71nd41qfi40d37u00.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F57s71nd41qfi40d37u00.gif" alt="Comment section"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Our goal
&lt;/h3&gt;

&lt;p&gt;Just like every good-old Todo list tutorials, we will build simple &lt;strong&gt;CRUD&lt;/strong&gt;(&lt;strong&gt;C&lt;/strong&gt;reate, &lt;strong&gt;R&lt;/strong&gt;ead, &lt;strong&gt;U&lt;/strong&gt;pdate and &lt;strong&gt;D&lt;/strong&gt;elete) features for our comments.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding comments&lt;/li&gt;
&lt;li&gt;Reading comments&lt;/li&gt;
&lt;li&gt;Editing comments&lt;/li&gt;
&lt;li&gt;Deleting comments&lt;/li&gt;
&lt;li&gt;Replying to comments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The follow-along code can be found &lt;a href="https://github.com/MRGRAVITY817/supabase-comments" rel="noopener noreferrer"&gt;here.&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What do we need?
&lt;/h3&gt;

&lt;p&gt;Obviously, we'll be using those three:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt;: Probably the best production-ready frontend framework in this planet&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://supabase.com/" rel="noopener noreferrer"&gt;Supabase&lt;/a&gt;: Trendy BaaS(Backend as a Service) product for PostgreSQL lovers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And also some extras that will make our project super-easy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;TailwindCSS&lt;/a&gt;: CSS library that will make styling super-easy.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://swr.vercel.app/" rel="noopener noreferrer"&gt;SWR&lt;/a&gt;: Super-simple data fetching/caching library&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Local Settting: Next.js and TailwindCSS
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Create Next App
&lt;/h3&gt;

&lt;p&gt;We'll first create a basic Typescript-based Next.js application using &lt;code&gt;npx create-next-app&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ npx create-next-app --typescript supabase-comments


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

&lt;/div&gt;

&lt;p&gt;When the project is created, head to the files directory and you'll see those basic files.&lt;br&gt;
We won't be needing all of them, so we'll remove some of them.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

...node_modules
├── package.json
├── pages
│   ├── _app.tsx
│   ├── api
│   │   └── hello.ts
│   └── index.tsx
├── public              # &amp;lt;- Remove
│   ├── favicon.ico     # &amp;lt;- Remove
│   └── vercel.svg      # &amp;lt;- Remove
├── styles
│   ├── Home.module.css # &amp;lt;- Remove
│   └── globals.css
├── tsconfig.json
└── yarn.lock


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Add TailwindCSS for NextJS
&lt;/h3&gt;

&lt;p&gt;Above done, then we'll add TailwindCSS &amp;amp; other dependencies for our styling.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ yarn add -D tailwindcss postcss autoprefixer
$ yarn tailwindcss init -p


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

&lt;/div&gt;

&lt;p&gt;The last command will create &lt;code&gt;tailwindcss.config.js&lt;/code&gt; file, which is a config javascript file for TailwindCSS.&lt;br&gt;
This simple file does a lot, but right now we'll just define which files should TailwindCSS watch for.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

module.exports = {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx}",
    "./components/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}


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

&lt;/div&gt;

&lt;p&gt;And then, replace contents in &lt;code&gt;styles/globals.css&lt;/code&gt; with the code down below.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@tailwind base;
@tailwind components;
@tailwind utilities;


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

&lt;/div&gt;

&lt;p&gt;Now we can properly use our TailwindCSS with NextJS!&lt;br&gt;
To test if it works, replace the contents in &lt;code&gt;pages/index.tsx&lt;/code&gt; with the code down below.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import type { NextPage } from "next";
import Head from "next/head";

const Home: NextPage = () =&amp;gt; {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;Head&amp;gt;
        &amp;lt;title&amp;gt;Comments Page&amp;lt;/title&amp;gt;
      &amp;lt;/Head&amp;gt;
      &amp;lt;div className="p-12"&amp;gt;
        &amp;lt;h1 className="text-2xl font-bold"&amp;gt;Comments!&amp;lt;/h1&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

export default Home;


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

&lt;/div&gt;

&lt;p&gt;Run the following command to serve web page in development environment,&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ yarn dev


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

&lt;/div&gt;

&lt;p&gt;... and head to &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; to see the bold typed &lt;strong&gt;Comments!&lt;/strong&gt; in the blank web page.&lt;/p&gt;

&lt;p&gt;Well done! We are done with our local settings(except SWR, but we'll install it later), so let's head to remote setting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remote Setting: Supabase
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Create an organization and a project
&lt;/h3&gt;

&lt;p&gt;Head to &lt;a href="https://supabase.com/" rel="noopener noreferrer"&gt;official Supabase website&lt;/a&gt; and sign in with your Github account.&lt;/p&gt;

&lt;p&gt;Then it will lead you to the app dashboard.&lt;br&gt;
Click &lt;code&gt;New Project&lt;/code&gt; and then click &lt;code&gt;+ New Organization&lt;/code&gt; to create your new project team.&lt;br&gt;
(If you already have an organization, feel free to skip creating one)&lt;/p&gt;

&lt;p&gt;Then it will tell you to create a new name for your organization. Just type any cool name, and hit &lt;code&gt;Create organization&lt;/code&gt;.&lt;/p&gt;

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

&lt;p&gt;Now for the organization we've just created, we'll create a new project which will contain SQL tables.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Name: I'll just name it "Master Supabase", but the naming doesn't matter.&lt;/li&gt;
&lt;li&gt;Database Password: For the password, try using passgen tool to create a strong one. I normally create one from &lt;a href="https://passgen.co/" rel="noopener noreferrer"&gt;PassGen.co&lt;/a&gt;, with length longer than 14.&lt;/li&gt;
&lt;li&gt;Region: Choose the nearest place where you live(as for me, Seoul).&lt;/li&gt;
&lt;li&gt;Pricing Plan: Choose 'Free tier', but you can upgrade later if you want.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once finished the form, hit &lt;code&gt;Create new project&lt;/code&gt;.&lt;/p&gt;

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

&lt;p&gt;And now we have our brand new Supabase project! Feel free to explore around, discover what you can do.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create &lt;code&gt;comments&lt;/code&gt; table
&lt;/h3&gt;

&lt;p&gt;Now we will make a SQL table called &lt;code&gt;comments&lt;/code&gt;. To do that click a 'Table editor' menu in left panel of the dashboard.&lt;br&gt;
If your project is new, the there will be no tables in it. Let's hit &lt;code&gt;Create a new table&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then it will show you a side panel to insert form for your table's settings.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Name: &lt;code&gt;comments&lt;/code&gt;, but feel free to choose other name.&lt;/li&gt;
&lt;li&gt;Description: This is optional, and I am gonna skip it this time.&lt;/li&gt;
&lt;li&gt;Enable Row Level Security: This is a fancy feature in Supabase, but we'll discover it my later post. Now we just skip it.&lt;/li&gt;
&lt;li&gt;Columns: We'll edit &amp;amp; add several columns like the image below.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Make sure you &lt;strong&gt;change the type of 'id'&lt;/strong&gt; column into uuid!&lt;/p&gt;

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

&lt;p&gt;If you've finished and confirmed the form, hit &lt;code&gt;Save&lt;/code&gt; button.&lt;/p&gt;

&lt;p&gt;Now we have our comments table! Let me explain each table columns' role :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;id&lt;/code&gt;: An unique identifier of the column.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;created_at&lt;/code&gt;: We'll use this to sort our comments in created order.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;updated_at&lt;/code&gt;: We'll use this and &lt;code&gt;created_at&lt;/code&gt;'s difference to tell if the comment has been edited.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;username&lt;/code&gt;: This will be used to indicate user who wrote the comment.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;payload&lt;/code&gt;: Actual content of the comment.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;reply_of&lt;/code&gt;: I will save the comment's id that we are replying to.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Create comment
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Add input form and Supabase client library
&lt;/h3&gt;

&lt;p&gt;Our index page is kinda empty now, so we should make it look better. Replace the code in &lt;code&gt;pages/index.tsx&lt;/code&gt; to the code down below.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import type { NextPage } from "next";
import Head from "next/head";

const Home: NextPage = () =&amp;gt; {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;Head&amp;gt;
        &amp;lt;title&amp;gt;Comments Page&amp;lt;/title&amp;gt;
      &amp;lt;/Head&amp;gt;
            &amp;lt;div className="pt-36 flex justify-center"&amp;gt;
        &amp;lt;div className="min-w-[600px]"&amp;gt;
          &amp;lt;h1 className="text-4xl font-bold "&amp;gt;Comments&amp;lt;/h1&amp;gt;
          &amp;lt;form onSubmit={onSubmit} className="mt-8 flex gap-8"&amp;gt;
            &amp;lt;input type="text" placeholder="Add a comment" className="p-2 border-b focus:border-b-gray-700 w-full outline-none" /&amp;gt;
            &amp;lt;button className="px-4 py-2 bg-green-500 rounded-lg text-white"&amp;gt;Submit&amp;lt;/button&amp;gt;
          &amp;lt;/form&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

export default Home;


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

&lt;/div&gt;

&lt;p&gt;We added as simple input form to create a comment. There are some styling codes added,&lt;br&gt;
but since this is not a TailwindCSS tutorial I will leave the explanation to better &lt;a href="https://tailwindcss.com/docs/utility-first" rel="noopener noreferrer"&gt;resource&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Our form looks good, but it doesn't really do anything. To create a comment, we should do this process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User type the comment in the form, then hit &lt;code&gt;Submit&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We somehow send the comment data to our Supabase &lt;code&gt;comments&lt;/code&gt; table.&lt;/li&gt;
&lt;li&gt;We check our table, see if the data has been added.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To perform the 2nd step, we need a Supabase node client library. Run the command below to add it.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ yarn add @supabase/supabase-js


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Make our messenger for Supabase
&lt;/h3&gt;

&lt;p&gt;Now we need to create a supabase client object, which is a messenger that will help us interact with Supabase.&lt;br&gt;
Add these 2 lines in &lt;code&gt;pages/index.tsx&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

...
import { createClient } from "@supabase/supabase-js";

export const supabase = createClient(supabaseUrl, supabaseKey);

const Home: NextPage = () =&amp;gt; {
  return (
    &amp;lt;div&amp;gt;
...


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

&lt;/div&gt;

&lt;p&gt;To create a Supabase client object, we need two data: Supabase project URL and key.&lt;br&gt;
These can be found in &lt;code&gt;Settings &amp;gt; Project settings &amp;gt; API&lt;/code&gt; section in our Supabase project dashboard.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Understanding environment variables
&lt;/h3&gt;

&lt;p&gt;These keys should be stored somewhere secure and separate.&lt;br&gt;
Many of the developers save secured data as &lt;em&gt;'environment variables'&lt;/em&gt;, commonly shorten as 'env vars'.&lt;/p&gt;

&lt;p&gt;Env vars also works as 'variables' which can be set differently across the environment.&lt;br&gt;
For our case, we need to define env vars for development environment, and to do that in NextJS, we use &lt;code&gt;.env.local&lt;/code&gt; file.&lt;br&gt;
If you want to use the same variables in production environment, you can use &lt;code&gt;.env.production&lt;/code&gt; file and replace the values.&lt;/p&gt;

&lt;p&gt;Ok, now let's create a &lt;code&gt;.env.local&lt;/code&gt; file in our NextJS app's root directory.&lt;br&gt;
Then copy-paste the first key (anon/public key) in the image above and save it to &lt;code&gt;NEXT_PUBLIC_SUPABASE_KEY&lt;/code&gt;.&lt;br&gt;
For the second key (URL key), save it to &lt;code&gt;NEXT_PUBLIC_SUPABASE_URL&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If done correctly, it should look like this.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

NEXT_PUBLIC_SUPABASE_KEY=[first-key]
NEXT_PUBLIC_SUPABASE_URL=[second-key]


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

&lt;/div&gt;

&lt;p&gt;Now what's all that &lt;code&gt;NEXT_PUBLIC_&lt;/code&gt; prefix? NextJS handles env vars differently by their names:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;With &lt;code&gt;NEXT_PUBLIC_&lt;/code&gt; prefix: Those are exposed in browser, which means it can be used in client-side jobs.&lt;/li&gt;
&lt;li&gt;Without &lt;code&gt;NEXT_PUBLIC_&lt;/code&gt; prefix: Those are server-side jobs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So that means our supabase basically use those keys in browser side. Once we define or edit our &lt;code&gt;.env.local&lt;/code&gt; file, we have to restart&lt;br&gt;
the development server, so go to terminal and kill the current session with &lt;code&gt;CTRL-C&lt;/code&gt;, and then restart with &lt;code&gt;yarn dev&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Since we can use our env vars now, add and edit the following lines in &lt;code&gt;pages/index.tsx&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

...
import { createClient } from "@supabase/supabase-js";

const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL ?? "";
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_KEY ?? "";

export const supabase = createClient(supabaseUrl, supabaseKey);

const Home: NextPage = () =&amp;gt; {
  return (
    &amp;lt;div&amp;gt;
...


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

&lt;/div&gt;

&lt;p&gt;The extra &lt;code&gt;?? ""&lt;/code&gt; after each env var initalization is to prevent them to be &lt;code&gt;undefined&lt;/code&gt; type, which then &lt;code&gt;createClient&lt;/code&gt; won't accept it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Send an &lt;code&gt;insert&lt;/code&gt; request
&lt;/h3&gt;

&lt;p&gt;Before we use our &lt;code&gt;supabase&lt;/code&gt; messenger, we will first get user's comment payload from our input form.&lt;br&gt;
To do that,&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add a &lt;code&gt;comment&lt;/code&gt; react state for the placeholder of user's comment payload.&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;onChange&lt;/code&gt; function to update the comment payload in &lt;code&gt;comment&lt;/code&gt; whenever the payload is changed.&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;onSubmit&lt;/code&gt; function to handle the submit behavior of the form.
In our case we don't want to reload everytime when we submit the form, so we use &lt;code&gt;event.preventDefault()&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The code change will be as following.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

...
const Home: NextPage = () =&amp;gt; {
  const [comment, setComment] = useState&amp;lt;string&amp;gt;("");

  const onChange = (event: ChangeEvent&amp;lt;HTMLInputElement&amp;gt;) =&amp;gt; {
    const commentValue = event.target.value;
    setComment(commentValue);
  };

  const onSubmit = (event: FormEvent&amp;lt;HTMLFormElement&amp;gt;) =&amp;gt; {
    event.preventDefault();
        console.log(comment);
  };
...
        &amp;lt;form onSubmit={onSubmit} className="mt-8 flex gap-8"&amp;gt;
            &amp;lt;input
                onChange={onChange}
                type="text"
                placeholder="Add a comment"
                className="p-2 border-b focus:border-b-gray-700 w-full outline-none"
            /&amp;gt;
...


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

&lt;/div&gt;

&lt;p&gt;To test if it works, open the Devtools for your browser and head to &lt;code&gt;Console&lt;/code&gt; tab, type something in the input and hit &lt;code&gt;Submit&lt;/code&gt;.&lt;br&gt;
If succeeded, it will show like the image below.&lt;/p&gt;

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

&lt;p&gt;Now we'll use our supabase client to create a comment. In SQL table's perspective, it will basically add a new row.&lt;br&gt;
Replace &lt;code&gt;onSubmit&lt;/code&gt; function to code down below. Don't forget to add an &lt;code&gt;async&lt;/code&gt; keyword since the supabase client api returns a &lt;code&gt;Promise&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

...
  const onSubmit = async (event: FormEvent&amp;lt;HTMLFormElement&amp;gt;) =&amp;gt; {
    event.preventDefault();

    const { data, error } = await supabase.from("comments").insert({
      username: "hoonwee@email.com",
      payload: comment,
    });

    if (!error &amp;amp;&amp;amp; data) {
        // If succeed
      window.alert("Hooray!");
    } else {
        // If failed
      window.alert(error?.message);
    }
  };
...


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

&lt;/div&gt;

&lt;p&gt;Let me disect the &lt;code&gt;await supabase&lt;/code&gt; part real quick:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;.from("comments")&lt;/code&gt; : This indicates which table should be looked for, since theoretically we have lot's of table in project.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;.insert(table_row)&lt;/code&gt; : By using &lt;code&gt;insert&lt;/code&gt;, we create a new row in our &lt;code&gt;comments&lt;/code&gt; table with give table_row data.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;As you can see, we have only put &lt;code&gt;username&lt;/code&gt; and &lt;code&gt;payload&lt;/code&gt; in our &lt;code&gt;table_row&lt;/code&gt;, because the rest of the data will have it's default value.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;code&gt;const { data, error }&lt;/code&gt;: Supabase will give us a response containing &lt;code&gt;data&lt;/code&gt; which contains information of our action, and &lt;code&gt;error&lt;/code&gt; if there's an error.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Now let's write something again and hit &lt;code&gt;Submit&lt;/code&gt;. And if you have succeeded, you'll see an alert window containing &lt;strong&gt;Hooray!&lt;/strong&gt; message.&lt;/p&gt;

&lt;p&gt;Well that's pretty nice, but we still don't know whether our data has been sent or not.&lt;br&gt;
Head to &lt;code&gt;comments&lt;/code&gt; table in Supabase dashboard, and you'll see a new data row inserted.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Read comments
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Create a comment list
&lt;/h3&gt;

&lt;p&gt;Okay great, now we can create comments but we also want to display them in our web page.&lt;br&gt;
To do that we'll do following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We get all the comments from the &lt;code&gt;comments&lt;/code&gt; table.&lt;/li&gt;
&lt;li&gt;We display it - in list-like format.&lt;/li&gt;
&lt;li&gt;We will sort then by &lt;code&gt;created_at&lt;/code&gt; data to see them chronologically.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;First we should add some UI for the list! Add the following code in &lt;code&gt;pages/index.tsx&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

...
export const supabase = createClient(supabaseUrl, supabaseKey);

interface CommentParams {
  id: string;
  created_at: string;
  updated_at: string;
  username: string;
  payload: string;
  reply_of?: string;
}

const Home: NextPage = () =&amp;gt; {
  const [comment, setComment] = useState&amp;lt;string&amp;gt;("");
  const [commentList, setCommentList] = useState&amp;lt;CommentParams[]&amp;gt;([]);
...
          Submit
        &amp;lt;/button&amp;gt;
      &amp;lt;/form&amp;gt;
      &amp;lt;div className="flex flex-col gap-4 pt-12"&amp;gt;
        {commentList.map((comment) =&amp;gt; (
          &amp;lt;div key={comment.id} className="border rounded-md p-4"&amp;gt;
            &amp;lt;p className="font-semibold mb-2"&amp;gt;{comment.username}&amp;lt;/p&amp;gt;
            &amp;lt;p className="font-light"&amp;gt;{comment.payload}&amp;lt;/p&amp;gt;
          &amp;lt;/div&amp;gt;
        ))}
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
...


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

&lt;/div&gt;

&lt;p&gt;Let's disect this part real quick:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Below our &lt;code&gt;form&lt;/code&gt; element we added a comment list section, iterated by &lt;code&gt;commentList&lt;/code&gt; react state that we newly created.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;commentList&lt;/code&gt; state has a array-type of &lt;code&gt;CommentParams&lt;/code&gt; interface, which contains all the column names for each object key.&lt;/li&gt;
&lt;li&gt;The question mark &lt;code&gt;?&lt;/code&gt; at &lt;code&gt;reply_of&lt;/code&gt; field indicates that this field is optional.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Send a &lt;code&gt;select&lt;/code&gt; request
&lt;/h3&gt;

&lt;p&gt;Before moving to next step, I want you to add more comments using our form&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;because once we succeed to get the comments from supabase, it will look awesome when we have bunch of comments.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you've done adding more comments, now let add a new function called &lt;code&gt;getCommentList&lt;/code&gt; that will use &lt;code&gt;supabase&lt;/code&gt; client to&lt;br&gt;
get all the comments. Add the code down below.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

...
  const [commentList, setCommentList] = useState&amp;lt;CommentParams[]&amp;gt;([]);

  const getCommentList = async () =&amp;gt; {
    const { data, error } = await supabase.from("comments").select("*");
    if (!error &amp;amp;&amp;amp; data) {
      setCommentList(data);
    } else {
      setCommentList([]);
    }
  };

  useEffect(() =&amp;gt; {
    getCommentList();
  }, []);
...


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

&lt;/div&gt;

&lt;p&gt;Right now &lt;code&gt;getCommentList&lt;/code&gt; function will be called &lt;strong&gt;once&lt;/strong&gt; only, when we first render our page.&lt;br&gt;
To do that we will call our &lt;code&gt;getCommentList&lt;/code&gt; in &lt;code&gt;useEffect&lt;/code&gt; hook. Since the &lt;code&gt;useEffect&lt;/code&gt; hook has no external dependency,&lt;br&gt;
this will invoke the inner part only once when the component is rendered.&lt;/p&gt;

&lt;p&gt;Now check our web page. It will look much like other comment sections!&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Sort comments by created order
&lt;/h3&gt;

&lt;p&gt;Now our Supabase client takes the comment list in &lt;code&gt;created&lt;/code&gt; order,&lt;br&gt;
but soon after when we edit and reply to some of the comments it would bring them by &lt;code&gt;updated&lt;/code&gt; order.&lt;/p&gt;

&lt;p&gt;So we should do a little tweak in our code to sort them.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

...

          &amp;lt;div className="flex flex-col gap-4 pt-12"&amp;gt;
            {commentList
              .sort((a, b) =&amp;gt; {
                const aDate = new Date(a.created_at);
                const bDate = new Date(b.created_at);
                return +aDate - +bDate;
              })
              .map((comment) =&amp;gt; (
                &amp;lt;div key={comment.id} className="border rounded-md p-4"&amp;gt;
                  &amp;lt;p className="font-semibold mb-2"&amp;gt;{comment.username}&amp;lt;/p&amp;gt;

...



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

&lt;/div&gt;

&lt;p&gt;Breaking this down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We added &lt;code&gt;.sort&lt;/code&gt; before doing &lt;code&gt;.map&lt;/code&gt; in comment list rendering part.&lt;/li&gt;
&lt;li&gt;The logic inside &lt;code&gt;.sort&lt;/code&gt; will arrange from the oldest to the youngest.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;+&lt;/code&gt; sign at the beginning of &lt;code&gt;aDate&lt;/code&gt; and &lt;code&gt;bDate&lt;/code&gt; is for casting &lt;code&gt;Date&lt;/code&gt; type to
&lt;code&gt;number&lt;/code&gt; type, since Typescript's &lt;code&gt;sort()&lt;/code&gt;'s return value has to be in &lt;code&gt;number&lt;/code&gt; type.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Update and Delete Comments
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Plan a feature
&lt;/h3&gt;

&lt;p&gt;We make mistakes, especially a lot when we write something in the internet.&lt;br&gt;
That's why there's always a &lt;code&gt;Edit&lt;/code&gt; and &lt;code&gt;Delete&lt;/code&gt; section in posts and comment section in Facebook, Medium, Twitter and etc.&lt;/p&gt;

&lt;p&gt;A properly working editing feature should have these features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Able to edit the payload(content) right at the comment item itself.&lt;/li&gt;
&lt;li&gt;Disable edit button unless payload hasn't changed.&lt;/li&gt;
&lt;li&gt;Indicate if this comment has been edited.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A great deleting feature should:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ask user if they're really gonna delete this to prevent if this was a click mistake.&lt;/li&gt;
&lt;li&gt;Then delete it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Create edit input form
&lt;/h3&gt;

&lt;p&gt;For the best user experience, the input form to edit the comment should not be the same place where you add a comment but the comment list item itself.&lt;br&gt;
That means we should upgrade our comment list item, so let's add the following code to do it!&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

...

const Home: NextPage = () =&amp;gt; {
  const [comment, setComment] = useState&amp;lt;string&amp;gt;("");
  const [commentList, setCommentList] = useState&amp;lt;CommentParams[]&amp;gt;([]);
  const [editComment, setEditComment] = useState&amp;lt;EditCommentParams&amp;gt;({
    id: "",
    payload: "",
  });

  const onChangeEditComment = (event: ChangeEvent&amp;lt;HTMLInputElement&amp;gt;) =&amp;gt; {
    const payload = event.target.value;
    setEditComment({ ...editComment, payload });
  };

  const confirmEdit = () =&amp;gt; {
    window.alert("Confirm edit comment");
  };

  const getCommentList = async () =&amp;gt; {
    const { data, error } = await supabase.from("comments").select("*");

...
        &amp;lt;div key={comment.id} className="border rounded-md p-4"&amp;gt;
          &amp;lt;p className="font-semibold mb-2"&amp;gt;{comment.username}&amp;lt;/p&amp;gt;
          &amp;lt;div className="flex items-center gap-2 justify-between"&amp;gt;
            {comment.id === editComment.id ? (
              &amp;lt;input
                type="text"
                value={editComment.payload}
                onChange={onChangeEditComment}
                className="pb-1 border-b w-full"
              /&amp;gt;
            ) : (
              &amp;lt;p className="font-light"&amp;gt;{comment.payload}&amp;lt;/p&amp;gt;
            )}
            {editComment.id === comment.id ? (
              &amp;lt;div className="flex gap-2"&amp;gt;
                &amp;lt;button type="button" onClick={confirmEdit} className="text-green-500"&amp;gt;
                  Confirm
                &amp;lt;/button&amp;gt;
                &amp;lt;button
                  type="button"
                  onClick={() =&amp;gt; setEditComment({ id: "", payload: "" })}
                  className="text-gray-500"
                &amp;gt;
                  Cancel
                &amp;lt;/button&amp;gt;
              &amp;lt;/div&amp;gt;
            ) : (
              &amp;lt;button
                type="button"
                onClick={() =&amp;gt; setEditComment({ id: comment.id, payload: comment.payload })}
                className="text-green-500"
              &amp;gt;
                Edit
              &amp;lt;/button&amp;gt;
            )}
          &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
      ))}
    &amp;lt;/div&amp;gt;

...



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

&lt;/div&gt;

&lt;p&gt;A brief analysis for this one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We added &lt;code&gt;editComment&lt;/code&gt; state, which will set which comment to edit and to what payload it should be.&lt;/li&gt;
&lt;li&gt;We added two functions:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;onChangeEditComment&lt;/code&gt;: This will watch the value of edit form input and set the payload to edit with &lt;code&gt;setEditComment&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;confirmEdit&lt;/code&gt;: This will send a request to Supabase to update the comment, but right now we just put silly window alert.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;We updated our comment item section to switch between 'read-mode' and 'edit-mode' with &lt;code&gt;editComment&lt;/code&gt; state.&lt;/li&gt;

&lt;/ul&gt;

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

&lt;h3&gt;
  
  
  Constructing &lt;code&gt;update&lt;/code&gt; request
&lt;/h3&gt;

&lt;p&gt;Now only thing to do is just replacing the &lt;code&gt;confirmEdit&lt;/code&gt; function to communicate with Supabase.&lt;br&gt;
Replace that part with this code. I'm sure you are now getting familiar with &lt;code&gt;supabase&lt;/code&gt; api.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


...

   const confirmEdit = async () =&amp;gt; {
    const { data, error } = await supabase
      .from("comments")
      .update({
        payload: editComment.payload,
      })
      .match({ id: editComment.id });
    if (!error &amp;amp;&amp;amp; data) {
      window.alert("Edited Comment!");
    } else {
      window.alert(error?.message);
    }
  };

...



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

&lt;/div&gt;

&lt;p&gt;So apparently from this code,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We used &lt;code&gt;update&lt;/code&gt; function to update the data.

&lt;ul&gt;
&lt;li&gt;We only need to pass the changed part, not the whole other parts.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Then used &lt;code&gt;match&lt;/code&gt; function to target which comments should be updated.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;But wait, shouldn't we update the &lt;code&gt;updated_at&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;That is correct! We will do it in Supabase Dashboard, not in our Next.js code.&lt;br&gt;
We will use an actual SQL query to accomplish this, and to do that head to &lt;strong&gt;SQL Editor&lt;/strong&gt; through navigation bar.&lt;br&gt;
Then you'll see an input box to write down the SQL query. Insert the SQL query down below.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;

&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="n"&gt;extension&lt;/span&gt; &lt;span class="n"&gt;if&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;exists&lt;/span&gt; &lt;span class="n"&gt;moddatetime&lt;/span&gt; &lt;span class="k"&gt;schema&lt;/span&gt; &lt;span class="n"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;trigger&lt;/span&gt; &lt;span class="n"&gt;handle_updated_at&lt;/span&gt; &lt;span class="k"&gt;before&lt;/span&gt; &lt;span class="k"&gt;update&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;comments&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;each&lt;/span&gt; &lt;span class="k"&gt;row&lt;/span&gt; &lt;span class="k"&gt;execute&lt;/span&gt; &lt;span class="k"&gt;procedure&lt;/span&gt; &lt;span class="n"&gt;moddatetime&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;updated_at&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;So much to explain about this query, but basically it will set the &lt;code&gt;updated_at&lt;/code&gt; column to the current timestamp for every update.&lt;br&gt;
Hit &lt;code&gt;Run&lt;/code&gt; to run the query and adapt the trigger.&lt;/p&gt;

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

&lt;p&gt;Now our edit request will work like a charm. Try editing any comment, and then refresh. If succeeded then you'll see your comment edited.&lt;/p&gt;

&lt;h3&gt;
  
  
  Disable &lt;code&gt;Confirm&lt;/code&gt; button when comment is not edited
&lt;/h3&gt;

&lt;p&gt;Currently we just allow user to click &lt;code&gt;Confirm&lt;/code&gt; button whenever they press &lt;code&gt;Edit&lt;/code&gt; button, without checking if the payload has changed.&lt;br&gt;
This can arise two problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Our &lt;code&gt;confirmEdit&lt;/code&gt; function always changes &lt;code&gt;updated_at&lt;/code&gt; data, so even though we've mistakenly confirm the edit without changing,
the comment will always be marked as &lt;code&gt;edited&lt;/code&gt; since there's no going back in time.&lt;/li&gt;
&lt;li&gt;Now this is not that critical matter, but if we were to use this in much bigger project,
then there will be unnecessary transaction between user browser and Supabase.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To prevent this, we need to &lt;strong&gt;disable&lt;/strong&gt; the &lt;code&gt;Confirm&lt;/code&gt; button when user hasn't change their comment. Let's tweek a code a little bit.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


...

    {editComment.id === comment.id ? (
      &amp;lt;&amp;gt;
        &amp;lt;button
          type="button"
          onClick={confirmEdit}
          disabled={editComment.payload === comment.payload}
          className={`${editComment.payload === comment.payload ? `text-gray-300` : `text-green-500`}`}
        &amp;gt;
          Confirm
        &amp;lt;/button&amp;gt;
        &amp;lt;button
          type="button"
          onClick={() =&amp;gt; setEditComment({ id: "", payload: "" })}
          className="text-gray-500"

...



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

&lt;/div&gt;

&lt;p&gt;Now our &lt;code&gt;Confirm&lt;/code&gt; button will be disabled unless the content of the comment has been changed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwws12jl2nur4y449vc3t.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwws12jl2nur4y449vc3t.gif" alt="Disable when not edited"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Indicate &lt;code&gt;edited&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Comment item should indicate that it has been edited. This can be acheived quite easily - by comparing &lt;code&gt;created_at&lt;/code&gt; and &lt;code&gt;updated_at&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


...

      .map((comment) =&amp;gt; (
        &amp;lt;div key={comment.id} className="border rounded-md p-4"&amp;gt;
          &amp;lt;p className="font-semibold mb-2"&amp;gt;
            {comment.username}
            {comment.updated_at !== comment.created_at &amp;amp;&amp;amp; (
              &amp;lt;span className="ml-4 text-sm italic font-extralight"&amp;gt;edited&amp;lt;/span&amp;gt;
            )}
          &amp;lt;/p&amp;gt;
          &amp;lt;div className="flex items-center gap-2 justify-between"&amp;gt;
            {comment.id === editComment.id ? (

...



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

&lt;/div&gt;

&lt;p&gt;Now if we edit any comment, it will indicate &lt;code&gt;edited&lt;/code&gt; in extra-light &amp;amp; italic font.&lt;/p&gt;

&lt;h3&gt;
  
  
  Delete comment
&lt;/h3&gt;

&lt;p&gt;Deleting comment is not so different from updating comment - it uses same &lt;code&gt;match&lt;/code&gt; function to target which comment should&lt;br&gt;
be deleted. Let's do this real quick.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


...

  const confirmDelete = async (id: string) =&amp;gt; {
    const ok = window.confirm("Delete comment?");
    if (ok) {
      const { data, error } = await supabase.from("comments").delete().match({ id });
      if (!error &amp;amp;&amp;amp; data) {
        window.alert("Deleted Comment :)");
      } else {
        window.alert(error?.message);
      }
    }
  };

...

                  onChange={onChangeEditComment}
                  className="pb-1 border-b w-full"
                /&amp;gt;
              ) : (
                &amp;lt;p className="font-light"&amp;gt;{comment.payload}&amp;lt;/p&amp;gt;
              )}
              &amp;lt;div className="flex gap-2"&amp;gt;
                {editComment.id === comment.id ? (
                  &amp;lt;&amp;gt;
                    &amp;lt;button type="button" onClick={confirmEdit} className="text-green-500"&amp;gt;
                      Confirm
                    &amp;lt;/button&amp;gt;
                    &amp;lt;button
                      type="button"
                      onClick={() =&amp;gt; setEditComment({ id: "", payload: "" })}
                      className="text-gray-500"
                    &amp;gt;
                      Cancel
                    &amp;lt;/button&amp;gt;
                  &amp;lt;/&amp;gt;
                ) : (
                  &amp;lt;&amp;gt;
                    &amp;lt;button
                      type="button"
                      onClick={() =&amp;gt; setEditComment({ id: comment.id, payload: comment.payload })}
                      className="text-green-500"
                    &amp;gt;
                      Edit
                    &amp;lt;/button&amp;gt;
                    &amp;lt;button type="button" onClick={() =&amp;gt; confirmDelete(comment.id)} className="text-gray-700"&amp;gt;
                      Delete
                    &amp;lt;/button&amp;gt;
                  &amp;lt;/&amp;gt;
                )}
              &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
          &amp;lt;/div&amp;gt;
        ))}
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;

...



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

&lt;/div&gt;

&lt;p&gt;After adding the code, click the &lt;code&gt;Delete&lt;/code&gt; button in the comment item that you want to delete, press &lt;code&gt;Ok&lt;/code&gt; in confirm window,&lt;br&gt;
and refresh the page - then the comment is gone!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You see, making CRUD feature with Supabase is Supa-easy!&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Replying to Comments
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How our reply will work
&lt;/h3&gt;

&lt;p&gt;Now guess the only column in our table that we haven't used yet - that's right, the &lt;code&gt;reply_of&lt;/code&gt; column. We'll use it right away to&lt;br&gt;
add a feature to reply comments.&lt;/p&gt;

&lt;p&gt;Let's think how our reply feature would work best:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User clicks &lt;code&gt;Reply&lt;/code&gt; button on the comment item.&lt;/li&gt;
&lt;li&gt;Then our input form(for adding comments) will show that this comment will be a replying comment of some other comment.&lt;/li&gt;
&lt;li&gt;Once added and fetched to the comment list, it should still be distinguishable from normal comments.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Adding &lt;code&gt;Reply of: ....&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Alrighty, as always let's work with the UI part first.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


...

  const [editComment, setEditComment] = useState&amp;lt;EditCommentParams&amp;gt;({ id: "", payload: "" });
  const [replyOf, setReplyOf] = useState&amp;lt;string | null&amp;gt;(null);

  const onChangeEditComment = (event: ChangeEvent&amp;lt;HTMLInputElement&amp;gt;) =&amp;gt; {

...

    &amp;lt;h1 className="text-4xl font-bold "&amp;gt;Comments&amp;lt;/h1&amp;gt;
    &amp;lt;form onSubmit={onSubmit} className="mt-8 flex gap-8"&amp;gt;
      &amp;lt;div className="w-full"&amp;gt;
      {replyOf &amp;amp;&amp;amp; (
        &amp;lt;div className="flex gap-4 my-2 items-center justify-start"&amp;gt;
          &amp;lt;p className="text-xs font-extralight italic text-gray-600"&amp;gt;
            Reply of: {commentList.find((comment) =&amp;gt; comment.id === replyOf)?.payload ?? ""}
          &amp;lt;/p&amp;gt;
          &amp;lt;button onClick={() =&amp;gt; setReplyOf(null)} className="text-xs font-light text-red-600"&amp;gt;
            Cancel
          &amp;lt;/button&amp;gt;
        &amp;lt;/div&amp;gt;
      )}

...

    .map((comment) =&amp;gt; (
      &amp;lt;div key={comment.id} className="border rounded-md p-4"&amp;gt;
        {comment.reply_of &amp;amp;&amp;amp;
          &amp;lt;p className="font-extralight italic text-gray-600 text-xs"&amp;gt;
            Reply of: {commentList.find((c) =&amp;gt; c.id === comment.reply_of)?.payload ?? ""}
          &amp;lt;/p&amp;gt;
        }
        &amp;lt;p className="font-semibold mb-2"&amp;gt;
          {comment.username}

...

            Delete
          &amp;lt;/button&amp;gt;
          &amp;lt;button type="button" onClick={() =&amp;gt; setReplyOf(comment.id)} className="text-orange-500"&amp;gt;
            Reply
          &amp;lt;/button&amp;gt;
        &amp;lt;/&amp;gt;
      )}
    &amp;lt;/div&amp;gt;

...



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

&lt;/div&gt;

&lt;p&gt;Here in this code, we can see that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We declared a new state called &lt;code&gt;replyOf&lt;/code&gt; to save the id of replying comment that we select.&lt;/li&gt;
&lt;li&gt;We added a single text line in (1) Input form (2) Above username in comment item, showing which comment we are replying to.

&lt;ul&gt;
&lt;li&gt;In input form, we also added a &lt;code&gt;Cancel&lt;/code&gt; button to cancel replying to it and revert our input form to normal one.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;We added a &lt;code&gt;Reply&lt;/code&gt; button, which will use &lt;code&gt;setReplyOf&lt;/code&gt; to save the id of comment that we select.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Ok this explanation enough, but basically it will look like this simple.&lt;/p&gt;

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

&lt;p&gt;Then all you need to add is to pass the replying comment's id to &lt;code&gt;reply_of&lt;/code&gt; field in &lt;code&gt;onSubmit&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


...

  const onSubmit = async (event: FormEvent&amp;lt;HTMLFormElement&amp;gt;) =&amp;gt; {
    event.preventDefault();
    const { data, error } = await supabase.from("comments").insert({
      username: "hoonwee@email.com",
      payload: comment,
      reply_of: replyOf,
    });
    if (!error &amp;amp;&amp;amp; data) {
      window.alert("Hooray!");
    } else {
      window.alert(error?.message);
    }
  };

...



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

&lt;/div&gt;

&lt;p&gt;Now try adding a replying comment and then refresh. If done correctly, you'll see a reply comment like the image below.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Restyling using Icons
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Problems with text-only UI
&lt;/h3&gt;

&lt;p&gt;So now our comment section is already awesome. It can read, create, update, delete and reply to comments.&lt;br&gt;
While it's fully functional, we have to admit that it looks very boring and visually unapealing - because we only used texts for our UI.&lt;/p&gt;

&lt;p&gt;The problem with using only text for our UI can invoke bad user experiences like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It can be less intuitive, which will confuse users.&lt;/li&gt;
&lt;li&gt;If the text is too long, it can ruin the overall visual design.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So to solve this, we need some design element that can pack the meaning of the UI into strong and concise visual format.&lt;br&gt;
The best one for this, as far as I know, is an &lt;strong&gt;icon&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Text → Icons
&lt;/h3&gt;

&lt;p&gt;There are tons of icon packs in internet, and in here we will use one called &lt;a href="https://heroicons.dev/" rel="noopener noreferrer"&gt;Hero icons&lt;/a&gt;.&lt;br&gt;
Since it's been developed from the people behind TailwindCSS, it works best with our project.&lt;/p&gt;

&lt;p&gt;Install Hero icons with the following command.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ yarn add @heroicons/react


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

&lt;/div&gt;

&lt;p&gt;Now let's start replacing several texts into Hero icons!&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


...

import { ReplyIcon, PencilIcon, TrashIcon, CheckCircleIcon, XCircleIcon, XIcon } from "@heroicons/react/outline";

...

          Cancel
        &amp;lt;/button&amp;gt;
      &amp;lt;/&amp;gt;
    ) : (
      &amp;lt;&amp;gt;
        &amp;lt;button
          onClick={() =&amp;gt; setEditComment({ id: comment.id, payload: comment.payload })}
          title="Edit comment"
        &amp;gt;
          &amp;lt;PencilIcon className="w-6" /&amp;gt;
        &amp;lt;/button&amp;gt;
        &amp;lt;button onClick={() =&amp;gt; confirmDelete(comment.id)} title="Delete comment"&amp;gt;
          &amp;lt;TrashIcon className="w-6" /&amp;gt;
        &amp;lt;/button&amp;gt;
        &amp;lt;button onClick={() =&amp;gt; setReplyOf(comment.id)} title="Reply to comment"&amp;gt;
          &amp;lt;ReplyIcon className="w-6 rotate-180" /&amp;gt;
        &amp;lt;/button&amp;gt;
      &amp;lt;/&amp;gt;
    )}
  &amp;lt;/div&amp;gt;

...



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

&lt;/div&gt;

&lt;p&gt;What has been changed?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We replaced three texts in our comment item - &lt;code&gt;Edit&lt;/code&gt;, &lt;code&gt;Delete&lt;/code&gt;, and &lt;code&gt;Reply&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We removed the unnecessary color variation between buttons cause our icons already are distinguishable.&lt;/li&gt;
&lt;li&gt;We added a &lt;code&gt;title&lt;/code&gt; property to show what this icon means when user's mouse pointer hovers on the button.

&lt;ul&gt;
&lt;li&gt;I strongly advice to do this, since the infographic that we know as common sense might not be the same in other culture.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;We rotated the &lt;code&gt;Reply&lt;/code&gt; icon to 180 degrees. I did it because it felt right with this angle, but you can change it if you want.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Let's keep adding more icons.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


...

      {replyOf &amp;amp;&amp;amp; (
        &amp;lt;div className="flex gap-4 my-2 items-center justify-start"&amp;gt;
          &amp;lt;div className="flex items-center justify-start gap-2"&amp;gt;
            &amp;lt;ReplyIcon className="w-4 text-gray-600 rotate-180" /&amp;gt;
            &amp;lt;p className="font-extralight italic text-gray-600 text-sm"&amp;gt;
              {commentList.find((comment) =&amp;gt; comment.id === replyOf)?.payload ?? ""}
            &amp;lt;/p&amp;gt;
          &amp;lt;/div&amp;gt;
          &amp;lt;button onClick={() =&amp;gt; setReplyOf(null)} title="Cancel"&amp;gt;
            &amp;lt;XIcon className="w-4 text-gray-600" /&amp;gt;
          &amp;lt;/button&amp;gt;
        &amp;lt;/div&amp;gt;
      )}

...

      {comment.reply_of &amp;amp;&amp;amp; (
        &amp;lt;div className="flex items-center justify-start gap-2"&amp;gt;
          &amp;lt;ReplyIcon className="w-3 text-gray-600 rotate-180" /&amp;gt;
          &amp;lt;p className="font-extralight italic text-gray-600 text-xs"&amp;gt;
            {commentList.find((c) =&amp;gt; c.id === comment.reply_of)?.payload ?? ""}
          &amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
      )}

...

      &amp;lt;div className="flex gap-2"&amp;gt;
        {editComment.id === comment.id ? (
          &amp;lt;&amp;gt;
            &amp;lt;button
              type="button"
              onClick={confirmEdit}
              disabled={editComment.payload === comment.payload}
              title="Confirm"
            &amp;gt;
              &amp;lt;CheckCircleIcon
                className={`${
                  editComment.payload === comment.payload ? `text-gray-300` : `text-green-500`
                } w-6`}
              /&amp;gt;
            &amp;lt;/button&amp;gt;
            &amp;lt;button type="button" onClick={() =&amp;gt; setEditComment({ id: "", payload: "" })} title="Cancel"&amp;gt;
              &amp;lt;XCircleIcon className="w-6 text-gray-600" /&amp;gt;
            &amp;lt;/button&amp;gt;
          &amp;lt;/&amp;gt;
        ) : (
          &amp;lt;&amp;gt;

...



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

&lt;/div&gt;

&lt;p&gt;We've changed &lt;code&gt;Reply of&lt;/code&gt; to our &lt;code&gt;Reply&lt;/code&gt; icon, and changed &lt;code&gt;Confirm&lt;/code&gt; and &lt;code&gt;Cancel&lt;/code&gt; too.&lt;br&gt;
We didn't change much, but looks way better!&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Implementing SWR
&lt;/h2&gt;

&lt;p&gt;Finally, we will gonna fix the data fetching part, which right now user needs to reload everytime they modify(create, edit, delete) the comments.&lt;br&gt;
By using amazing library called &lt;a href="https://swr.vercel.app/" rel="noopener noreferrer"&gt;&lt;code&gt;SWR&lt;/code&gt;&lt;/a&gt;, we will gonna fix this problem and it will take our comment section's user experience into whole another level.&lt;/p&gt;

&lt;h3&gt;
  
  
  A brief overview of SWR
&lt;/h3&gt;

&lt;p&gt;There are numerous data-fetching libraries for Next.js, but one of the most popular and easiest to use is SWR.&lt;br&gt;
Here's a simple example from their official document page(modified a little bit for better understanding).&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;useSWR&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;swr&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;fetcher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&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;Profile&lt;/span&gt;&lt;span class="p"&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;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSWR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fetcher&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;failed&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;load&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;hello&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;!&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;This simple looking code does something beautiful.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It uses a hook called &lt;code&gt;useSWR&lt;/code&gt;, which takes 2 arguments:

&lt;ul&gt;
&lt;li&gt;An &lt;code&gt;url&lt;/code&gt; to fetch a data.&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;fetcher&lt;/code&gt; function that'll fetch from the given url.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Then you can just use the the &lt;code&gt;data&lt;/code&gt;, like you use a React state.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;See? It's so simple! Say goodbye to the hard days where you had to use several &lt;code&gt;useState&lt;/code&gt;s and &lt;code&gt;useEffect&lt;/code&gt;s to manipulate and update the&lt;br&gt;
remote date - which is complicated easy to make mistakes.&lt;/p&gt;

&lt;p&gt;As the name propose, the mechanism for this comes from a HTTP cache invalidation strategy called &lt;code&gt;stale-while-revalidate&lt;/code&gt;.&lt;br&gt;
Explaining the details about it is beyond our article, so better check out&lt;br&gt;
&lt;a href="https://developers.google.com/web/tools/workbox/modules/workbox-strategies#stale-while-revalidate" rel="noopener noreferrer"&gt;this link&lt;/a&gt; if you're interested.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Setting SWR, and refactor APIs
&lt;/h3&gt;

&lt;p&gt;Now let's install SWR with the command below.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ yarn add swr


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

&lt;/div&gt;

&lt;p&gt;And we will replace our old method of fetching data to new one, using &lt;code&gt;useSWR&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But first I am pretty sure our code need some refactoring, since we already have too much API-related code in our client-side file &lt;code&gt;index.tsx&lt;/code&gt;.&lt;br&gt;
Thankfully, Next.js provides us a &lt;code&gt;api&lt;/code&gt; directory inside &lt;code&gt;pages&lt;/code&gt; directory, which you can put all kinds of API codes.&lt;/p&gt;

&lt;p&gt;Let's make new file &lt;code&gt;pages/api/comments.ts&lt;/code&gt;, with the code down below.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import { createClient } from "@supabase/supabase-js";
import { NextApiRequest, NextApiResponse } from "next";

const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL + "";
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_KEY + "";

export const supabase = createClient(supabaseUrl, supabaseKey);

const CommentsApi = async (req: NextApiRequest, res: NextApiResponse) =&amp;gt; {
  switch (req.method) {
    // Get all comments
    case "GET":
      const { data: getData, error: getError } = await supabase.from("comments").select("*");
      if (getError) {
        return res.status(500).json({ message: getError.message });
      }
      return res.status(200).json(getData);
    // Add comment
    case "POST":
      const comment = req.body;
      const { data: postData, error: postError } = await supabase.from("comments").insert(comment);
      if (postError) {
        return res.status(500).json({ message: postError.message });
      }
      return res.status(200).json(postData);
    // Edit comment
    case "PATCH":
      const { commentId: editcommentId, payload } = req.body;
      const { data: patchData, error: patchError } = await supabase
        .from("comments")
        .update({ payload })
        .eq("id", editcommentId);
      if (patchError) {
        return res.status(500).json({ message: patchError.message });
      }
      return res.status(200).json(patchData);
    // Delete comment
    case "DELETE":
      const { comment_id: deleteCommentId } = req.query;
      if (typeof deleteCommentId === "string") {
        const { data: deleteData, error: deleteError } = await supabase
          .from("comments")
          .delete()
          .eq("id", deleteCommentId + "");
        if (deleteError) {
          return res.status(500).json({ message: deleteError.message });
        }
        return res.status(200).json(deleteData);
      }
    default:
      return res.status(405).json({
        message: "Method Not Allowed",
      });
  }
};

export default CommentsApi;


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

&lt;/div&gt;

&lt;p&gt;Now that's a lot of code all of a sudden! Don't worry, I'll explain one-by-one.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;CommentsApi&lt;/code&gt; function takes &lt;code&gt;req&lt;/code&gt; which is a request from the caller of this API,
and &lt;code&gt;res&lt;/code&gt; which is a response that we'll modify according to the request.&lt;/li&gt;
&lt;li&gt;Inside the function, we encounter a &lt;code&gt;switch&lt;/code&gt; condition filter with 5 &lt;code&gt;case&lt;/code&gt;s:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;case "GET"&lt;/code&gt;: This will be called for getting comments.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;case "POST"&lt;/code&gt;: This will be called for adding a comment.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;case "PATCH"&lt;/code&gt;: This will be called for editing a comment.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;case "DELETE"&lt;/code&gt;: This will be called for deleting a comment.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;default&lt;/code&gt;: This will omit error for unsupported methods.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;So what we've done is just moving the API related stuffs to this file.&lt;br&gt;
Each implementation inside the &lt;code&gt;case&lt;/code&gt; block is identical to ones we've written in &lt;code&gt;index.tsx&lt;/code&gt;.&lt;br&gt;
It uses &lt;code&gt;await supabase.from("comments").something(...)&lt;/code&gt; for every case.&lt;/p&gt;

&lt;p&gt;Now we've made our decent looking API code, how do we access to it? It's super-easy - Just fetch "/api/comments".&lt;/p&gt;

&lt;h3&gt;
  
  
  Replacing 'get comments'
&lt;/h3&gt;

&lt;p&gt;Now we are going to use our well organized &lt;code&gt;comments.ts&lt;/code&gt; API with &lt;code&gt;useSWR&lt;/code&gt; hook.&lt;br&gt;
First let's replace the old implementation of getting all the comments.&lt;/p&gt;

&lt;p&gt;Edit &amp;amp; Delete codes in &lt;code&gt;index.tsx&lt;/code&gt; with the code below.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


...

const fetcher = (url: string) =&amp;gt; fetch(url, { method: "GET" }).then((res) =&amp;gt; res.json());

const Home: NextPage = () =&amp;gt; {
  const { data: commentList, error: commentListError } = useSWR&amp;lt;CommentParams[]&amp;gt;("/api/comments", fetcher);
  /* Deleted
    const [commentList, setCommentList] = useState&amp;lt;CommentParams[]&amp;gt;([]);
    */
  const [comment, setComment] = useState&amp;lt;string&amp;gt;("");

...

    /* Deleted
    const getCommentList = async () =&amp;gt; {
        const { data, error } = await supabase.from("comments").select("*");
        if (!error &amp;amp;&amp;amp; data) {
            setCommentList(data);
        } else {
            setCommentList([]);
        }
    };

    useEffect(() =&amp;gt; {
        getCommentList();
    }, []);
    */

...

&amp;lt;div className="flex items-center justify-start gap-2"&amp;gt;
    &amp;lt;ReplyIcon className="w-4 text-gray-600 rotate-180" /&amp;gt;
    &amp;lt;p className="font-extralight italic text-gray-600 text-sm"&amp;gt;
        {commentList?.find((comment) =&amp;gt; comment.id === replyOf)?.payload ?? ""}
    &amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;

...

    {(commentList ?? [])
        .sort((a, b) =&amp;gt; {
            const aDate = new Date(a.created_at);

...

&amp;lt;div className="flex items-center justify-start gap-2"&amp;gt;
    &amp;lt;ReplyIcon className="w-3 text-gray-600 rotate-180" /&amp;gt;
    &amp;lt;p className="font-extralight italic text-gray-600 text-xs"&amp;gt;
        {commentList?.find((c) =&amp;gt; c.id === comment.reply_of)?.payload ?? ""}
    &amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;

...



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

&lt;/div&gt;

&lt;p&gt;Here's what happened:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Removed &lt;code&gt;commentList&lt;/code&gt; React State, &lt;code&gt;getCommentList&lt;/code&gt; function and &lt;code&gt;useEffect&lt;/code&gt; which was used to update comments when data is refetched.&lt;/li&gt;
&lt;li&gt;Replaced that part with a single line of code(or maybe 2 or 3 lines of code depending on your formatter), using &lt;code&gt;useSWR&lt;/code&gt; hook.

&lt;ul&gt;
&lt;li&gt;Same as an example from above, it contains &lt;code&gt;url("/api/comments")&lt;/code&gt; and &lt;code&gt;fetcher&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Since we are using &lt;code&gt;GET&lt;/code&gt; method with &lt;code&gt;fetch&lt;/code&gt;, our &lt;code&gt;GET&lt;/code&gt; case in &lt;code&gt;comments.ts&lt;/code&gt; is executed, which fetches the full comment list.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Added little &lt;code&gt;?&lt;/code&gt; and &lt;code&gt;?? []&lt;/code&gt; to &lt;code&gt;commentList&lt;/code&gt; when it's used for &lt;code&gt;find&lt;/code&gt;ing or &lt;code&gt;sort&lt;/code&gt;ing something.

&lt;ul&gt;
&lt;li&gt;A reason for this is because our data fetched from &lt;code&gt;useSWR&lt;/code&gt; is fallible, so it counts for the chance to being a &lt;code&gt;undefined&lt;/code&gt; for fetch failure.&lt;/li&gt;
&lt;li&gt;So we should inform the &lt;code&gt;find&lt;/code&gt; function with &lt;code&gt;?&lt;/code&gt; typing that it might contain the &lt;code&gt;undefined&lt;/code&gt; data.&lt;/li&gt;
&lt;li&gt;For &lt;code&gt;sort&lt;/code&gt; function, which doesn't tolerate &lt;code&gt;undefined&lt;/code&gt;, we have to hand over at least the empty array.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;We changed our code a lot, in a good way! Our comment section should work just the same.&lt;/p&gt;

&lt;h3&gt;
  
  
  Replacing "add comments"
&lt;/h3&gt;

&lt;p&gt;Next we'll replace 'add comment' feature. To do that we have to add another fetching function which will send a post request to our &lt;code&gt;comments.ts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Add &lt;code&gt;addCommentRequest&lt;/code&gt; function right after &lt;code&gt;fetcher&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


...

const fetcher = (url: string) =&amp;gt; fetch(url, { method: "GET" }).then((res) =&amp;gt; res.json());

const addCommentRequest = (url: string, data: any) =&amp;gt;
  fetch(url, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(data),
  }).then((res) =&amp;gt; res.json());

const Home: NextPage = () =&amp;gt; {

...



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

&lt;/div&gt;

&lt;p&gt;We stringify the comment data and post it. No difficult things to be explained.&lt;/p&gt;

&lt;p&gt;Now we'll use an interesting feature of SWR, called &lt;code&gt;mutate&lt;/code&gt;.&lt;br&gt;
Using &lt;code&gt;mutate&lt;/code&gt; we can modify the local cache of comment list before we even refetch the updated list from Supabase server.&lt;/p&gt;

&lt;p&gt;Let's discover the behaviour by just implementing it. Update the &lt;code&gt;onSubmit&lt;/code&gt; function, and edit our add comment form.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


...

  const onSubmit = async (event: FormEvent&amp;lt;HTMLFormElement&amp;gt;) =&amp;gt; {
    event.preventDefault();
    const newComment = {
      username: "hoonwee@email.com",
      payload: comment,
      reply_of: replyOf,
    };
    if (typeof commentList !== "undefined") {
      mutate("api/comments", [...commentList, newComment], false);
      const response = await addCommentRequest("api/comments", newComment);
      if (response[0].created_at) {
        mutate("api/comments");
        window.alert("Hooray!");
        setComment("")
      }
    }
  };

...

    &amp;lt;input
        onChange={onChange}
        value={comment}
        type="text"
        placeholder="Add a comment"
        className="p-2 border-b focus:border-b-gray-700 w-full outline-none"
    /&amp;gt;



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

&lt;/div&gt;

&lt;p&gt;We removed our old &lt;code&gt;await supabase...&lt;/code&gt; and replaced it with someting else:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We added two &lt;code&gt;mutate&lt;/code&gt; functions, which will refetch comment list that has added a new comment. But why two?

&lt;ul&gt;
&lt;li&gt;The first one won't actually refetch the data.
Instead it will &lt;strong&gt;assume&lt;/strong&gt; that adding a comment has succeeded, and &lt;strong&gt;pretend&lt;/strong&gt; that it refetched it by modifying the local cache of comment list.&lt;/li&gt;
&lt;li&gt;Now the second one will actually refetch the data, and compare between data modified and data refetched.
When it's equal, it does nothing. While there's any difference, it will rerender for the correct comment list.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;There's a &lt;code&gt;await addCommentRequest&lt;/code&gt; function call in between two &lt;code&gt;mutate&lt;/code&gt; functions. This will send a POST request to &lt;code&gt;comments.ts&lt;/code&gt; API, and return the response for the request.

&lt;ul&gt;
&lt;li&gt;Once succeeded adding a comment, it will return an array with single comment item.&lt;/li&gt;
&lt;li&gt;So if the response is an array, and the first element has &lt;code&gt;created_at&lt;/code&gt; field, the request is confirmed to be successful
so we'll use second &lt;code&gt;mutate&lt;/code&gt; function to compare with modified cache, and initalize the comment form with &lt;code&gt;setComment&lt;/code&gt; by setting an empty string.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Now with our powerful cache-modifying code, we can see updated comment list &lt;strong&gt;without reloading&lt;/strong&gt; the page!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fobbi20kv1al45hp5rwl5.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fobbi20kv1al45hp5rwl5.gif" alt="Without reloading"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Replacing "edit, delete comments"
&lt;/h3&gt;

&lt;p&gt;Let's practice using &lt;code&gt;mutate&lt;/code&gt; one more time, replacing old code for editing comment.&lt;br&gt;
Add &amp;amp; Replace code like down below.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


...

const editCommentRequest = (url: string, data: any) =&amp;gt;
  fetch(url, {
    method: "PATCH",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(data),
  }).then((res) =&amp;gt; res.json());

...

  const confirmEdit = async () =&amp;gt; {
    const editData = {
      payload: editComment.payload,
      commentId: editComment.id,
    };
    if (typeof commentList !== "undefined") {
      mutate(
        "api/comments",
        commentList.map((comment) =&amp;gt; {
          if (comment.id === editData.commentId) {
            return { ...comment, payload: editData.payload };
          }
        }),
        false
      );
      const response = await editCommentRequest("api/comments", editData);
      console.log(response);
      if (response[0].created_at) {
        mutate("api/comments");
        window.alert("Hooray!");
        setEditComment({ id: "", payload: "" });
      }
    }
  };

...



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

&lt;/div&gt;

&lt;p&gt;The flow is the same as we've done for &lt;code&gt;onSubmit&lt;/code&gt; function.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We first added a &lt;code&gt;editCommentRequest&lt;/code&gt; fetcher function.&lt;/li&gt;
&lt;li&gt;We added two &lt;code&gt;mutate&lt;/code&gt;, the &lt;em&gt;pretending&lt;/em&gt; one, and the &lt;em&gt;real one&lt;/em&gt; in &lt;code&gt;confirmEdit&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Before executing 2nd &lt;code&gt;mutate&lt;/code&gt;, we check if our request has succeeded with &lt;code&gt;response[0].created_at&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Finally we reset the &lt;code&gt;editComment&lt;/code&gt; state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's do the same work for deleting comments.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


...

const deleteCommentRequest = (url: string, id: string) =&amp;gt;
  fetch(`${url}?comment_id=${id}`, { method: "DELETE" }).then((res) =&amp;gt; res.json());

...

  const confirmDelete = async (id: string) =&amp;gt; {
    const ok = window.confirm("Delete comment?");
    if (ok &amp;amp;&amp;amp; typeof commentList !== "undefined") {
      mutate(
        "api/comments",
        commentList.filter((comment) =&amp;gt; comment.id !== id),
        false
      );
      const response = await deleteCommentRequest("api/comments", id);
      if (response[0].created_at) {
        mutate("api/comments");
        window.alert("Deleted Comment :)");
      }
    }
  };

...



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

&lt;/div&gt;

&lt;p&gt;No explaination needed! It's the same as we did for editing comment.&lt;/p&gt;

&lt;p&gt;Try editing &amp;amp; deleting comment, and check if the comment list changes properly without reloading.&lt;/p&gt;

&lt;h2&gt;
  
  
  And we are done!
&lt;/h2&gt;

&lt;p&gt;Congratulations! We successfully built a comments section with feature of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CRUD(Create, Read, Update, Delete)ing the comments, with &lt;strong&gt;Supabase&lt;/strong&gt; node library.&lt;/li&gt;
&lt;li&gt;Mutate UI without reloading with &lt;strong&gt;SWR&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Clean &amp;amp; understandable design, powered by &lt;strong&gt;TailwindCSS&lt;/strong&gt; and &lt;strong&gt;Hero Icons&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Although our comment section is awesome, there are some improvements to be made (do it by yourself!):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Replace browser's alert/confirm window to &lt;em&gt;toast UI&lt;/em&gt;. It will look better.&lt;/li&gt;
&lt;li&gt;Implement &lt;em&gt;user login&lt;/em&gt;, to make it usable in community service. You can make it from scratch, or...

&lt;ul&gt;
&lt;li&gt;Use Supabase's native user table&lt;/li&gt;
&lt;li&gt;Use &lt;a href="https://magic.link/" rel="noopener noreferrer"&gt;Magic&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Use &lt;a href="https://next-auth.js.org/" rel="noopener noreferrer"&gt;NextAuth&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Transform the replying system into &lt;em&gt;threads&lt;/em&gt;.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm5j77ebgt3w4mskraqz1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm5j77ebgt3w4mskraqz1.gif" alt="Toast UI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that's all for this series! Thank you so much for following up this far, and I hope to see you on my next blog post/series!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Until then, happy coding!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>typescript</category>
      <category>supabase</category>
      <category>swr</category>
    </item>
  </channel>
</rss>
