<?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: Isa</title>
    <description>The latest articles on DEV Community by Isa (@isabelatravaglia).</description>
    <link>https://dev.to/isabelatravaglia</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%2F589991%2Fc5906a90-c557-43a9-84a0-067aece0aabc.jpg</url>
      <title>DEV Community: Isa</title>
      <link>https://dev.to/isabelatravaglia</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/isabelatravaglia"/>
    <language>en</language>
    <item>
      <title>Golang: A simple dockerized app to demonstrate the use of Handle, Handler, HandleFunc and HandlerFunc.</title>
      <dc:creator>Isa</dc:creator>
      <pubDate>Wed, 08 Sep 2021 11:57:01 +0000</pubDate>
      <link>https://dev.to/isabelatravaglia/golang-a-simple-dockerized-app-to-demonstrate-the-use-of-handle-handler-handlefunc-and-handlerfunc-n4j</link>
      <guid>https://dev.to/isabelatravaglia/golang-a-simple-dockerized-app-to-demonstrate-the-use-of-handle-handler-handlefunc-and-handlerfunc-n4j</guid>
      <description>&lt;p&gt;When trying to understand how to build a web application in Go, it took me a while to understand the logic behind Handle, Handler, HandleFunc and HandlerFunc. I had a hard time grasping the logic, so I decided to write an article to make sure I understood everything correctly (which I hadn't until the middle of the writing process) and to help out others who may also be struggling.&lt;/p&gt;

&lt;p&gt;To demonstrate the concepts of Handle, Handler, HandleFunc and HandlerFunc, we will write a very simple Go code inside of a container to serve a couple of web pages. But before checking the code, we need to have at least a general understanding of what are handlers in the context of the &lt;code&gt;net/http&lt;/code&gt; package in Go.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What are handlers?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;On the &lt;code&gt;net/http&lt;/code&gt; package, handlers are used to handle HTTP requests. This package has capabilities of initiating an HTTP server and the handlers' role is to handle the HTTP requests by receiving them and then responding back. So if the server receives a request to access the path "/products", a handler will handle that request by responding back with the code corresponding to that path. Technically speaking, a Handler is an interface that has a method called &lt;code&gt;ServeHTTP&lt;/code&gt; which has two parameters: a &lt;code&gt;ResponseWriter&lt;/code&gt; type (an interface) and a pointer to a &lt;code&gt;Request&lt;/code&gt; type (a struct). So any object that has this &lt;code&gt;ServeHTTP&lt;/code&gt; method with that signature is a handler.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Let's create our first page using Handle&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Now let's jump to our code! We will build a page with the path "/handlepage":&lt;br&gt;
&lt;/p&gt;

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

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

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;HandlePage&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="n"&gt;HandlePage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ServeHTTP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"text/html"&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;Fprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;h1&amp;gt;Welcome to the handle page!&amp;lt;/h1&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;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;NewHandlePage&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HandlePage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/handlepage"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NewHandlePage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":3000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you run the code above, and visit the page &lt;code&gt;localhost:3000/handlepage&lt;/code&gt;, you'll see the message "Welcome to the handle page!". But what does this code mean? Let's take a look at the main steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Understanding the code&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;First, we import the necessary packages to serve the page. Then we create a struct of type &lt;code&gt;HandlePage&lt;/code&gt;. Next, we add the &lt;code&gt;ServeHTTP&lt;/code&gt; method to the &lt;code&gt;HandlePage&lt;/code&gt; struct (remember that any object that contains a &lt;code&gt;ServeHTTP&lt;/code&gt; method with a &lt;code&gt;ResponseWriter&lt;/code&gt; type and a pointer to a &lt;code&gt;Request&lt;/code&gt; type as its parameters is a handler!). So by adding the &lt;code&gt;ServeHTTP&lt;/code&gt; method to the &lt;code&gt;HandlePage&lt;/code&gt; struct we have transformed it into a handler.&lt;/p&gt;

&lt;p&gt;Let's look at what's inside of the &lt;code&gt;ServeHTTP&lt;/code&gt; method.&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;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"text/html"&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;Fprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;h1&amp;gt;Welcome to the handle page!&amp;lt;/h1&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal of the code is to write a simple &lt;code&gt;html&lt;/code&gt; text to the page. So we set the content type as &lt;code&gt;text/html&lt;/code&gt; and then we use &lt;code&gt;fmt.Fprint&lt;/code&gt; to insert the &lt;code&gt;html&lt;/code&gt; text we want to write.&lt;/p&gt;

&lt;p&gt;Next, we go to the &lt;code&gt;main&lt;/code&gt; function. We start by initializing an instance of &lt;code&gt;HandlePage&lt;/code&gt; and assign it to &lt;code&gt;newHandlePage&lt;/code&gt;. Then we call the Handle method and pass two arguments: a string (&lt;code&gt;"/handlepage"&lt;/code&gt;) and a handler (&lt;code&gt;newHandlePage&lt;/code&gt;). Let's take a closer look to what this handle method does.&lt;/p&gt;

&lt;p&gt;This is the definition we find on the official &lt;a href="https://pkg.go.dev/net/http#Handle"&gt;Go documentation&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&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;handler&lt;/span&gt; &lt;span class="n"&gt;Handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;// Handle registers the handler for the given pattern in the DefaultServeMux.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the &lt;code&gt;Handle&lt;/code&gt; function accepts two arguments: a &lt;code&gt;String&lt;/code&gt; type and a &lt;code&gt;Handler&lt;/code&gt; type. But what is this &lt;code&gt;Handler&lt;/code&gt; type? We've mentioned it briefly earlier, but let's take a closer look and understand how we can create a handler from a struct and then use it inside a &lt;code&gt;Handle&lt;/code&gt; function. According to the &lt;a href="https://pkg.go.dev/net/http#Handler"&gt;documentation&lt;/a&gt;, the &lt;code&gt;Handler&lt;/code&gt; type is an interface that responds to an HTTP request:&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;type&lt;/span&gt; &lt;span class="n"&gt;Handler&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;ServeHTTP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That Handler interface has the &lt;code&gt;ServeHTTP&lt;/code&gt; behavior, which is the same behavior that we added to the &lt;code&gt;HandlePage&lt;/code&gt; struct at the beginning of our code, so we can say that the &lt;code&gt;HandlePage&lt;/code&gt; type implements the &lt;code&gt;ServeHTTP&lt;/code&gt; method. When a type has all the methods defined by an interface, then we can use that type to implement that interface. What does that mean in our case? It means that we can use a &lt;code&gt;HandlePage&lt;/code&gt; type as the Handler type in the second argument of the &lt;code&gt;Handle&lt;/code&gt; function. So it's like we could rewrite the Handle function 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="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&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;handler&lt;/span&gt; &lt;span class="n"&gt;HandlePage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can only make that replacement because our &lt;code&gt;HandlePage&lt;/code&gt; type implements all the methods found on the Handler type (which is only the &lt;code&gt;ServeHTTP&lt;/code&gt; method). A side note here: interfaces can be a little confusing, so if you're not familiar with it, my suggestion is that you take a look at &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-use-interfaces-in-go"&gt;this article&lt;/a&gt; from Digital Ocean, and then at &lt;a href="https://www.calhoun.io/crash-course-on-go-interfaces/"&gt;this one&lt;/a&gt; from Jon Calhoun.&lt;/p&gt;

&lt;p&gt;So just to recap, why did we add the &lt;code&gt;ServeHTTP&lt;/code&gt; method to our &lt;code&gt;HandlePage&lt;/code&gt; struct? So that we can use the &lt;code&gt;HandlePage&lt;/code&gt; type as an implementation of the Handler interface.&lt;/p&gt;

&lt;p&gt;What about the &lt;code&gt;Handle&lt;/code&gt; function? The Handle function's job is to register the handler for the given pattern on the &lt;code&gt;DefaultServeMux&lt;/code&gt; (more about this ahead). &lt;/p&gt;

&lt;p&gt;Finally, &lt;code&gt;ListenAndServe&lt;/code&gt; is executed using port 3000. &lt;code&gt;ListenAndServe&lt;/code&gt; starts an HTTP server, and when the second parameter is nil, the &lt;code&gt;DefaultServeMux&lt;/code&gt; is used. &lt;code&gt;DefaultServeMux&lt;/code&gt; is an instance of &lt;code&gt;ServeMux&lt;/code&gt; that is used as the default multiplexer. A multiplexer's job is to redirect a request to a handler.&lt;/p&gt;

&lt;p&gt;One interesting thing to notice is that the type of the second parameter of &lt;code&gt;ListenAndServe&lt;/code&gt; is a &lt;code&gt;Handler&lt;/code&gt; type. Hence, if a multiplexer (&lt;code&gt;DefaultServeMux&lt;/code&gt;) is being used in the second parameter, it means it must have a &lt;code&gt;ServeHTTP&lt;/code&gt; method to enable it to act as a handler. So the &lt;code&gt;DefaultServeMux&lt;/code&gt; object is nothing but a handler that redirects requests made to an URL to the registered handler.&lt;/p&gt;

&lt;p&gt;Now let's try to get the big picture of what happens when &lt;code&gt;http.Handle("/handlepage", NewHandlePage)&lt;/code&gt; and &lt;code&gt;http.ListenAndServe(":3000", nil)&lt;/code&gt; are executed. When &lt;code&gt;http.Handle("/handlepage", NewHandlePage)&lt;/code&gt;is executed, the &lt;code&gt;NewHandlePage&lt;/code&gt; handler for the &lt;code&gt;"/handlepage"&lt;/code&gt; path is registered in the &lt;code&gt;DefaultServeMux&lt;/code&gt;. When &lt;code&gt;http.ListenAndServe(":3000", nil)&lt;/code&gt; is executed, the HTTP server is started and the &lt;code&gt;DefaultServeMux&lt;/code&gt; can then redirect requests according to what was registered on it (so far we only registered the relationship between the &lt;code&gt;"/handlepage"&lt;/code&gt; path and the &lt;code&gt;NewHandlePage&lt;/code&gt; handler). When the &lt;code&gt;"/handlepage"&lt;/code&gt; path is visited, &lt;code&gt;DefaultServeMux&lt;/code&gt; redirects the request to the &lt;code&gt;NewHandlePage&lt;/code&gt; handler and the &lt;code&gt;ServeHTTP&lt;/code&gt; method is called, executing the code inside of it. So in our case, when we visit &lt;code&gt;http://localhost:3000/handlepage&lt;/code&gt;, the code inside the &lt;code&gt;ServeHTTP&lt;/code&gt; method,&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;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"text/html"&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;Fprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;h1&amp;gt;Welcome to the handle page!&amp;lt;/h1&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is executed and we see the "Welcome to the handle page!" written on the page.&lt;/p&gt;

&lt;p&gt;Ok, so now we have a general understanding of what handle and handlers do and how they relate to each other: the first is a function that uses the second, an interface (or an object that implements that interface), as one of its parameters. So since a &lt;code&gt;Handle&lt;/code&gt; uses a &lt;code&gt;Handler&lt;/code&gt; as one of its parameters, can we infer that &lt;code&gt;HandleFunc&lt;/code&gt; uses a &lt;code&gt;HandlerFunc&lt;/code&gt; as one of its parameters too? Well, not necessarily. So what is the difference between a &lt;code&gt;HandleFunc&lt;/code&gt; and a &lt;code&gt;Handle&lt;/code&gt; and between a &lt;code&gt;Handler&lt;/code&gt; and a &lt;code&gt;HandlerFunc&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;Let's continue with our example to have a better understanding and make a brief recap on what we've done so far to create our single page on &lt;code&gt;"/handlepage"&lt;/code&gt; using only Handle and Handler:&lt;br&gt;
1- Created a &lt;code&gt;HandlePage&lt;/code&gt; struct;&lt;br&gt;
2- Added the &lt;code&gt;ServeHTTP&lt;/code&gt; method to the &lt;code&gt;HandlePage&lt;/code&gt; struct;&lt;br&gt;
3- Created an instance of &lt;code&gt;HandlePage&lt;/code&gt; and name it &lt;code&gt;NewHandlePage&lt;/code&gt;;&lt;br&gt;
4- Executed &lt;code&gt;http.Handle&lt;/code&gt; having &lt;code&gt;NewHandlePage&lt;/code&gt; as its handler.&lt;/p&gt;

&lt;p&gt;Now let's assume we want to create several pages. If we follow the logic above, we would need to create a struct for every page and then add the &lt;code&gt;ServeHTTP&lt;/code&gt; method to each. &lt;code&gt;HandleFunc&lt;/code&gt; and &lt;code&gt;HandlerFunc&lt;/code&gt; can help us simplify this flow. But how? Let's see!&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;The second page: using HandleFunc&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;We will now create a different page, with a path &lt;code&gt;"/handlefuncpage"&lt;/code&gt;. Let's add to the previous code all the steps necessary to create and serve this new page, but now using &lt;code&gt;HandleFunc&lt;/code&gt; instead of &lt;code&gt;Handle&lt;/code&gt;. Here's how our new code 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;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

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

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;HandlePage&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="n"&gt;HandlePage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ServeHTTP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"text/html"&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;Fprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;h1&amp;gt;Welcome to the handle page!&amp;lt;/h1&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;HandleFuncPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"text/html"&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;Fprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;h1&amp;gt;Welcome to the handlefunc page!&amp;lt;/h1&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;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;NewHandlePage&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HandlePage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/handlepage"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NewHandlePage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/handlefuncpage"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HandleFuncPage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":3000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So which steps did we have to take to create the new page on &lt;code&gt;"/handlefuncpage"&lt;/code&gt; path?&lt;br&gt;
1- Created a &lt;code&gt;HandleFuncPage&lt;/code&gt; function;&lt;br&gt;
2- Executed &lt;code&gt;http.HandleFunc&lt;/code&gt; having the &lt;code&gt;HandleFuncPage&lt;/code&gt; function as its second parameter.&lt;/p&gt;

&lt;p&gt;When comparing the steps made to create the page on &lt;code&gt;"/handlepage"&lt;/code&gt; and the ones made to create the new page on &lt;code&gt;"/handlefuncpage"&lt;/code&gt;, we will see that we can replace the first's three steps by the second's first step: instead of i) creating a struct, ii) adding the &lt;code&gt;ServeHTTP&lt;/code&gt; method to it and iii) creating an instance of the struct, all we had to do was to create a function having a &lt;code&gt;ResponseWriter&lt;/code&gt; type and a pointer of an &lt;code&gt;Request&lt;/code&gt; type as its parameters. But how is this simplification possible? Let's compare &lt;code&gt;HandleFunc&lt;/code&gt; to &lt;code&gt;Handle&lt;/code&gt; and understand the differences.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&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;handler&lt;/span&gt; &lt;span class="n"&gt;Handler&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;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&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;handler&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;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When comparing &lt;code&gt;Handle&lt;/code&gt; and &lt;code&gt;HandleFunc&lt;/code&gt;, we see that the second parameter, the handler, is different: whereas in &lt;code&gt;Handle&lt;/code&gt; it is a &lt;code&gt;Handler&lt;/code&gt; type, in &lt;code&gt;HandleFunc&lt;/code&gt; it is a function with a &lt;code&gt;ResponseWriter&lt;/code&gt; type and a pointer of &lt;code&gt;Request&lt;/code&gt; type as parameters (yes, the same signature of the &lt;code&gt;ServeHTTP&lt;/code&gt; method). So this means that to use &lt;code&gt;HandleFunc&lt;/code&gt;, we need to have a function with the proper parameters to make it work like a handler. Once this is done, &lt;code&gt;HandleFunc&lt;/code&gt;, when executed, will convert this function into a handler and register it on the &lt;code&gt;DefaultServeMux&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Right, but where does &lt;code&gt;HandlerFunc&lt;/code&gt; enter after all? It's nowhere to be seen in our code! To unfold this mystery, we first need to understand what a &lt;code&gt;HandlerFunc&lt;/code&gt; is. &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What is &lt;code&gt;HandlerFunc&lt;/code&gt; and where is it?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;HandlerFunc&lt;/code&gt; is a function type that can convert any function with the right signature into a handler. But what is the right signature the function must have? Yes, the same signature of the &lt;code&gt;ServeHTTP&lt;/code&gt; method: a &lt;code&gt;ResponseWriter&lt;/code&gt; type and a pointer to a &lt;code&gt;Request&lt;/code&gt; type. So when we have a function with that signature, we can use &lt;code&gt;HandlerFunc&lt;/code&gt; to convert this function into a handler, allowing us to use it as the second parameter of a Handle function, for example. &lt;/p&gt;

