<?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: Paul Akinyemi</title>
    <description>The latest articles on DEV Community by Paul Akinyemi (@morgenstern2573).</description>
    <link>https://dev.to/morgenstern2573</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%2F423160%2Fffc527f0-d906-4771-8a97-1aa802c15644.png</url>
      <title>DEV Community: Paul Akinyemi</title>
      <link>https://dev.to/morgenstern2573</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/morgenstern2573"/>
    <language>en</language>
    <item>
      <title>Slog: Zero-dependency structured logging in Go</title>
      <dc:creator>Paul Akinyemi</dc:creator>
      <pubDate>Sat, 04 Nov 2023 09:37:05 +0000</pubDate>
      <link>https://dev.to/morgenstern2573/slog-zero-dependency-structured-logging-in-go-apf</link>
      <guid>https://dev.to/morgenstern2573/slog-zero-dependency-structured-logging-in-go-apf</guid>
      <description>&lt;p&gt;Structured logging is vital for all production deployments. It’s one of the few windows of insight you have into an app that isn’t running on your local machine. But what makes structured logging different from a call to &lt;code&gt;log.Println()&lt;/code&gt;, and why does it matter?&lt;/p&gt;

&lt;p&gt;Structured logs conform to a uniform format or share a specific structure, usually consisting of a collection of key-value pairs. The opposite of structured logging is free-form logging, with no consistent layout imposed on the log statements. Here’s an example:&lt;/p&gt;

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

&lt;span class="c"&gt;// free form&lt;/span&gt;
&lt;span class="s"&gt;"Error while running function"&lt;/span&gt;

&lt;span class="c"&gt;// structured log&lt;/span&gt;
&lt;span class="s"&gt;"message: 'Error while running function' level:'ERROR' time: '2023-09-17:09:52'"&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;So why do you need structured logs? Programs find it easier to process structured logs than free-form logs, which is a crucial advantage when you have many statements to comb through while looking for the reason for an error because it lets you perform operations like filtering and searching. &lt;/p&gt;

&lt;p&gt;In this article, you’ll learn how to add structured logging to your applications with the &lt;code&gt;slog&lt;/code&gt; package, which was added to the standard library in Go 1.21. To follow along, you’ll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go 1.21 or later installed on your machine&lt;/li&gt;
&lt;li&gt;A working knowledge of Go&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s it!&lt;/p&gt;

&lt;h2&gt;
  
  
  The basics of slog
&lt;/h2&gt;

&lt;p&gt;To begin with slog, you only need to understand three concepts: loggers, records, and handlers. Let’s start with loggers. The &lt;code&gt;logger&lt;/code&gt; type is the front end of slog. It's the type that owns the methods you call when you want to log something. When you call one of those methods, the &lt;code&gt;logger&lt;/code&gt; creates an instance of another type called a &lt;code&gt;record&lt;/code&gt;, whose only job is to store the necessary information to create a log.&lt;/p&gt;

&lt;p&gt;Finally, the &lt;code&gt;logger&lt;/code&gt; passes the &lt;code&gt;record&lt;/code&gt; to an instance of the &lt;code&gt;handler&lt;/code&gt; type. The &lt;code&gt;handler&lt;/code&gt; type is an interface, and it’s slog’s “backend”. The code that’s responsible for actually creating the log (writing it to &lt;code&gt;stdout&lt;/code&gt;, for example) lives in the &lt;code&gt;handler&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To recap, here’s a diagram illustrating the relationship between &lt;code&gt;logger&lt;/code&gt;s, &lt;code&gt;record&lt;/code&gt;s, and &lt;code&gt;handler&lt;/code&gt;s:&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%2Fjtd729alo9c24j84tw5q.jpeg" 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%2Fjtd729alo9c24j84tw5q.jpeg" alt="logging diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s look at some code. Assume you’re calling a function that returns an error, and if the error isn’t &lt;code&gt;nil&lt;/code&gt;, you want to log it:&lt;/p&gt;

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

&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// log the error&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;First, you need a &lt;code&gt;logger&lt;/code&gt;. &lt;code&gt;Slog&lt;/code&gt; provides a default logger that you can use by calling one of these top-level methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;slog.Debug()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;slog.Info()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;slog.Warn()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;slog.Error()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These methods all have the same function signature and produce the same output: a log statement whose severity matches the method's name. The &lt;code&gt;Debug&lt;/code&gt; method has the lowest severity, and the &lt;code&gt;Error&lt;/code&gt; method has the highest. Take a look at the signature of &lt;code&gt;slog.Error()&lt;/code&gt;:&lt;/p&gt;

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

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;It takes a string as its first argument and then an arbitrary list of key-value pairs. The keys must be strings, but the values can be any type. Calling &lt;code&gt;Error()&lt;/code&gt; usually looks like this:&lt;/p&gt;

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

&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;// log the error&lt;/span&gt;
  &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"doSomething returned an error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&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;Here, &lt;code&gt;"error"&lt;/code&gt; and &lt;code&gt;err&lt;/code&gt; form the key-value pair. This code would produce output like this:&lt;/p&gt;

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

2023/09/30 17:41:57 ERROR doSomething returned an error &lt;span class="nv"&gt;error&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"random error"&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;The timestamp was generated automatically by the default logger and is a part of every log record. What happens if you omit the key in the key-value pairs? This code:&lt;/p&gt;

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

&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"doSomething returned an error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Generates this output:&lt;/p&gt;

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

&lt;span class="m"&gt;2023&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;09&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;30&lt;/span&gt; &lt;span class="m"&gt;17&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="m"&gt;51&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="m"&gt;32&lt;/span&gt; &lt;span class="n"&gt;ERROR&lt;/span&gt; &lt;span class="n"&gt;doSomething&lt;/span&gt; &lt;span class="n"&gt;returned&lt;/span&gt; &lt;span class="n"&gt;an&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;BADKEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"random error"&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;BADKEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;Slog&lt;/code&gt; will still output the log in a structured, key-value format, but it will prefix all values without a key with a key called &lt;code&gt;!BADKEY&lt;/code&gt;, so you must be careful you don’t omit any keys from your key-value pairs.&lt;/p&gt;

&lt;p&gt;Slog also offers a more rigid way to create a log that removes the possibility of a &lt;code&gt;!BADKEY&lt;/code&gt; error: the &lt;code&gt;Attr&lt;/code&gt; type. A value of type &lt;code&gt;Attr&lt;/code&gt; represents a single key-value pair, and slog offers you several convenient methods to create an &lt;code&gt;Attr&lt;/code&gt;. There’s a method for each basic data type in Go, as well as a &lt;code&gt;slog.Any&lt;/code&gt; method that will allow you to use a value of any type to create an &lt;code&gt;Attr&lt;/code&gt;. To put this into context, this means that we can replace this method call:&lt;/p&gt;

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

&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"doSomething returned an error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;with an &lt;code&gt;Attr&lt;/code&gt; version:&lt;/p&gt;

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

&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"doSomething returned an error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;The convenience method you should use depends on the type of the  value on the key-value pair you want to turn into an &lt;code&gt;Attr&lt;/code&gt;. If you stick to &lt;code&gt;Attr&lt;/code&gt;s instead of the alternating value style, you’re less likely to omit a key from your log statement. Ultimately, the top-level methods place no restrictions on you - you’re free to use alternating keys and values, &lt;code&gt;Attr&lt;/code&gt;s, or to mix both, so it’s up to you to be careful with your log statements.&lt;/p&gt;

&lt;p&gt;So far, we’ve focused on the default logger, but if want more control over the output format, you need a create a custom logger. &lt;code&gt;Slog&lt;/code&gt; provides the &lt;code&gt;slog.New&lt;/code&gt; function for this. &lt;code&gt;slog.New&lt;/code&gt; takes a value that satisfies the &lt;code&gt;handler&lt;/code&gt;  interface and returns a &lt;code&gt;logger&lt;/code&gt; that uses the handler you provided to it. Slog provides two types of handlers for you: a text handler and a JSON handler. You can create new loggers like this:&lt;/p&gt;

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

&lt;span class="n"&gt;textLogger&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewTextHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c"&gt;// or&lt;/span&gt;
&lt;span class="n"&gt;jsonLogger&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewJSONHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;NewTextHandler&lt;/code&gt; and &lt;code&gt;NewJSONHandler&lt;/code&gt; methods take an &lt;code&gt;io.Writer&lt;/code&gt;  and a configuration struct as arguments, and return values that satisfy the &lt;code&gt;handler&lt;/code&gt;  interface. These loggers have the same methods as the default logger, but because they use different handlers, the output they produce is different. Both loggers produce structured, key-value output, but the specific format is different. &lt;code&gt;textLogger&lt;/code&gt; will produce a string of regular text,  but &lt;code&gt;jsonLogger&lt;/code&gt; will produce JSON output. Here’s an example. This code:&lt;/p&gt;

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

&lt;span class="n"&gt;textLogger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"an error occurred"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;jsonLogger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"an error occurred"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Produces this output:&lt;/p&gt;

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