&lt;p&gt;One thing to notice is that &lt;code&gt;HandlerFunc&lt;/code&gt; converts the function to a &lt;code&gt;HandlerFunc&lt;/code&gt; type, and not a &lt;code&gt;Handler&lt;/code&gt; type. So how can we use this &lt;code&gt;HandlerFunc&lt;/code&gt; type as a handler? Because a &lt;code&gt;HandlerFunc&lt;/code&gt; type implements the &lt;code&gt;ServeHTTP&lt;/code&gt; method, the same method that the &lt;code&gt;Handler&lt;/code&gt; type implements. Remember the interface logic we mentioned earlier: when a type has all the methods defined by an interface, then we can use that type to implement that interface. &lt;/p&gt;

&lt;p&gt;Let's check the &lt;code&gt;ServeHTTP&lt;/code&gt; method added to a &lt;code&gt;HandlerFunc&lt;/code&gt; type and see if there's anything interesting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// The HandlerFunc type is an adapter to allow the use of&lt;/span&gt;
&lt;span class="c"&gt;// ordinary functions as HTTP handlers. If f is a function&lt;/span&gt;
&lt;span class="c"&gt;// with the appropriate signature, HandlerFunc(f) is a&lt;/span&gt;
&lt;span class="c"&gt;// Handler that calls f.&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;HandlerFunc&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;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&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="c"&gt;// ServeHTTP calls f(w, r).&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;f&lt;/span&gt; &lt;span class="n"&gt;HandlerFunc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ServeHTTP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;When we convert a function to a handler using &lt;code&gt;HandlerFunc&lt;/code&gt;, a &lt;code&gt;ServeHTTP&lt;/code&gt; method is added to it. Calling &lt;code&gt;ServeHTTP&lt;/code&gt; on this new handler will simply call the underlying function (&lt;code&gt;f(w, r)&lt;/code&gt;). Think of when we added the &lt;code&gt;ServeHTTP&lt;/code&gt; method to the &lt;code&gt;HandlePage&lt;/code&gt; struct: calling &lt;code&gt;ServeHTTP&lt;/code&gt; on this struct simply executes the code we wrote inside of it. The &lt;code&gt;ServeHTTP&lt;/code&gt; method inside of a &lt;code&gt;HandlerFunc&lt;/code&gt; type was built to have the same effect: execute the underlying function and hence the code inside of it.&lt;/p&gt;

&lt;p&gt;Being able to convert any function with the appropriate signature into a handler is quite handy 😃, but going back to our mystery, we don't see this &lt;code&gt;HandlerFunc&lt;/code&gt; being used anywhere. When we call &lt;code&gt;HandleFunc&lt;/code&gt; in our code using our &lt;code&gt;HandleFuncPage&lt;/code&gt; function as its second parameter, everything works as expected, as if the function is already a handler. How can this be?&lt;/p&gt;

&lt;p&gt;To understand what is happening behind the hood, we must check the source code of &lt;code&gt;HandleFunc&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mux&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ServeMux&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&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;handler&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;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;handler&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="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http: nil handler"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;mux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HandlerFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handler&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;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&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;handler&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;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;DefaultServeMux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler&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;Ah! Now we can see &lt;code&gt;HandlerFunc&lt;/code&gt; in action! Notice in the last line of the first block that &lt;code&gt;HandlerFunc&lt;/code&gt; is being used to convert the function called handler into an actual &lt;code&gt;HandlerFunc&lt;/code&gt; type (&lt;code&gt;HandlerFunc(handler)&lt;/code&gt;). It is very important to understand that whenever you see something like &lt;code&gt;var nf http.Handler = http.HandlerFunc(notFound)&lt;/code&gt;, it means that you are converting a function into a handler type (in this case, we are converting the function &lt;code&gt;notFound&lt;/code&gt; into a handler). Some people who are not familiar with how &lt;code&gt;HandlerFunc&lt;/code&gt; works might think that it is a function call having &lt;code&gt;notFound&lt;/code&gt; as an argument and that it might return a different thing.&lt;/p&gt;