// text handler output
&lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2023-10-02T08:12:43.634+01:00 &lt;span class="nv"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ERROR &lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"doSomething returned an error"&lt;/span&gt; &lt;span class="nv"&gt;error&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"random error"&lt;/span&gt;

// JSON handler output
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"time"&lt;/span&gt;:&lt;span class="s2"&gt;"2023-10-02T08:12:43.634180321+01:00"&lt;/span&gt;,&lt;span class="s2"&gt;"level"&lt;/span&gt;:&lt;span class="s2"&gt;"ERROR"&lt;/span&gt;,&lt;span class="s2"&gt;"msg"&lt;/span&gt;:&lt;span class="s2"&gt;"doSomething returned an error"&lt;/span&gt;,&lt;span class="s2"&gt;"error"&lt;/span&gt;:&lt;span class="s2"&gt;"random error"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Slog allows you to replace the default package logger with a custom logger. Calling &lt;code&gt;slog.SetDefault(jsonLogger)&lt;/code&gt;, for example, will cause the top-level methods like &lt;code&gt;slog.Info()&lt;/code&gt; to use the &lt;code&gt;jsonLogger&lt;/code&gt; instead of the default logger. &lt;/p&gt;

&lt;h2&gt;
  
  
  Customizing loggers
&lt;/h2&gt;

&lt;p&gt;Slog gives you some options to change the behavior of your loggers. For example, if you have a key-value pair you want to include in all your logs like a user ID, you can use &lt;code&gt;logger.With()&lt;/code&gt; to create a new logger that will always include that pair in your log messages. Here’s an example:&lt;/p&gt;

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

&lt;span class="n"&gt;authLogger&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;jsonLogger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;With&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"userId"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;authLogger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user action failed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;authLogger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user logged in!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;And here’s the output:&lt;/p&gt;

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

&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"time"&lt;/span&gt;:&lt;span class="s2"&gt;"2023-10-02T11:04:40.852885792+01:00"&lt;/span&gt;,&lt;span class="s2"&gt;"level"&lt;/span&gt;:&lt;span class="s2"&gt;"ERROR"&lt;/span&gt;,&lt;span class="s2"&gt;"msg"&lt;/span&gt;:&lt;span class="s2"&gt;"user action failed"&lt;/span&gt;,&lt;span class="s2"&gt;"userId"&lt;/span&gt;:1,&lt;span class="s2"&gt;"error"&lt;/span&gt;:&lt;span class="s2"&gt;"random error"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"time"&lt;/span&gt;:&lt;span class="s2"&gt;"2023-10-02T11:04:40.852935987+01:00"&lt;/span&gt;,&lt;span class="s2"&gt;"level"&lt;/span&gt;:&lt;span class="s2"&gt;"INFO"&lt;/span&gt;,&lt;span class="s2"&gt;"msg"&lt;/span&gt;:&lt;span class="s2"&gt;"user logged in!"&lt;/span&gt;,&lt;span class="s2"&gt;"userId"&lt;/span&gt;:1&lt;span class="o"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Note how both logs contain the &lt;code&gt;userId:1&lt;/code&gt; pair, even though it wasn’t added to the calls to either &lt;code&gt;Error&lt;/code&gt; or &lt;code&gt;Info&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Slog allows you to group key-value pairs using the &lt;code&gt;slog.Group&lt;/code&gt; function. This lets you create one value that represents multiple key-value pairs so you can save space when you want to use the group of pairs. To create a group, you need to pass a group name and one or more key-value pairs  to &lt;code&gt;slog.Group&lt;/code&gt;. Here’s an example:&lt;/p&gt;

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

&lt;span class="n"&gt;userInfo&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user-info"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;"ip-address"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"180.80.80.5"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;This code creates a group named &lt;code&gt;user-info&lt;/code&gt;, with the pairs &lt;code&gt;id:5, ip-address: "180.80.80.5"&lt;/code&gt;. You can then use the group like this:&lt;/p&gt;

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

&lt;span class="n"&gt;jsonLogger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user action failed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userInfo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;To get this result:&lt;/p&gt;

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

&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"time"&lt;/span&gt;:&lt;span class="s2"&gt;"2023-10-02T11:41:31.521075924+01:00"&lt;/span&gt;,&lt;span class="s2"&gt;"level"&lt;/span&gt;:&lt;span class="s2"&gt;"ERROR"&lt;/span&gt;,&lt;span class="s2"&gt;"msg"&lt;/span&gt;:&lt;span class="s2"&gt;"user action failed"&lt;/span&gt;,&lt;span class="s2"&gt;"user-info"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:5,&lt;span class="s2"&gt;"ip-address"&lt;/span&gt;:&lt;span class="s2"&gt;"180.80.80.5"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="s2"&gt;"error"&lt;/span&gt;:&lt;span class="s2"&gt;"random error"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;You can also pass a group as an argument to &lt;code&gt;logger.With&lt;/code&gt; :&lt;/p&gt;

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

&lt;span class="n"&gt;authLogger&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;jsonLogger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;With&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userInfo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;authLogger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user action failed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;To get this result: &lt;/p&gt;

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

&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"time"&lt;/span&gt;:&lt;span class="s2"&gt;"2023-10-02T11:48:10.436603233+01:00"&lt;/span&gt;,&lt;span class="s2"&gt;"level"&lt;/span&gt;:&lt;span class="s2"&gt;"ERROR"&lt;/span&gt;,&lt;span class="s2"&gt;"msg"&lt;/span&gt;:&lt;span class="s2"&gt;"user action failed"&lt;/span&gt;,&lt;span class="s2"&gt;"user-info"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:5,&lt;span class="s2"&gt;"ip-address"&lt;/span&gt;:&lt;span class="s2"&gt;"180.80.80.5"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="s2"&gt;"error"&lt;/span&gt;:&lt;span class="s2"&gt;"random error"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Finally, you can use the &lt;code&gt;logger.WithGroup()&lt;/code&gt; method to prefix all key-value pairs the logger will output with a group name. This is useful in cases where other parts of your codebase might create log statements that have the same keys as the ones you’re using. The prefix allows you to identify which part of the code produced a particular log statement, even when the code uses the same keys. Here’s how it works. This code:&lt;/p&gt;

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

&lt;span class="n"&gt;authLogger&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;jsonLogger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"auth"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;authLogger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user action failed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Produces this output:&lt;/p&gt;

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

&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"time"&lt;/span&gt;:&lt;span class="s2"&gt;"2023-10-02T14:42:41.186099512+01:00"&lt;/span&gt;,&lt;span class="s2"&gt;"level"&lt;/span&gt;:&lt;span class="s2"&gt;"ERROR"&lt;/span&gt;,&lt;span class="s2"&gt;"msg"&lt;/span&gt;:&lt;span class="s2"&gt;"user action failed"&lt;/span&gt;,&lt;span class="s2"&gt;"auth"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;:&lt;span class="s2"&gt;"random error"&lt;/span&gt;&lt;span class="o"&gt;}}&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Customizing handlers
&lt;/h2&gt;

&lt;p&gt;Another way to control the output of your log statements is by customizing your handlers. With the handlers slog gives you, that’s done by passing them a &lt;code&gt;HandlerOptions&lt;/code&gt; struct. This is the type definition of &lt;code&gt;HandlerOptions&lt;/code&gt;: &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;HandlerOptions&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;AddSource&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;
  &lt;span class="n"&gt;Level&lt;/span&gt; &lt;span class="n"&gt;Leveler&lt;/span&gt;
  &lt;span class="n"&gt;ReplaceAttr&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;groups&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;Attr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Attr&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;A &lt;code&gt;HandlerOptions&lt;/code&gt; where &lt;code&gt;AddSource&lt;/code&gt; is true will make the log include a &lt;code&gt;source&lt;/code&gt; key whose value is the filename, function, and line number from which the log originated. Here’s an example:&lt;/p&gt;

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

&lt;span class="n"&gt;jsonLogger&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewJSONHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandlerOptions&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;AddSource&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;
&lt;span class="n"&gt;jsonLogger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"doSomething returned an error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;And the output:&lt;/p&gt;

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

&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"time"&lt;/span&gt;:&lt;span class="s2"&gt;"2023-10-02T15:04:59.905411319+01:00"&lt;/span&gt;,&lt;span class="s2"&gt;"level"&lt;/span&gt;:&lt;span class="s2"&gt;"ERROR"&lt;/span&gt;,&lt;span class="s2"&gt;"source"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"function"&lt;/span&gt;:&lt;span class="s2"&gt;"main.main"&lt;/span&gt;,&lt;span class="s2"&gt;"file"&lt;/span&gt;:&lt;span class="s2"&gt;"/home/Documents/dev/slog-sandbox/main.go"&lt;/span&gt;,&lt;span class="s2"&gt;"line"&lt;/span&gt;:21&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="s2"&gt;"msg"&lt;/span&gt;:&lt;span class="s2"&gt;"doSomething returned an error"&lt;/span&gt;,&lt;span class="s2"&gt;"error"&lt;/span&gt;:&lt;span class="s2"&gt;"random error"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;A note on enabling AddSource: If you have &lt;code&gt;AddSource&lt;/code&gt; enabled and you wrap your log statement in another function, it will cause the location reported by &lt;code&gt;AddSource&lt;/code&gt; to be off. Here’s an example:&lt;/p&gt;

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

&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s"&gt;"errors"&lt;/span&gt;
  &lt;span class="s"&gt;"log/slog"&lt;/span&gt;
  &lt;span class="s"&gt;"os"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewJSONHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandlerOptions&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;AddSource&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;
  &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"random error"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"an error occurred"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&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 code returns this output:&lt;/p&gt;

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

&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"time"&lt;/span&gt;:&lt;span class="s2"&gt;"2023-10-02T17:41:40.031790944+01:00"&lt;/span&gt;,&lt;span class="s2"&gt;"level"&lt;/span&gt;:&lt;span class="s2"&gt;"ERROR"&lt;/span&gt;,&lt;span class="s2"&gt;"source"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"function"&lt;/span&gt;:&lt;span class="s2"&gt;"main.wrapper"&lt;/span&gt;,&lt;span class="s2"&gt;"file"&lt;/span&gt;:&lt;span class="s2"&gt;"/home/Documents/dev/slog-sandbox/main.go"&lt;/span&gt;,&lt;span class="s2"&gt;"line"&lt;/span&gt;:13&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="s2"&gt;"msg"&lt;/span&gt;:&lt;span class="s2"&gt;"an error occurred"&lt;/span&gt;,&lt;span class="s2"&gt;"error"&lt;/span&gt;:&lt;span class="s2"&gt;"random error"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;And you can see the location of the log is reported as inside &lt;code&gt;wrapper&lt;/code&gt; when we want the location where &lt;code&gt;wrapper&lt;/code&gt; was called inside &lt;code&gt;main&lt;/code&gt;. So what’s the fix? You have to create a record yourself with the correct source information using the &lt;code&gt;runtime&lt;/code&gt; package. The documentation provides &lt;a href="https://pkg.go.dev/log/slog#example-package-Wrapping" rel="noopener noreferrer"&gt;an example&lt;/a&gt; of such a function.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Level&lt;/code&gt; field governs the minimum severity of the logs the Handler will accept. Any log statements with a severity less than the &lt;code&gt;Level&lt;/code&gt; field will be ignored by the handler and not logged. But what’s up with the &lt;code&gt;Leveler&lt;/code&gt; type? &lt;code&gt;Leveler&lt;/code&gt; is an interface with this definition:&lt;/p&gt;

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

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Leveler&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Level&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;Level&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;And this is the definition of type level:&lt;/p&gt;

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

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Level&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;The higher the value of the &lt;code&gt;Level&lt;/code&gt;, the more severe it is. The &lt;code&gt;Level&lt;/code&gt; field is a &lt;code&gt;Leveler&lt;/code&gt; type to allow you to provide a value that can change the &lt;code&gt;Level&lt;/code&gt; it returns - you can read about that in the docs, but if you don’t need that, the &lt;code&gt;Level&lt;/code&gt; type also satisfies the &lt;code&gt;Leveler&lt;/code&gt; interface, so you can pass a static &lt;code&gt;Level&lt;/code&gt; instead. Like so:&lt;/p&gt;

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

&lt;span class="n"&gt;jsonLogger&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewJSONHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandlerOptions&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;AddSource&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Level&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Level&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)}))&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;These are the values for slog’s levels: &lt;/p&gt;

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

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;LevelDebug&lt;/span&gt; &lt;span class="n"&gt;Level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;
  &lt;span class="n"&gt;LevelInfo&lt;/span&gt;  &lt;span class="n"&gt;Level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
  &lt;span class="n"&gt;LevelWarn&lt;/span&gt;  &lt;span class="n"&gt;Level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;
  &lt;span class="n"&gt;LevelError&lt;/span&gt; &lt;span class="n"&gt;Level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Which means that &lt;code&gt;jsonLogger&lt;/code&gt; will now log only &lt;code&gt;Warn&lt;/code&gt; and &lt;code&gt;Error&lt;/code&gt; statements, and ignore &lt;code&gt;Info&lt;/code&gt; and &lt;code&gt;Debug&lt;/code&gt;. Here's a demonstration:&lt;/p&gt;

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

&lt;span class="n"&gt;jsonLogger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"doSomething returned an error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;jsonLogger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"doSomething returned an error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;jsonLogger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"doSomething returned an error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;jsonLogger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"doSomething returned an error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Will output:&lt;/p&gt;

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

&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"time"&lt;/span&gt;:&lt;span class="s2"&gt;"2023-10-02T17:21:58.740882837+01:00"&lt;/span&gt;,&lt;span class="s2"&gt;"level"&lt;/span&gt;:&lt;span class="s2"&gt;"WARN"&lt;/span&gt;,&lt;span class="s2"&gt;"source"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"function"&lt;/span&gt;:&lt;span class="s2"&gt;"main.main"&lt;/span&gt;,&lt;span class="s2"&gt;"file"&lt;/span&gt;:&lt;span class="s2"&gt;"/home/Documents/dev/slog-sandbox/main.go"&lt;/span&gt;,&lt;span class="s2"&gt;"line"&lt;/span&gt;:23&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="s2"&gt;"msg"&lt;/span&gt;:&lt;span class="s2"&gt;"doSomething returned an error"&lt;/span&gt;,&lt;span class="s2"&gt;"error"&lt;/span&gt;:&lt;span class="s2"&gt;"random error"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"time"&lt;/span&gt;:&lt;span class="s2"&gt;"2023-10-02T17:21:58.740946987+01:00"&lt;/span&gt;,&lt;span class="s2"&gt;"level"&lt;/span&gt;:&lt;span class="s2"&gt;"ERROR"&lt;/span&gt;,&lt;span class="s2"&gt;"source"&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"function"&lt;/span&gt;:&lt;span class="s2"&gt;"main.main"&lt;/span&gt;,&lt;span class="s2"&gt;"file"&lt;/span&gt;:&lt;span class="s2"&gt;"/home/Documents/dev/slog-sandbox/main.go"&lt;/span&gt;,&lt;span class="s2"&gt;"line"&lt;/span&gt;:24&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="s2"&gt;"msg"&lt;/span&gt;:&lt;span class="s2"&gt;"doSomething returned an error"&lt;/span&gt;,&lt;span class="s2"&gt;"error"&lt;/span&gt;:&lt;span class="s2"&gt;"random error"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;We won’t delve into the &lt;code&gt;ReplaceAttr&lt;/code&gt; function, but its purpose is to allow you to transform both the keys and values of every key-value pair. You can use &lt;code&gt;ReplaceAttr&lt;/code&gt; to remove key-value pairs, change the key names, or modify the value in any way you desire.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimizing for performance
&lt;/h2&gt;

&lt;p&gt;If you do a quick Google search, you’ll see the primary culprit for performance issues when logging in Golang is something called allocation. Allocation is the process of reserving computer memory for your program’s use. That memory can be reserved from the &lt;a href="https://en.wikipedia.org/wiki/Call_stack" rel="noopener noreferrer"&gt;stack&lt;/a&gt; or the &lt;a href="https://en.wikipedia.org/wiki/Memory_management#HEAP" rel="noopener noreferrer"&gt;heap&lt;/a&gt;. Allocating memory on the stack is straightforward,  but allocating memory on the heap is much more complicated, and thus consumes more computational power and time.&lt;/p&gt;

&lt;p&gt;So when people say allocations are making a server slower, they mean that executing some log statements is forcing the program to do a lot of heap allocation, and thus slowing it down. Now that you understand what allocation is and why it’s bad for performance, how can you minimize allocations while using slog?&lt;/p&gt;

&lt;p&gt;The first thing you should do is use &lt;code&gt;Attrs&lt;/code&gt; in all your log statements. Creating an &lt;code&gt;Attr&lt;/code&gt; allows your log statement to avoid an allocation when executing for that key-value pair. Taking it one step further, slog provides the &lt;code&gt;Logger.LogAttrs()&lt;/code&gt; method. The fact that it accepts only &lt;code&gt;Attrs&lt;/code&gt; means that it can avoid allocations even further. According to &lt;a href="https://pkg.go.dev/log/slog#hdr-Attrs_and_Values" rel="noopener noreferrer"&gt;the documentation&lt;/a&gt;, &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The call &lt;code&gt;logger.LogAttrs(ctx, slog.LevelInfo, "hello", slog.Int("count", 3))&lt;/code&gt;&lt;br&gt;
is the most efficient way to achieve the same output as &lt;code&gt;slog.Info("hello", "count", 3)&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;logAttrs&lt;/code&gt; requires a &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-use-contexts-in-go" rel="noopener noreferrer"&gt;context&lt;/a&gt; as its first argument, which is a value that contains information about the code’s environment that can be passed between functions. Its other arguments are a &lt;code&gt;Level&lt;/code&gt; for the log statement, a string, and then an arbitrarily long list of &lt;code&gt;Attrs&lt;/code&gt;.  You can find a list of other performance considerations &lt;a href="https://pkg.go.dev/log/slog#hdr-Performance_considerations" rel="noopener noreferrer"&gt;in the documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extending slog
&lt;/h2&gt;