&lt;p&gt;Going back to our code, that's why when we called &lt;code&gt;http.HandleFunc("/handlefuncpage", HandleFuncPage)&lt;/code&gt; everything worked: &lt;code&gt;Handlefunc&lt;/code&gt; converted the &lt;code&gt;HandleFuncPage&lt;/code&gt; function into a handler under the hood.&lt;/p&gt;

&lt;p&gt;Right, so we now understand what a &lt;code&gt;HandlerFunc&lt;/code&gt; does and how it is used by &lt;code&gt;HandleFunc&lt;/code&gt; to convert functions into handlers. In our case, we didn't have to use &lt;code&gt;HandlerFunc&lt;/code&gt; explicitly, but there might be cases where we would want to convert our functions into handlers and then use them elsewhere. For example, if for some reason we wanted to keep using &lt;code&gt;Handle&lt;/code&gt; instead of &lt;code&gt;HandleFunc&lt;/code&gt;, we could have converted our function &lt;code&gt;HandleFuncPage&lt;/code&gt; using &lt;code&gt;HandlerFunc&lt;/code&gt; and then used &lt;code&gt;Handler&lt;/code&gt; to register it on &lt;code&gt;DefaultServeMux&lt;/code&gt;, 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="c"&gt;//Convert HandleFuncPage function into a handler:&lt;/span&gt;
&lt;span class="n"&gt;hfp&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandlerFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HandleFuncPage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;//Use the converted function as a handler using Handle:&lt;/span&gt;
&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/handlefuncpage"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hfp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember that the &lt;code&gt;Handle&lt;/code&gt; function doesn't convert functions to handlers, so that's why we need to convert the function to a handler and then pass it inside &lt;code&gt;Handle&lt;/code&gt;. In fact, &lt;code&gt;HandleFunc&lt;/code&gt; was a shortcut created to avoid having to explicitly convert functions into handlers.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The end&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;And that's it! I hope you enjoyed this article and that your understanding of all those handy stuff is now a little bit better. &lt;/p&gt;

&lt;p&gt;You can find the code &lt;a href="https://github.com/isabelatravaglia/go-handlers-article"&gt;here&lt;/a&gt;. You can open it in a devcontainer if you are using VSCode.&lt;/p&gt;

&lt;p&gt;Thanks!&lt;/p&gt;

</description>
      <category>go</category>
      <category>docker</category>
    </item>
    <item>
      <title>Using GitHub Actions to build test workflows on a Rails on Docker app (+ Postgres and Selenium) leveraging Docker layer caching
</title>
      <dc:creator>Isa</dc:creator>
      <pubDate>Tue, 25 May 2021 18:46:03 +0000</pubDate>
      <link>https://dev.to/isabelatravaglia/using-github-actions-to-build-test-workflows-on-a-rails-on-docker-app-postgres-and-selenium-leveraging-docker-layer-caching-2hb8</link>
      <guid>https://dev.to/isabelatravaglia/using-github-actions-to-build-test-workflows-on-a-rails-on-docker-app-postgres-and-selenium-leveraging-docker-layer-caching-2hb8</guid>
      <description>&lt;p&gt;Ever since I've heard about Github Actions (from now on mentioned as GHA), I've been wanting to try it out in one of my projects. Also, I've been eager to write tests, as in the code bootcamp I took there wasn't an opportunity to implement them during the final project. So given those two topics I wanted to learn more about, I decided to couple them together and experiment creating a GitHub Actions workflow for tests. Although having come across tutorials/articles about setting up GHA workflows for Rails apps, I didn't find any specific tutorials explaining how to set up a GHA workflow to test a Rails on Docker app. Hence, I thought if I wrote an article explaining my approach I could help out someone.&lt;/p&gt;