&lt;p&gt;Because slog is split into a front end and a back end, it’s possible to further customize slog while keeping its interface the same, by changing the back end. While slog only provides two built-in handlers, because &lt;code&gt;handler&lt;/code&gt; is an interface, you can write a custom implementation to have complete control over the output. &lt;/p&gt;

&lt;p&gt;A guide covering how to write custom handlers is out of scope for this post, but you can find one such guide written by the author of slog &lt;a href="https://github.com/golang/example/blob/master/slog-handler-guide/README.md" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Thankfully, you don’t need to write a handler from scratch to use one. There are several community-contributed handlers, including &lt;a href="https://github.com/lmittmann/tint" rel="noopener noreferrer"&gt;handlers&lt;/a&gt; that allow you to output colored logs, and a &lt;a href="https://github.com/samber/slog-sampling" rel="noopener noreferrer"&gt;handler&lt;/a&gt; that lets you implement sampling. You can find a full list &lt;a href="https://github.com/golang/go/wiki/Resources-for-slog" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Context in slog
&lt;/h2&gt;

&lt;p&gt;Sometimes, &lt;code&gt;handler&lt;/code&gt;s may need access to the  &lt;code&gt;context&lt;/code&gt; of the function where the log statement was called, so slog provides methods that allow you to pass that context as part of the log statement. &lt;br&gt;
The &lt;code&gt;Logger.LogAttr()&lt;/code&gt;  method takes a context as the first parameter, and all the log statements have alternate versions that let you provide a context. That means that there’s a &lt;code&gt;logger.{Level}Context&lt;/code&gt; method for each of slog’s built-in severity levels.&lt;/p&gt;

&lt;h2&gt;
  
  
  Slog vs Zap vs ZeroLog
&lt;/h2&gt;

&lt;p&gt;To help you decide whether you’d want to use slog in your next project, here’s a table comparing slog to some of the most popular alternatives for structured logging.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Slog (with built-in handlers)&lt;/th&gt;
&lt;th&gt;Zap&lt;/th&gt;
&lt;th&gt;ZeroLog&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Community and Support&lt;/td&gt;
&lt;td&gt;Part of Go’s stdlib&lt;/td&gt;
&lt;td&gt;Owned by Uber&lt;/td&gt;
&lt;td&gt;No org. backing it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance&lt;/td&gt;
&lt;td&gt;Slower than Zap&lt;/td&gt;
&lt;td&gt;Slower than ZeroLog&lt;/td&gt;
&lt;td&gt;Fatest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extensibility&lt;/td&gt;
&lt;td&gt;Most extensible&lt;/td&gt;
&lt;td&gt;Decently extensible&lt;/td&gt;
&lt;td&gt;Least extensible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output Format&lt;/td&gt;
&lt;td&gt;Plain text or JSON&lt;/td&gt;
&lt;td&gt;JSON&lt;/td&gt;
&lt;td&gt;JSON or CBOR only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You can find benchmarks for slog, zap, and zeroLog &lt;a href="https://go.googlesource.com/exp/+/refs/heads/master/slog/benchmarks" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;Structured logging is a feature all production applications should have, to provide the developer as much insight into the running of the application as possible. Slog is a new addition to the Golang standard library that provides convenient, efficient, and easily extensible structured logging. &lt;/p&gt;

&lt;p&gt;Slog can meet the most common use cases for structured logging with its built-in handlers, but if you need more control or have a niche requirement, it allows you to use third-party handlers, or write your own. I hope you enjoyed the article, and happy coding! &lt;/p&gt;

</description>
      <category>go</category>
      <category>tutorial</category>
      <category>backend</category>
      <category>programming</category>
    </item>
    <item>
      <title>Solving the "session store not found" error while testing handlers in Echo</title>
      <dc:creator>Paul Akinyemi</dc:creator>
      <pubDate>Sun, 30 Jul 2023 14:57:27 +0000</pubDate>
      <link>https://dev.to/morgenstern2573/solving-the-session-store-not-found-error-while-testing-handlers-in-echo-3gcf</link>
      <guid>https://dev.to/morgenstern2573/solving-the-session-store-not-found-error-while-testing-handlers-in-echo-3gcf</guid>
      <description>&lt;p&gt;Just a quick tip to make the solution for this easier to find. If you're having this problem, you most likely have code similar to this in your test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Middleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sessions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewCookieStore&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sessionKey&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;sess&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user-session"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And your call to &lt;code&gt;session.Get()&lt;/code&gt; returns the "session store not found" error. Here's what the source code for &lt;code&gt;Get()&lt;/code&gt; looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"_session_store"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// Get returns a named session.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sessions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%q session store not found"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sessions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your call to &lt;code&gt;Get()&lt;/code&gt; throws an error because &lt;code&gt;Get()&lt;/code&gt; checks the context you pass to it for the &lt;code&gt;session_store&lt;/code&gt; key, but the context returned by &lt;code&gt;e.NewContext()&lt;/code&gt; is completely blank, unlike the context echo passes to your handlers when serving HTTP requests. &lt;/p&gt;

&lt;p&gt;To solve the error, before you make any calls to &lt;code&gt;Get()&lt;/code&gt; you need to set a cookie store on the context returned by &lt;code&gt;e.NewContext()&lt;/code&gt; with the key &lt;code&gt;_session_store&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;You can do that like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"_session_store"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sessions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewCookieStore&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sessionKey&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="n"&gt;sess&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user-session"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that will take care of the error.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to Integrate FormKit and Bootstrap</title>
      <dc:creator>Paul Akinyemi</dc:creator>
      <pubDate>Mon, 16 Jan 2023 11:26:31 +0000</pubDate>
      <link>https://dev.to/morgenstern2573/how-to-integrate-formkit-and-bootstrap-2571</link>
      <guid>https://dev.to/morgenstern2573/how-to-integrate-formkit-and-bootstrap-2571</guid>
      <description>&lt;p&gt;FormKit is powerful, but for the most part, it leaves styling your forms to you. In this article, you'll learn how to style your FormKit forms with Bootstrap. &lt;/p&gt;

&lt;p&gt;You’ll discover how to style forms individually, and how you can create an application-wide form theme that is easy to reuse in other projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites and Assumptions
&lt;/h2&gt;

&lt;p&gt;This guide assumes the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have a working knowledge of &lt;a href="https://formkit.com/getting-started/what-is-formkit" rel="noopener noreferrer"&gt;FormKit&lt;/a&gt;, &lt;a href="https://getbootstrap.com/docs/5.3/getting-started/introduction/" rel="noopener noreferrer"&gt;Bootstrap&lt;/a&gt;, and npm.&lt;/li&gt;
&lt;li&gt;You understand the Vue 3 Composition API.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;If you want to follow along with this guide, you'll need to do some setup work first.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;

&lt;p&gt;Your first task is to create a Vue project with Vite. Start by navigating to the location you want your project to live in the terminal. Next, run the following command in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm create vite@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;create-vite&lt;/code&gt; command will ask you a few questions. Answer them as shown in the screenshot below:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuc1y6a1koypd391xp23e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuc1y6a1koypd391xp23e.png" alt="command line screenshot" width="800" height="63"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then &lt;code&gt;cd&lt;/code&gt; into the project folder and install the necessary dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;formkit-bootstrap
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, start the dev server by running: &lt;code&gt;npm run dev&lt;/code&gt;. Visit &lt;a href="http://localhost:5173" rel="noopener noreferrer"&gt;localhost:5173&lt;/a&gt; to see your application.&lt;/p&gt;

&lt;p&gt;Now that you've set up your base Vue project, it's time to install the other dependencies you'll need: FormKit, Bootstrap, and Sass. Open another terminal window and run this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i bootstrap sass @formkit/vue @formkit/themes @formkit/utils
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your last setup task is to clean up boilerplate you don't need. At this point, your project directory should have a structure that looks like this:&lt;/p&gt;

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

&lt;p&gt;Delete the &lt;code&gt;src/assets&lt;/code&gt; folder, &lt;code&gt;src/components/HelloWorld.vue&lt;/code&gt;, and &lt;code&gt;src/style.css&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Replace the content of &lt;code&gt;src/App.vue&lt;/code&gt; with an empty template and script like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, import all Bootstrap's classes by creating a file called &lt;code&gt;styles.scss&lt;/code&gt; in &lt;code&gt;src&lt;/code&gt;, with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sass"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Import all of Bootstrap's CSS&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s"&gt;"../node_modules/bootstrap/scss/bootstrap";&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you only want to import some of Bootstrap's CSS, you can find the &lt;a href="https://getbootstrap.com/docs/5.3/customize/sass/#importing" rel="noopener noreferrer"&gt;relevant import statements&lt;/a&gt; for the parts you want in their docs.&lt;/p&gt;

&lt;p&gt;Then import the styling and register Formkit with your Vue app by replacing the content of &lt;code&gt;src/main.js&lt;/code&gt; with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createApp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defaultConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@formkit/vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./styles.scss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;App&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;./App.vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nf"&gt;createApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defaultConfig&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you're done with the setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Styling forms individually
&lt;/h2&gt;

&lt;p&gt;Next, you'll learn how to style individual forms separately.&lt;/p&gt;

&lt;p&gt;Start by creating a basic, unstyled login form by adding the following code to the &lt;code&gt;template&lt;/code&gt; of &lt;code&gt;src/App.vue&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt; &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"row m-0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;FormKit&lt;/span&gt;
    &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"form"&lt;/span&gt;
    &lt;span class="na"&gt;submit-label=&lt;/span&gt;&lt;span class="s"&gt;"Log in"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mb-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; Log In &lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;FormKit&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;
             &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"john@xyz.com"&lt;/span&gt;
             &lt;span class="na"&gt;label=&lt;/span&gt;&lt;span class="s"&gt;"Email"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&amp;lt;/FormKit&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;FormKit&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt;
             &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"***********"&lt;/span&gt;
             &lt;span class="na"&gt;label=&lt;/span&gt;&lt;span class="s"&gt;"Password"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&amp;lt;/FormKit&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;/FormKit&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, your Vue application should render a form that looks like this:&lt;/p&gt;

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

&lt;p&gt;So, how will you style this? FormKit provides &lt;a href="https://formkit.com/essentials/styling#outer-attributes" rel="noopener noreferrer"&gt;several methods&lt;/a&gt; for styling form inputs, from writing CSS that targets the inputs with attribute values to applying classes to inputs via props.&lt;/p&gt;

&lt;p&gt;Before you go on, you need to understand some FormKit jargon: input sections. A section is a specific part of the rendered HTML of a FormKit input. You can find a list and description of the sections for each input in FormKit’s docs for the input. &lt;/p&gt;

&lt;p&gt;In general, the &lt;code&gt;outer&lt;/code&gt; section refers to an input's outermost wrapper, the &lt;code&gt;input&lt;/code&gt; section refers to the core rendered input, and the &lt;code&gt;label&lt;/code&gt; section targets the generated label.  &lt;/p&gt;

&lt;p&gt;The approach to styling you'll use here is to use &lt;code&gt;{section}-class&lt;/code&gt; props to apply classes to the form inputs. &lt;/p&gt;

&lt;p&gt;Here's what the code for the login form looks like when you style it this way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;FormKit&lt;/span&gt;
    &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"form"&lt;/span&gt;
    &lt;span class="na"&gt;form-class=&lt;/span&gt;&lt;span class="s"&gt;"col-md-4 col-lg-3 mt-5 mx-auto 
                p-5 border rounded"&lt;/span&gt;
    &lt;span class="na"&gt;:submit-attrs=&lt;/span&gt;&lt;span class="s"&gt;"{
      inputClass: 'btn btn-primary'
    }"&lt;/span&gt;
    &lt;span class="na"&gt;submit-label=&lt;/span&gt;&lt;span class="s"&gt;"Log in"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mb-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; Log In &lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;FormKit&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;
             &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"john@xyz.com"&lt;/span&gt;
             &lt;span class="na"&gt;outer-class=&lt;/span&gt;&lt;span class="s"&gt;"mb-3"&lt;/span&gt;
             &lt;span class="na"&gt;input-class=&lt;/span&gt;&lt;span class="s"&gt;"form-control"&lt;/span&gt;
             &lt;span class="na"&gt;label=&lt;/span&gt;&lt;span class="s"&gt;"Email"&lt;/span&gt;
             &lt;span class="na"&gt;label-class=&lt;/span&gt;&lt;span class="s"&gt;"form-label"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&amp;lt;/FormKit&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;FormKit&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt;
             &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"***********"&lt;/span&gt;
             &lt;span class="na"&gt;outer-class=&lt;/span&gt;&lt;span class="s"&gt;"mb-3"&lt;/span&gt;
             &lt;span class="na"&gt;input-class=&lt;/span&gt;&lt;span class="s"&gt;"form-control"&lt;/span&gt;
             &lt;span class="na"&gt;label=&lt;/span&gt;&lt;span class="s"&gt;"Password"&lt;/span&gt;
             &lt;span class="na"&gt;label-class=&lt;/span&gt;&lt;span class="s"&gt;"form-label"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&amp;lt;/FormKit&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;/FormKit&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since you already imported Bootstrap's classes during setup, all you need to style the form is the name of the classes you want to apply to the input.&lt;/p&gt;

&lt;p&gt;The code applies classes to sections of each input with &lt;code&gt;{section}-class&lt;/code&gt; props that take the name of the class FormKit will apply to that section. In your login form, the &lt;code&gt;outer&lt;/code&gt; sections get a class of &lt;code&gt;mb-3&lt;/code&gt;, the &lt;code&gt;input&lt;/code&gt; sections get &lt;code&gt;form-control&lt;/code&gt;, and the &lt;code&gt;label&lt;/code&gt; gets &lt;code&gt;form-label&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here's what the login form should look like after you style it:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9nlcky2zt2sl7y98c0rc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9nlcky2zt2sl7y98c0rc.jpg" alt="styled form" width="800" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And to illustrate how section classes are applied, here's what the rendered HTML for the email input might look like:&lt;/p&gt;

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

&lt;p&gt;That's the general process for styling individual forms: you apply CSS to the input sections, one input at a time.&lt;/p&gt;

&lt;p&gt;While this works, it can become tedious and difficult to maintain if you have several forms in your app. It can also become clumsy if your app has large forms.&lt;/p&gt;

&lt;p&gt;The solution to this is to create a theme. A theme is a baseline set of styles that FormKit applies to every form in your app. &lt;/p&gt;
&lt;h2&gt;
  
  
  Creating a Bootstrap theme
&lt;/h2&gt;

&lt;p&gt;Before you build the theme, you'll create a new form to demo how the theme styles forms automatically.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;App.vue&lt;/code&gt;, replace the template for the login form with this registration form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;FormKit&lt;/span&gt;
        &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"form"&lt;/span&gt;
        &lt;span class="na"&gt;submit-label=&lt;/span&gt;&lt;span class="s"&gt;"Register"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mb-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; Register &lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;

      &lt;span class="nt"&gt;&amp;lt;FormKit&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;
               &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"John Doe"&lt;/span&gt;
               &lt;span class="na"&gt;label=&lt;/span&gt;&lt;span class="s"&gt;"Full Name"&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;gt;&amp;lt;/FormKit&amp;gt;&lt;/span&gt;

      &lt;span class="nt"&gt;&amp;lt;FormKit&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;
               &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"john@xyz.com"&lt;/span&gt;
               &lt;span class="na"&gt;label=&lt;/span&gt;&lt;span class="s"&gt;"Email"&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;gt;&amp;lt;/FormKit&amp;gt;&lt;/span&gt;

      &lt;span class="nt"&gt;&amp;lt;FormKit&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"range"&lt;/span&gt;
               &lt;span class="na"&gt;label=&lt;/span&gt;&lt;span class="s"&gt;"Age"&lt;/span&gt;
               &lt;span class="na"&gt;min=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;
               &lt;span class="na"&gt;max=&lt;/span&gt;&lt;span class="s"&gt;"100"&lt;/span&gt;
               &lt;span class="na"&gt;:help=&lt;/span&gt;&lt;span class="s"&gt;"`You are ${age} years old`"&lt;/span&gt;
               &lt;span class="na"&gt;v-model=&lt;/span&gt;&lt;span class="s"&gt;"age"&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;gt;&amp;lt;/FormKit&amp;gt;&lt;/span&gt;

      &lt;span class="nt"&gt;&amp;lt;FormKit&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt;
               &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"***********"&lt;/span&gt;
               &lt;span class="na"&gt;label=&lt;/span&gt;&lt;span class="s"&gt;"Password"&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;gt;&amp;lt;/FormKit&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;/FormKit&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then add this code to the &lt;code&gt;script&lt;/code&gt; section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, your registration form should have zero styling, and should look like this:&lt;/p&gt;

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

&lt;p&gt;Next, you'll create your form theme. Add a file to the root of your &lt;code&gt;formkit-bootstrap&lt;/code&gt; directory called &lt;code&gt;formkit.config.js&lt;/code&gt;, then put the following code in it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;generateClasses&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@formkit/themes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;generateClasses&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;global&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// classes&lt;/span&gt;
        &lt;span class="na"&gt;outer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$reset mb-3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;form-control&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;form-label&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;help&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;form-text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;form&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;form&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;col-md-4 col-lg-3 mt-5 mx-auto p-5 border rounded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;range&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$reset form-range&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;outer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$reset mt-3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$reset btn btn-primary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code is what's responsible for your theme, so let's break it down. The code relies on the &lt;code&gt;generateClasses&lt;/code&gt; function from the &lt;code&gt;@formkit/themes&lt;/code&gt; package to apply a list of classes to each section of each input type you specify.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;$reset&lt;/code&gt; keyword at the beginning of a class list tells FormKit to disregard any styling applied to that section before that point.&lt;/p&gt;