&lt;p&gt;I'm assuming that those reading this article are already somehow familiar with GHA, Docker, and Rails, so I will focus on explaining the workflow setups.&lt;/p&gt;

&lt;p&gt;In the simple Rails app used to build the workflow, there are three services that we need to run the tests: the app itself, the database (Postgres), and a tool that allows us to perform the system tests by simulating a browser (Selenium). Hence, we need to build three containers, one for each of those services.&lt;/p&gt;

&lt;p&gt;I decided to try out two approaches for the workflow: one that uses GHA services containers and another one that builds those services based on Dockerfiles that we will define. In the end, my favorite approach ended up being the first one, as it is easier to set up and a little bit faster. Let's take a look at those approaches in detail.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Services Containers Approach&lt;/strong&gt;&lt;br&gt;
Our goal in setting up this workflow is to test the app every time there is a push action performed against our repository. There are only a few tests in the app (one model test and a couple of system tests) as the main purpose of this tutorial is to show a basic setup to implement a GHA workflow to run tests on every push (and not the testing methodology itself). We are using Rail's default test framework, Minitest.&lt;br&gt;
We will start by showing up the &lt;code&gt;test-services.yml&lt;/code&gt; file, placed inside the &lt;code&gt;.github/workflows&lt;/code&gt; folder.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Let's walk through each step of this configuration.&lt;/p&gt;

&lt;p&gt;First, we define which action will trigger this workflow: in our case, the push action will do it. Then we set up some env variables related to the test environment and specify which kind of runner we want to use (Ubuntu).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Test&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;RAILS_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
      &lt;span class="na"&gt;NODE_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt; &lt;span class="c1"&gt;# runner&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Next, we enter the services' part (which should be placed before the steps part). We must specify all the services we need to run our tests: in this case, Postgres and Selenium with the Chrome browser. By doing this, GHA will create one container for each of the services and a specific network so that the containers can communicate with each other. The network part is important for this setup because later we will need to connect to it to execute our tests. You can read more about GHA Services Containers and how to set them up &lt;a href="https://docs.github.com/en/actions/guides/about-service-containers"&gt;here&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;5432:5432&lt;/span&gt;
        &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;-&lt;/span&gt;
          &lt;span class="s"&gt;--health-cmd pg_isready&lt;/span&gt;
          &lt;span class="s"&gt;--health-interval 10s&lt;/span&gt;
          &lt;span class="s"&gt;--health-timeout 5s&lt;/span&gt;
          &lt;span class="s"&gt;--health-retries 5&lt;/span&gt;
      &lt;span class="na"&gt;chrome&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;selenium/standalone-chrome-debug&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;4444:4444&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;5900:5900&lt;/span&gt;
        &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/dev/shm:/dev/shm&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now we go to the "steps" part of the configuration. The first step, named "Output services network", will store, under the variable &lt;code&gt;services_network&lt;/code&gt;, the name of the network created for the services' containers. Later, on the step named "Run tests", we will access this variable and use it to connect our main container to the services container network. In the following step, "Checkout code", we use a GHA action called checkout that "&lt;a href="https://github.com/actions/checkout"&gt;checks-out your repository under $GITHUB_WORKSPACE, so your workflow can access it.&lt;/a&gt;"&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Output services network&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;network&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;echo ::set-output name=services_network::${{ job.container.network }}&lt;/span&gt;
        &lt;span class="s"&gt;echo ${{ job.container.network }}&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout code&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;

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

&lt;/div&gt;


&lt;p&gt;The next step relies on a Docker action that installs Docker Buildx in the runner used to run our workflow. Buildx is a CLI plugin that allows the use of extra features of the BuildKit builder toolkit. The main reason we're installing Buildx is to take advantage of Buildkit's ability to store Docker layers as artifacts, which will later let us speed up our workflow by caching the layers of our Docker builds. The &lt;code&gt;install: true&lt;/code&gt; option will make Buildx the default builder in Docker. In the following step, "Prepare tags", we are, well, preparing the tags for the images we'll build/use.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Docker Buildx&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/setup-buildx-action@v1&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;latest&lt;/span&gt;
        &lt;span class="na"&gt;install&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prepare Tags&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tags&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;TAG=$(echo $GITHUB_SHA | head -c7)&lt;/span&gt;
        &lt;span class="s"&gt;IMAGE="dev/test"&lt;/span&gt;
        &lt;span class="s"&gt;echo ::set-output name=tagged_image::${IMAGE}:${TAG}&lt;/span&gt;
        &lt;span class="s"&gt;echo ::set-output name=tag::${TAG}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Next, we use a Github Action that allows caching dependencies for a job. Basically, it lets us specify a path that will be used to cache and restore the dependencies. You can specify any folder you want, &lt;a href="https://github.com/docker/build-push-action/issues/175"&gt;as long as it's empty or containing only Buildx data&lt;/a&gt;. In the following step,  "Build Code", we will finally build our main image, which contains the app we want to test. We use Docker's build-push action, which, as the name implies, lets us build and push Docker images on GHA runners. In this step there are some important configurations, so let's go over each one of them. &lt;/p&gt;