&lt;p&gt;The theme you created is pretty minimal. All it does is apply Bootstrap classes to the inputs you used in your form, and define some default styling for the form container and submit buttons.&lt;/p&gt;

&lt;p&gt;Now that you have your theme, the next step is to register it in your app. To do that, replace the code in &lt;code&gt;main.js&lt;/code&gt; with this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createApp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defaultConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@formkit/vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;customConfig&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;../formkit.config.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./styles.scss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;App&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;./App.vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;


&lt;span class="nf"&gt;createApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;defaultConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;customConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;
&lt;span class="p"&gt;})).&lt;/span&gt;&lt;span class="nf"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And wait for your dev server to reload.&lt;/p&gt;

&lt;p&gt;After your server has reloaded, check your application's page. You should see that your registration form is completely styled, and it should look like this:&lt;/p&gt;

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

&lt;p&gt;If you feel like testing your theme a bit, you can copy the unstyled version of the login form to &lt;code&gt;App.vue&lt;/code&gt; to see how the styling applies to it. &lt;/p&gt;

&lt;p&gt;Here's a great thing about FormKit’s themes: all they are is a convenient baseline. Applying form-specific styling to an input will override the theme styling without conflicts. This makes it easy for you to tweak the styling of individual forms.&lt;/p&gt;

&lt;p&gt;As an example, if you wanted to change the color of the register button to green, you could apply section styles like earlier in the article:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt; &lt;span class="nt"&gt;&amp;lt;FormKit&lt;/span&gt;
        &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"form"&lt;/span&gt;
        &lt;span class="na"&gt;submit-label=&lt;/span&gt;&lt;span class="s"&gt;"Register"&lt;/span&gt;
        &lt;span class="na"&gt;:submit-attrs=&lt;/span&gt;&lt;span class="s"&gt;"{
          inputClass: 'btn btn-success'
        }"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And voila.&lt;/p&gt;

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

&lt;p&gt;Note:&lt;br&gt;
Themes are framework-agnostic. As long as the classes you put in your config are part of your app's stylesheet, you can create a theme with anything from vanilla CSS to &lt;a href="https://formkit.com/guides/create-a-tailwind-theme" rel="noopener noreferrer"&gt;Tailwind&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Floating labels
&lt;/h2&gt;

&lt;p&gt;If you want to use floating labels in your forms, you can use a plugin from this &lt;a href="https://github.com/formkit/formkit/issues/500#issuecomment-1338203487" rel="noopener noreferrer"&gt;GitHub issue&lt;/a&gt;. Since Boostrap's floating labels are CSS-only, they won't conflict with Vue or FormKit. Just keep in mind: registering the plugin globally will give all &lt;code&gt;type=text&lt;/code&gt; inputs floating labels.&lt;/p&gt;

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

&lt;p&gt;At this point, you've learned how to build your forms with FormKit and style them with Bootstrap, both disparately and by creating a theme. If you need further assistance, FormKit has a &lt;a href="https://discord.gg/Vhu97pAC76" rel="noopener noreferrer"&gt;Discord server&lt;/a&gt; where you can ask for help.&lt;/p&gt;

</description>
      <category>gratitude</category>
    </item>
    <item>
      <title>ELI5: Promises in JavaScript</title>
      <dc:creator>Paul Akinyemi</dc:creator>
      <pubDate>Sun, 30 Jan 2022 19:44:17 +0000</pubDate>
      <link>https://dev.to/morgenstern2573/eli5-promises-in-javascript-22a3</link>
      <guid>https://dev.to/morgenstern2573/eli5-promises-in-javascript-22a3</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;Promises are an integral part of asynchronous programming in JavaScript. If you need to do any asynchronous work, chances are you'll need to work with them. But how exactly do Promises work, and how can we use them in our code?&lt;/p&gt;

&lt;p&gt;This article explains the basics of Promises in JavaScript, but it does not cover async/await. After you finish reading, you should:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;posses an intuitive understanding of how Promises work&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;understand how to create and use Promises&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;This article assumes the reader understands the following concepts: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Introducing"&gt;asynchronous JavaScript and how it differs from synchronous JavaScript&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://javascript.info/class"&gt;classes in JavaScript&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://javascript.info/static-properties-methods"&gt;the difference between static methods and instance methods&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://javascript.info"&gt;the fundamentals of JavaScript&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you don't already understand those, visit the links above!&lt;/p&gt;

&lt;h2&gt;
  
  
  What are JavaScript Promises?
&lt;/h2&gt;

&lt;p&gt;A Promise is a JavaScript object that makes it easy for us to write asynchronous code. You can think of a promise as a sort of code IOU. A Promise serves as a placeholder for a value that isn't available yet, and it provides the requested value when said value is available.&lt;/p&gt;

&lt;p&gt;JavaScript Promises work much like the non-code kind. When someone makes you a promise, they're saying: I can't do this for you yet, but I'll try my best, then get back to you.&lt;/p&gt;

&lt;p&gt;JavaScript Promises are similar. A piece of code requests a resource that isn't available, the same way you might ask for a gift from a friend. In response, the requesting code gets a particular object: a Promise. &lt;/p&gt;

&lt;p&gt;This object allows the providing code to deliver the resource when it's ready or notify the requesting code of its failure, the way your friend might come to you later to deliver your gift. &lt;/p&gt;

&lt;p&gt;Here's another definition from MDN:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A Promise is an object representing the eventual completion or failure of an asynchronous operation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises"&gt;Using Promises&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I prefer a slightly different phrasing:&lt;/p&gt;

&lt;p&gt;A Promise represents an asynchronous operation that will eventually run to completion or encounter an error in the process.  &lt;/p&gt;

&lt;h2&gt;
  
  
  The State of a JavaScript Promise
&lt;/h2&gt;

&lt;p&gt;A Promise can exist in one of three states:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The pending state, where all Promises start.&lt;/li&gt;
&lt;li&gt;The fulfilled state, which means the operation is complete. &lt;/li&gt;
&lt;li&gt;The rejected state, which means the operation failed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A Promise exists in the pending state when the operation it represents hasn't run to completion.&lt;/p&gt;

&lt;p&gt;A Promise moves to the fulfilled state if the operation it represents runs successfully. &lt;/p&gt;

&lt;p&gt;If the operation fails, the Promise moves to the rejected state.&lt;/p&gt;

&lt;p&gt;When a Promise moves to either the fulfilled or rejected state, we say that the Promise has "settled".&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a Promise
&lt;/h2&gt;

&lt;p&gt;The syntax for creating a Promise is &lt;code&gt;new Promise(function)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The function we pass to the Promise is where the code that will request the desired resource lives. That function has two mandatory arguments: resolve() and reject().&lt;/p&gt;

&lt;p&gt;Both arguments are functions that the browser will supply.&lt;br&gt;
We call resolve() in our function when our asynchronous code executes successfully, and we call reject() if we can't complete the operation.&lt;/p&gt;

&lt;p&gt;We call the value we pass to resolve() the "fulfilment value", and the value we pass to reject() the "rejection reason".&lt;/p&gt;

&lt;p&gt;Here's an example of creating a Promise:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;// do some async stuff&lt;/span&gt;

&lt;span class="c1"&gt;// if code is successful&lt;/span&gt;
&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// we couldn't complete the operation for some reason&lt;/span&gt;
&lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using Promises
&lt;/h2&gt;

&lt;p&gt;We can use Promises in two ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;async/await&lt;/li&gt;
&lt;li&gt;Promise instance methods&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We won't be covering async/await in this article, but you can read about it &lt;a href="https://javascript.info/async-await"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Promise instance methods
&lt;/h3&gt;

&lt;p&gt;Remember that a Promise is a kind of IOU for the result of an operation? We use Promises by passing the code we want to use the result of that operation (the code that claims the IOU) to one of three instance methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;then() method&lt;/li&gt;
&lt;li&gt;catch() method&lt;/li&gt;
&lt;li&gt;finally() method&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All instance methods only run after the Promise they belong to have settled, and all instance methods return a new Promise.&lt;/p&gt;

&lt;h3&gt;
  
  
  The then() method
&lt;/h3&gt;

&lt;p&gt;The then() method accepts up to two functions as arguments. &lt;br&gt;
The first argument contains the code you want to run if the Promise fulfils, and the second contains code that should run if the Promise rejects.&lt;/p&gt;

&lt;p&gt;Both arguments to then() are optional. If we don't provide a callback to a then() method corresponding to the parent Promise's current state, the method will return a new Promise in the same state as its parent Promise.&lt;/p&gt;

&lt;p&gt;Here's an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// we can use then() like this:&lt;/span&gt;
&lt;span class="nx"&gt;demoPromise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;successCallback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;failureCallback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// or if we don't care about failure:&lt;/span&gt;
&lt;span class="nx"&gt;demoPromise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;successCallback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// if demoPromise is in the rejected state,&lt;/span&gt;
&lt;span class="c1"&gt;// the above lcode will immediately return a new rejected Promise&lt;/span&gt;


&lt;span class="c1"&gt;// we can handle only failure like this:&lt;/span&gt;
&lt;span class="nx"&gt;demoPromise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;failureCallback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// if demoPromise is in the fulfilled state,&lt;/span&gt;
&lt;span class="c1"&gt;// this line will immediately return a new fulfilled Promise&lt;/span&gt;


&lt;span class="c1"&gt;// not very useful, but it won't cause an error&lt;/span&gt;
&lt;span class="nx"&gt;demoPromise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The catch() method
&lt;/h3&gt;

&lt;p&gt;The catch() method receives one mandatory argument.&lt;br&gt;
The purpose of the catch() method is to handle the failure of the operation the Promise represents. The argument to catch() contains the code that we want to run if the operation fails. &lt;/p&gt;

&lt;p&gt;The calling the catch() method works the same as calling &lt;code&gt;then(undefined, failureCallback)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The function passed to catch receives the rejection reason of the parent Promise as its argument.&lt;/p&gt;
&lt;h3&gt;
  
  
  The finally() method
&lt;/h3&gt;

&lt;p&gt;The finally() method receives a single function as its argument. The argument to finally() contains code that we want to execute regardless of the success or failure of the operation the Promise represents, and the function passed to finally() never receives an argument.&lt;/p&gt;

&lt;p&gt;So now we can use the value represented by a single Promise, but what do we do when we want to execute multiple operations back to back, because the second operation depends on the first? We use Promise Chaining.&lt;/p&gt;
&lt;h3&gt;
  
  
  Promise Chaining
&lt;/h3&gt;

&lt;p&gt;Promise chaining is a technique where you attach one instance method to another to execute successive operations. Promise chaining is possible because each instance method returns a new settled Promise, which becomes the parent of the following instance method in the chain.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;demoPromise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://example.com/resource.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;demoPromise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;// do some cool stuff&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;// first then() returns a new, already settled Promise&lt;/span&gt;
&lt;span class="c1"&gt;// value 1 is the fulfillment value that this then() receives&lt;/span&gt;

&lt;span class="c1"&gt;// we can now do something with value 1&lt;/span&gt;
&lt;span class="nx"&gt;someOperation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;//handle error if something goes wrong in producing value 1&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Chained Promise methods usually execute one after another, except when an operation in the chain fails and throws an error.&lt;/p&gt;

&lt;p&gt;If this happens, the method that raised the error returns a rejected Promise. The next method to execute is the closest method that has a failure callback (a then() with two arguments or a catch() method).&lt;/p&gt;

&lt;p&gt;Execution resumes from the then() method after the method that handled the error, if there is any.&lt;/p&gt;

&lt;p&gt;Here's an example of a Promise chain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;demoPromise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://example.com/promise.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;demoPromise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;// an error occurs&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;// this function won't run&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;//handle error&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;//resume execution after the error&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;// handle any new errors&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This section covered how to execute successive asynchronous operations, but what if the code we need to run depends on the result of multiple Promises at once?&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Multiple Promises Together
&lt;/h3&gt;

&lt;p&gt;So what do you do when you want to run code that depends on two or more Promises that need to run simultaneously? We use the static methods of the Promise Class.&lt;/p&gt;

&lt;p&gt;The Promise class has six static methods in total, but we'll only talk about the three you're most likely to need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Promise.all() &lt;/li&gt;
&lt;li&gt;Promise.race()&lt;/li&gt;
&lt;li&gt;Promise.any()&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All static methods take multiple Promises as their argument and return a single Promise based on the settled states of the argument Promises.&lt;/p&gt;

&lt;h4&gt;
  
  
  Promise.all()
&lt;/h4&gt;

&lt;p&gt;Promise.all() allows you to attach an instance method to a Promise whose fulfilment value is an array of the fulfilment values of the Promises passed to Promise.all(). &lt;/p&gt;

&lt;p&gt;The Promise the instance method is attached to only moves to the fulfilled state when all Promises passed to Promise.all() have moved to the fulfilled state.&lt;br&gt;
Once this happens, Promise.all() returns a new fulfilled Promise to the instance method.&lt;/p&gt;

&lt;p&gt;If any of the input Promises rejects, Promise.all() returns a settled Promise in the rejection state, whose rejection reason is the reason of the first Promise to reject. Any Promises still in the pending state are ignored, regardless of which state they settle into.&lt;/p&gt;

&lt;p&gt;Let's look at an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;multiPromise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resource1.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resource2.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resorce3.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="nx"&gt;multiPromise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;arrayOfFulfilledValues&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;// process all the fulfilled values&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;// process the first rejection that happens&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Promise.race()
&lt;/h4&gt;

&lt;p&gt;Promise.race() is similar to Promise.all().The difference is: the Promise returned by Promise.race is simply the first Promise to settle. Once any Promise moves to the fulfilled or rejected states, Promise.race() ignores the other input Promises.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firstSettledPromise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;race&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resource1.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resource2.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resorce3.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="nx"&gt;firstSettledPromise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;firstResolvedValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;// process the first fulfilled value&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;// process the first rejection that happens&lt;/span&gt;
&lt;span class="c1"&gt;// whether in the Promise race or in the then()&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Promise.any()
&lt;/h4&gt;

&lt;p&gt;Promise.any() is like Promise.race(), but it will wait for the first Promise to move to the fulfilled state, instead of the first Promise to settle.&lt;/p&gt;

&lt;p&gt;If an input Promise moves to the rejected state, Promise.any() does nothing as long as other Promises are still in the pending state.&lt;/p&gt;

&lt;p&gt;If all the input Promises reject, Promise.any() returns a rejected Promise with an Aggregate Error, containing all the rejection reasons.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firstFulfilledPromise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resource1.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resource2.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resorce3.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;firstFulfilledPromise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;firstResolvedValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;// process the resolved value&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;// process the Aggregate error or&lt;/span&gt;
&lt;span class="c1"&gt;// an error that occurs in the then()&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Use cases of Promises
&lt;/h2&gt;

&lt;p&gt;Typically, working with Promises in the real world involves consuming Promises returned to you from a browser API or JavaScript method.&lt;/p&gt;

&lt;p&gt;It's relatively rare that you'll have to create a Promise in your code. Here are some of  the most common APIs and functions that return Promises: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Fetch API&lt;/li&gt;
&lt;li&gt;Response.json()&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;In this article, we covered the basics necessary to work with Promises. If you would like to learn more, visit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises"&gt;Using Promises&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://javascript.info/promise-api"&gt;Promise API&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"&gt;Promise&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>ELI5: Reactivity in Vue 3</title>
      <dc:creator>Paul Akinyemi</dc:creator>
      <pubDate>Mon, 17 Jan 2022 18:22:22 +0000</pubDate>
      <link>https://dev.to/morgenstern2573/eli5-reactivity-in-vue-3-4o40</link>
      <guid>https://dev.to/morgenstern2573/eli5-reactivity-in-vue-3-4o40</guid>
      <description>&lt;p&gt;Reactivity. It's a popular buzzword. It's also one of the most convenient features of front-end frameworks.&lt;/p&gt;

&lt;p&gt;What is it exactly, and how does it work in Vue 3?&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Working Knowledge of basic JavaScript and JS objects&lt;/li&gt;
&lt;li&gt;Working Knowledge of basic Vue.js&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is Reactivity?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Reactivity is a programming paradigm that allows us to adjust to changes in a declarative manner. &lt;br&gt;
&lt;a href="https://v3.vuejs.org/guide/reactivity.html#what-is-reactivity"&gt;&lt;strong&gt;Vue 3.x documentation&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We say a value is reactive when it can update itself in response to changes in values it depends on. What do we mean by depends on? &lt;/p&gt;

&lt;p&gt;Let's take an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;val1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;val2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;val1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;val2&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The value of sum is always determined by the values of val1 and val2, so we say that sum depends on val1 and val2.&lt;/p&gt;

&lt;p&gt;What happens to sum when one of the values it depends on changes? In regular JavaScript, it stays the same.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// 5&lt;/span&gt;

&lt;span class="nx"&gt;val1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Still 5&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;But if sum was reactive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// 5&lt;/span&gt;

&lt;span class="nx"&gt;val1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Sum is 6!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The value of sum would change in response to the change in a value it depended on. &lt;/p&gt;

&lt;h2&gt;
  
  
  What does Vue need to make a value reactive?
&lt;/h2&gt;

&lt;p&gt;Vue needs to know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what dependencies that value has.&lt;/li&gt;
&lt;li&gt;when those dependencies change.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vue also needs to be able to re-calculate values when their dependencies change.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Vue knows when dependencies change
&lt;/h2&gt;

&lt;p&gt;Vue wraps the data object of all components with an &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy"&gt;ES6 Proxy&lt;/a&gt;.&lt;br&gt;
A proxy is an object that wraps a target object.&lt;/p&gt;

&lt;p&gt;This is important because all reactive values depend (directly or not) on the properties in a component's data object. &lt;/p&gt;

&lt;p&gt;Proxies allow you to intercept all requests to get or set properties of the target. They also let you run any code in response to those requests. &lt;/p&gt;

&lt;p&gt;Thanks to this, when code attempts to change one of the properties of a data object, Vue intercepts it and is aware of it.&lt;/p&gt;