&lt;p&gt;We start with the &lt;code&gt;push: false&lt;/code&gt;, meaning we are not interested in pushing this image anywhere as we just want it to be tested (based on the &lt;a href="https://github.com/docker/build-push-action/blob/master/action.yml"&gt;official documentation&lt;/a&gt;, this option is false by default, hence we could have it removed from the configuration and the result would be the same). &lt;/p&gt;

&lt;p&gt;Next comes &lt;code&gt;file: .ci-services/Dockerfile-services.ci&lt;/code&gt;, which tells the builder which Dockerfile to use for the build. The &lt;code&gt;load: true&lt;/code&gt; option will load the build result to Docker images. If we don't specify this option, the execution of the "Run tests" step will fail because it will not find the image built on this step to execute the tests. &lt;/p&gt;

&lt;p&gt;Next comes the two configurations that will allow the caching and caching retrieval for our builds: &lt;code&gt;cache-from&lt;/code&gt; and &lt;code&gt;cache-to&lt;/code&gt;. On &lt;code&gt;cache-from&lt;/code&gt; we are specifying the cache type (in our case is local, but it could also be on a registry), and its path (&lt;code&gt;src=/tmp/.buildx-main-cache&lt;/code&gt;). This path should seem familiar, as we set it up on the previous step "Cache main image layers". So here we are telling the builder to use the folder &lt;code&gt;/tmp/.buildx-main-cache&lt;/code&gt;, specified in the previous step, to retrieve the cache. &lt;/p&gt;

&lt;p&gt;As for the &lt;code&gt;cache-to&lt;/code&gt; option, it will write the new cache generated by the build on another folder called &lt;code&gt;/tmp/.buildx-main-cache-new&lt;/code&gt;. We could have used the same folder as in the &lt;code&gt;cache-from&lt;/code&gt; option, but as this build-push action &lt;a href="https://github.com/docker/build-push-action/issues/252"&gt;still doesn't offer a way to clear up the cache&lt;/a&gt;, we have to cache to a different location, remove the &lt;code&gt;/tmp/.buildx-main-cache&lt;/code&gt; folder and rename the &lt;code&gt;/tmp/.buildx-main-cache-new&lt;/code&gt; path to &lt;code&gt;/tmp/.buildx-main-cache&lt;/code&gt; (this removal and renaming commands are executed in the last step, "Move cache"). This way, we can prevent caching from becoming huge. &lt;/p&gt;

&lt;p&gt;Although we are using a regular Dockerfile in this tutorial (without multi-stage building), it is worth mentioning an important configuration that impacts multi-stage buildings: the &lt;code&gt;mode=max&lt;/code&gt; option inside the &lt;code&gt;cache-to&lt;/code&gt; option. The &lt;code&gt;mode=max&lt;/code&gt; option allows the caching of all layers generated by a multi-stage Dockerfile, and not just the final layer. If you are using a multi-stage Dockerfile and you need to access intermediate layers, you have to set &lt;code&gt;mode=max&lt;/code&gt; on the &lt;code&gt;cache-to&lt;/code&gt; entry. &lt;/p&gt;

&lt;p&gt;The last option is the &lt;code&gt;tag&lt;/code&gt;, which will receive as input the &lt;code&gt;tagged_image&lt;/code&gt; variable set on the "Prepare Tags" step.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cache main image layers&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v2&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/tmp/.buildx-main-cache&lt;/span&gt;
        &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ runner.os }}-buildx-main-${{ github.sha }}&lt;/span&gt;
        &lt;span class="na"&gt;restore-keys&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;${{ runner.os }}-buildx-main-&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build code&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/build-push-action@v2&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
        &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.ci-services/Dockerfile-services.ci&lt;/span&gt;
        &lt;span class="na"&gt;load&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;cache-from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;type=local,src=/tmp/.buildx-main-cache&lt;/span&gt;
        &lt;span class="na"&gt;cache-to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;type=local,mode=max,dest=/tmp/.buildx-main-cache-new&lt;/span&gt;
        &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.tags.outputs.tagged_image }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Finally, we have everything set to run our tests! Now we can run our docker-compose file with some env variables to build a container from the image created on the "Build code" step and execute our test service. The &lt;code&gt;TEST_IMAGE_TAG&lt;/code&gt; will store the name of the image we built on the "Build code" step and the &lt;code&gt;SERVICES_NETWORK&lt;/code&gt; will store the name of the network created for the services' container. We also specify which docker-compose file will be used (&lt;code&gt;.ci-services/docker-compose.test.services.yml&lt;/code&gt;) and the service we want to run (&lt;code&gt;test&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Tests&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;TEST_IMAGE_TAG=${{ steps.tags.outputs.tagged_image }} SERVICES_NETWORK=${{ steps.network.outputs.services_network }} docker-compose -f .ci-services/docker-compose.test.services.yml run test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now let's take a look at the docker-compose file we are using to understand how those env variables are used. &lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;It's straightforward to see that we are using the &lt;code&gt;TEST_IMAGE_TAG&lt;/code&gt; env variable to tell docker-compose which image to use to build the test service. Inside the test service, we are also specifying two env variables: &lt;code&gt;HUB_URL&lt;/code&gt; and &lt;code&gt;PARALLEL_WORKERS&lt;/code&gt;. The first is a configuration related to Selenium and it is used on the &lt;code&gt;application_system_test_case.rb&lt;/code&gt; file to tell Rails where to find the Chrome executable that will be used on system tests. Rails will then know that it should find and execute the Chrome browser located in the chrome container (defined on our services step), and not locally. &lt;/p&gt;