&lt;p&gt;Vue can then re-calculate any functions that depend on that value. But how does Vue know which functions depend on which values?&lt;/p&gt;
&lt;h2&gt;
  
  
  How Vue knows which dependencies belong to a value
&lt;/h2&gt;

&lt;p&gt;To make our value reactive, we need to wrap it in a function. Using sum to illustrate again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// we need to go from&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;val1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;val2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;val1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;val2&lt;/span&gt;

&lt;span class="c1"&gt;// to&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;updateSum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;val1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;val2&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Vue then wraps all such functions with an effect. An effect is a function that takes another function as an argument. Vue then calls the effect in place of that function. &lt;/p&gt;

&lt;p&gt;When Vue calls an effect, the effect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Records that it's running.&lt;/li&gt;
&lt;li&gt;Calls the function it received as an argument. &lt;/li&gt;
&lt;li&gt;Removes itself from the list of running effects after the function ends.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Remember all source values come from a Proxy (the data component)? While executing the function it wraps, the effect will need a property from the data object, and try to read it.&lt;/p&gt;

&lt;p&gt;The Proxy will intercept that read request. Vue checks which effect is currently running. It then records that the effect depends on the property it tried to read. This is how Vue knows which values depend on which properties.&lt;/p&gt;

&lt;h3&gt;
  
  
  So how does Vue know when to re-run the functions that return dependent values?
&lt;/h3&gt;

&lt;p&gt;The answer is once again the magic of Proxies. Proxies can intercept requests to set property values too. &lt;/p&gt;

&lt;p&gt;Remember we now have a record of effects, as well as the values they depend on. When the value of a property in data changes, Vue needs to do one thing: check that record and update the source value.&lt;/p&gt;

&lt;p&gt;Vue can then re-run all the effects that depend on it, and thus update the values. &lt;/p&gt;

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

&lt;p&gt;This article is a simplified overview of how reactivity works in Vue 3. If you'd like to read more on the subject, here are some resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.sitepoint.com/vue-3-reactivity-system/"&gt;Understanding the New Reactivity System in Vue 3&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://v3.vuejs.org/guide/reactivity.html#how-vue-tracks-these-changes"&gt;Reactivity in Depth&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>vue</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>A beginner's guide to writing cleaner code</title>
      <dc:creator>Paul Akinyemi</dc:creator>
      <pubDate>Mon, 03 Jan 2022 13:24:14 +0000</pubDate>
      <link>https://dev.to/morgenstern2573/a-guide-to-clean-code-for-new-developers-87h</link>
      <guid>https://dev.to/morgenstern2573/a-guide-to-clean-code-for-new-developers-87h</guid>
      <description>&lt;p&gt;Why should anyone even care whether their code is 'clean' or not? &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Indeed, the ratio of time spent reading versus writing is well over 10 to 1.&lt;br&gt;
We are constantly reading old code as part of the effort to write new code. ...[Therefore,] making it easy to read makes it easier to write.&lt;br&gt;
― Robert C. Martin, Clean Code: A Handbook of Agile Software Craftsmanship&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I think he put it rather nicely.&lt;/p&gt;

&lt;p&gt;So we know why we should care, but what exactly is clean code? What separates clean code from messy code? &lt;/p&gt;

&lt;p&gt;This is the answer I prefer: "code is clean when it's easy to read, easy to understand, and it's organized in a way that makes sense to other people". &lt;/p&gt;

&lt;p&gt;Now that we have a definition, what exactly can you do to clean up your code? I've outlined my best advice below:&lt;/p&gt;

&lt;h2&gt;
  
  
  Making your code easy to read
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Whitespace is your friend, and too much whitespace is far better than too little
&lt;/h3&gt;

&lt;p&gt;Text, in general, is much easier to read when it's generously spaced, but it tends to feel overwhelming when it's too dense. Dense blocks of text also make it harder to keep track of your position in the document.&lt;/p&gt;

&lt;p&gt;To illustrate, here's some dense code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;function&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt;
&lt;span class="n"&gt;variable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argument&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;variable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;variable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;my_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argument&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="n"&gt;do&lt;/span&gt; &lt;span class="n"&gt;xyz&lt;/span&gt;
&lt;span class="n"&gt;variable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;my&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;variable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here's some well-spaced code:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="n"&gt;variable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argument&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;variable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;variable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;my_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argument&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
   &lt;span class="n"&gt;do&lt;/span&gt; &lt;span class="n"&gt;xyz&lt;/span&gt;

&lt;span class="n"&gt;variable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;my&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;variable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See how some space makes it easier to read?&lt;/p&gt;

&lt;h3&gt;
  
  
  The length of any line in your code shouldn't exceed 80 characters
&lt;/h3&gt;

&lt;p&gt;Your code will be easier to read in many short lines, instead of a few very long ones. There's nothing magical about the 80 character limit, but I find it a convenient rule of thumb, and anything much longer quickly becomes a pain to read.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making your code easy to understand
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Writing too many comments is much better than writing too few
&lt;/h3&gt;

&lt;p&gt;Comments can sometimes feel bothersome to write, but whenever you write any code that is even slightly complicated, you should have a short comment before it. &lt;br&gt;
The comment should explain what you were trying to achieve and if it's a very complex piece of code, briefly documenting why you chose that approach might be a good idea.&lt;/p&gt;
&lt;h3&gt;
  
  
  Long identifiers are better than cryptic ones
&lt;/h3&gt;

&lt;p&gt;Sometimes, it feels hard to express the purpose of a variable or a function in one word, and writing a multi-word variable name can feel awkward. &lt;br&gt;
But it's still better to use a long name that clearly explains the purpose of a variable than one that is short but cryptic.&lt;/p&gt;
&lt;h3&gt;
  
  
  Avoid nesting where you can(but keep line length in mind)
&lt;/h3&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
   &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is easier to understand than :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;black&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;white&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;blue&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;red&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;green&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is harder to understand than:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;black&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;white&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;red&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;green&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Organising your code
&lt;/h2&gt;

&lt;h3&gt;
  
  
  As much as is reasonable, &lt;strong&gt;DRY&lt;/strong&gt;(Don't Repeat Yourself)
&lt;/h3&gt;

&lt;p&gt;When you find yourself repeating the same(or very similar) code in more than two different places, you would usually be better served turning it into a function and calling it twice instead. Your code ends up being shorter, and making future changes becomes much less of a hassle.&lt;/p&gt;

&lt;h3&gt;
  
  
  Side effects are evil, so use with (extreme) caution
&lt;/h3&gt;

&lt;p&gt;Some functions accept parameters and return a value, and do nothing else. That's what an ideal function looks like. Impure functions cause side-effects: changing a global variable or class attribute within the body of the function, for example. Side effects are evil because they generally make debugging a pain in the behind: You end up having to scrutinize each line of code to understand where the heck the value of your variable is coming from.&lt;/p&gt;

&lt;h3&gt;
  
  
  Your functions should ideally do only one thing
&lt;/h3&gt;

&lt;p&gt;Functions that have more than one job are usually much longer than they should be and tend to cause much confusion in the code you're calling them. To use an analogy, if you asked a friend to grab you a book and he returned with a shoe in his other hand, wouldn't you be really confused? It's the same idea with your functions, so please don't confuse the poor folks who end up using or changing them. Your function should do what its name says, and nothing more.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Keep it short&lt;/li&gt;
&lt;li&gt;Space it out&lt;/li&gt;
&lt;li&gt;80-character lines&lt;/li&gt;
&lt;li&gt;Write lots of comments&lt;/li&gt;
&lt;li&gt;Use good names&lt;/li&gt;
&lt;li&gt;D.R.Y&lt;/li&gt;
&lt;li&gt;Pure functions&lt;/li&gt;
&lt;li&gt;Simple functions&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Putting it into practice
&lt;/h2&gt;

&lt;p&gt;No one keeps an eight-step checklist in their head as you're trying to solve a problem or fix a bug, so how does anyone manage to write clean code? Simple. We don't, at least not at first. The answer is called &lt;a href="https://en.wikipedia.org/wiki/Code_refactoring"&gt;refactoring&lt;/a&gt;, and it means changing code without affecting the output.&lt;/p&gt;

&lt;p&gt;After you're done with one piece of code, stop. Take ten minutes to breathe. Look away for a little while. Then go over the code you just wrote, and fix any messy bits you find.&lt;/p&gt;

&lt;p&gt;Over time, writing clean(er) code will become more and more of a habit, and you'll find progressively fewer mistakes when you refactor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;p&gt;I tried to make this post as useful as I could, but a full, in-depth treatment would probably require writing a short book. Here's some extra content you'll find useful if you want to learn more about writing clean code.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.python.org/dev/peps/pep-0020"&gt;The Zen of Python&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.freecodecamp.org/news/clean-coding-for-beginners"&gt;Clean Code Explained&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.goodreads.com/book/show/3735293-clean-code"&gt;Clean Code: A Handbook of Agile Software Craftsmanship&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>beginners</category>
      <category>programming</category>
      <category>codenewbie</category>
    </item>
  </channel>
</rss>