&lt;p&gt;The second env variable, &lt;code&gt;PARALLEL_WORKERS&lt;/code&gt;, just states that there should be only one "worker" to run the tests, which means the tests will run sequentially instead of simultaneously. Obviously, this isn't the most efficient setup, but creating more "workers" to run tests would mean launching multiple chrome containers, which would make our workflow a little bit more complicated. &lt;a href="https://vitobotta.com/2019/09/04/rails-parallel-system-tests-selenium-docker/"&gt;This article&lt;/a&gt; should help if you want to know more about how to scale the chrome service to run tests in parallel.&lt;/p&gt;

&lt;p&gt;Going back to the docker-compose file, after setting env variables, the test service will execute a command to run Rails tests (&lt;code&gt;rails test &amp;amp;&amp;amp; rails test:system&lt;/code&gt;). At last we can see how the &lt;code&gt;SERVICES_NETWORK&lt;/code&gt; env variable is used: it tells the test service container to connect to a specific network, the one created for the services' containers at the beginning of our workflow. Being connected to this network allows the test service container to access the database and the chrome service containers to run the tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Dockerfile Containers Approach&lt;/strong&gt;&lt;br&gt;
I won't get into a lot of details about this approach as it has a lot of similarities with the previous approach. Same as before, the goal here is to test the app every time there is a push action performed against our repository. The main difference is that instead of using GHA service containers, we will be using Docker's build-push action to build those containers. So for this approach, we have the following &lt;code&gt;test.yml&lt;/code&gt; placed on the &lt;code&gt;.github/workflows&lt;/code&gt; folder:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Given that we are now building our two service containers from the scratch instead of using GHA service containers, the &lt;code&gt;test.yml&lt;/code&gt; file becomes a little bit more extensive. The same steps we only had to perform for our main service in the previous approach now have to be performed for the postgres and chrome services as well: prepare the caching, build the images and move the cache. &lt;/p&gt;

&lt;p&gt;Besides, we also need to have two additional Dockerfiles: one for the postgres service and one for the chrome service. The docker-compose file is also a little bit different: it now has to use env variables to refer to the images built for the postgres and chrome services to execute the tests. Here's how it looks:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Given that docker-compose is building all the services together and automatically creating a network for all of them, we don't need to worry about connecting to a specific network to make things work as we did in the first approach.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wrapping up&lt;/strong&gt;&lt;br&gt;
As mentioned before, I prefer using the first approach (using GHA service containers) as is is more simple and a bit faster to execute. Anyway, I thought it would be worth mentioning the second approach (build the service containers with the build-push action) because it works and it was the first approach that I could implement.&lt;/p&gt;

&lt;p&gt;You can find the repository with those two workflows &lt;a href="https://github.com/isabelatravaglia/rails-dockerized-gha-testing"&gt;here&lt;/a&gt;. It has a .devcontainer folder, so you can use Vscode and &lt;a href="https://code.visualstudio.com/docs/remote/containers"&gt;open the code inside a container&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'm far from being an expert on GHA, Rails, or Docker, so please let me know if you think I can improve this workflow and/or if I wrote something wrong. 😃&lt;/p&gt;

&lt;p&gt;I'd also like to mention &lt;a href="https://evilmartians.com/chronicles/build-images-on-github-actions-with-docker-layer-caching"&gt;this excellent article&lt;/a&gt; that helped me to set up my workflow and understand how to leverage Docker layer caching on GHA.&lt;/p&gt;

&lt;p&gt;Thank you for reading this!&lt;/p&gt;

</description>
      <category>rails</category>
      <category>docker</category>
      <category>githubactions</category>
      <category>cicd</category>
    </item>
  </channel>
</rss>
