<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Bryan Primus Lumbantobing</title>
    <description>The latest articles on Forem by Bryan Primus Lumbantobing (@bryanprimus).</description>
    <link>https://forem.com/bryanprimus</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%2F371630%2F55069687-f852-419e-a793-4cab49c85d41.jpg</url>
      <title>Forem: Bryan Primus Lumbantobing</title>
      <link>https://forem.com/bryanprimus</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/bryanprimus"/>
    <language>en</language>
    <item>
      <title>Graceful Shutdown in Go</title>
      <dc:creator>Bryan Primus Lumbantobing</dc:creator>
      <pubDate>Fri, 13 Feb 2026 05:43:46 +0000</pubDate>
      <link>https://forem.com/bryanprimus/graceful-shutdown-in-go-2mlg</link>
      <guid>https://forem.com/bryanprimus/graceful-shutdown-in-go-2mlg</guid>
      <description>&lt;p&gt;This is cross-post from my blog: &lt;a href="https://bryanprim.us/blogs/graceful-shutdown-in-go" rel="noopener noreferrer"&gt;https://bryanprim.us/blogs/graceful-shutdown-in-go&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Visit original &lt;a href="https://bryanprim.us/blogs/graceful-shutdown-in-go" rel="noopener noreferrer"&gt;post&lt;/a&gt; for better reading experience where mermaid diagram properly rendered&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;http.Server.ListenAndServe&lt;/code&gt; is a blocking call, so if we run it in &lt;code&gt;main&lt;/code&gt; nothing after it runs.&lt;/li&gt;
&lt;li&gt;Start the server in a goroutine and keep &lt;code&gt;main&lt;/code&gt; free for handling shutdown.&lt;/li&gt;
&lt;li&gt;Send any server startup errors to a channel so &lt;code&gt;main&lt;/code&gt; can handle them.&lt;/li&gt;
&lt;li&gt;Listen for &lt;code&gt;SIGINT&lt;/code&gt; (Ctrl+C) and &lt;code&gt;SIGTERM&lt;/code&gt; (container orchestration) using &lt;code&gt;signal.NotifyContext&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;select&lt;/code&gt; to wait on multiple channels: shutdown signal and server error channel.&lt;/li&gt;
&lt;li&gt;On shutdown signal, create a deadline with &lt;code&gt;context.WithTimeout&lt;/code&gt; and call &lt;code&gt;server.Shutdown(ctx)&lt;/code&gt; to stop accepting new requests while letting in flight requests finish.&lt;/li&gt;
&lt;li&gt;Clean up resources like DB connections after shutdown completes.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;sync.WaitGroup&lt;/code&gt; to wait for other background goroutines to finish gracefully. (TODO)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bonus:&lt;/strong&gt; Migrate to &lt;code&gt;errgroup&lt;/code&gt; for better orchestration and automatic context cancellation across goroutines. (TODO)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Example Scenario
&lt;/h2&gt;

&lt;p&gt;Imagine we are eating at a restaurant. We still haven't finished our food. The restaurant is about to close. The restaurant would stop taking new orders or seating new customers, and a good restaurant will usually wait for us to finish our food before closing and cleaning up. That's exactly what a &lt;strong&gt;graceful shutdown&lt;/strong&gt; is. We are trying to become a good restaurant. Even so, a good restaurant won't wait forever. It sets a time limit. If we exceed it, we'll be asked to leave.&lt;/p&gt;

&lt;p&gt;In a typical web service using HTTP, we first stop accepting new incoming requests, then wait for in flight requests to finish within a time limit. We don't want to wait forever, of course. Once all requests have completed (or the deadline has passed), we can safely shut down the server and clean up resources such as closing DB connections and stopping any other background work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TB
  %% Graceful shutdown
  subgraph G[Graceful shutdown]
    direction TB
    subgraph GR[Restaurant]
      direction TB
      r1[Normal service]
      r2[Closing time approaching]
      r3[Stop seating new customers]
      r4[Stop taking new orders]
      r5[Let current diners finish eating]
      r6{Time limit reached?}
      r7[Ask remaining diners to leave]
      r8[Clean up and close]
      r1 --&amp;gt; r2 --&amp;gt; r3 --&amp;gt; r4 --&amp;gt; r5 --&amp;gt; r6
      r6 -- No --&amp;gt; r5
      r6 -- Yes --&amp;gt; r7 --&amp;gt; r8
    end
    subgraph GS[Web service]
      direction TB
      s1[Normal traffic]
      s2[Shutdown signal received]
      s3[Stop accepting new incoming HTTP requests]
      s4[Drain in flight requests]
      s5{Shutdown timeout reached?}
      s6[Force close remaining work]
      s7[Clean up resources]
      s8[Close DB connections]
      s9[Stop background operations]
      s1 --&amp;gt; s2 --&amp;gt; s3 --&amp;gt; s4 --&amp;gt; s5
      s5 -- No --&amp;gt; s4
      s5 -- Yes --&amp;gt; s6 --&amp;gt; s7 --&amp;gt; s8 --&amp;gt; s9
    end
  end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The opposite of this would be a &lt;strong&gt;forceful shutdown&lt;/strong&gt;, where the restaurant immediately kicks customers out even though they haven't finished their food, interrupts orders being prepared in the kitchen, and starts cleaning up, mopping the floor and stacking chairs, while customers still need them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TB
  %% Forceful shutdown
  subgraph F[Forceful shutdown]
    direction TB
    subgraph FR[Restaurant]
      direction TB
      fr1[Closing time approaching]
      fr2[Kick customers out immediately]
      fr3[Start cleaning now]
      fr4[Orders in kitchen get interrupted]
      fr1 --&amp;gt; fr2 --&amp;gt; fr3
      fr2 --&amp;gt; fr4
    end
    subgraph FS[Web service]
      direction TB
      fs1[Shutdown signal received]
      fs2[Immediately stop service]
      fs3[Drop active requests]
      fs4[Close connections now]
      fs5[Best effort cleanup]
      fs1 --&amp;gt; fs2 --&amp;gt; fs3 --&amp;gt; fs4 --&amp;gt; fs5
    end
  end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  HTTP Server in Go
&lt;/h2&gt;

&lt;p&gt;Go provides the &lt;code&gt;net/http&lt;/code&gt; package that we can use to run an HTTP server using the &lt;code&gt;server.ListenAndServe()&lt;/code&gt; method. This method is a blocking operation. It contains a never ending loop that waits for and serves each new connection or request. That's why any code placed after this call will never execute.&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;srv&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Serve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="n"&gt;net&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Listener&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="c"&gt;// ... setup ...&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="c"&gt;// &amp;lt;-- An infinite loop!&lt;/span&gt;
        &lt;span class="n"&gt;rw&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;l&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Accept&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c"&gt;// &amp;lt;-- THIS IS THE KEY BLOCKING CALL!&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;// ... error handling ...&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;srv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;newConn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;go&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;serve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connCtx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;// Handle each connection in a new goroutine&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;It blocks because it's an infinite loop with a blocking syscall (&lt;code&gt;Accept()&lt;/code&gt;), &lt;strong&gt;not because of channels&lt;/strong&gt;. It's the same reason any infinite loop blocks. The function literally never returns unless there's an error!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Moving to a Background Goroutine
&lt;/h2&gt;

&lt;p&gt;We just learned that &lt;code&gt;server.ListenAndServe()&lt;/code&gt; in Go is a blocking operation. Running it directly in the main function (which is the main goroutine) is a very bad idea because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We won't be able to run code after it.&lt;/li&gt;
&lt;li&gt;We won't be able to listen for external signals for graceful shutdown.&lt;/li&gt;
&lt;li&gt;We won't be able to run background workers or other concurrent tasks.&lt;/li&gt;
&lt;li&gt;Our deferred cleanup functions won't execute until the server stops (which may never happen in normal operation).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;cleanup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// This NEVER runs (unless server errors out)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Starting server..."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;// This blocks forever!&lt;/span&gt;
    &lt;span class="n"&gt;server&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="c"&gt;// NONE of this code ever executes:&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Server stopped"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// Never prints&lt;/span&gt;
    &lt;span class="n"&gt;startBackgroundWorker&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;       &lt;span class="c"&gt;// Never runs&lt;/span&gt;
    &lt;span class="n"&gt;listenForShutdownSignals&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;    &lt;span class="c"&gt;// Never runs&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Therefore, the best practice is to run it in a background goroutine. This unblocks the main goroutine so we can run other operations such as handling shutdown.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// This still blocks forever!&lt;/span&gt;
        &lt;span class="c"&gt;// But now in the scope of its own background goroutine&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;server&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="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="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&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="p"&gt;}()&lt;/span&gt;
    &lt;span class="c"&gt;// -- Code after this will execute --&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Communicating with Channels
&lt;/h2&gt;

&lt;p&gt;Moving &lt;code&gt;server.ListenAndServe&lt;/code&gt; to a background goroutine means the main goroutine never sees any error it returns. If the server fails to start, we usually want the whole program to exit right away, so anything that comes after it should never run.&lt;/p&gt;

&lt;p&gt;We need a way to communicate between goroutines: the background goroutine running the server, and the main goroutine. We can accomplish this using &lt;strong&gt;channels&lt;/strong&gt;. A channel is a thread safe queue. Values come out in the same order they went in. We can send values from any number of goroutines and receive them from any number of other goroutines without corrupted data or a race condition.&lt;/p&gt;

&lt;p&gt;First, we create a channel to carry our server error in the main goroutine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;serverErrors&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;error&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we send a value to that channel from our background goroutine using the &lt;code&gt;&amp;lt;-&lt;/code&gt; syntax.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&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;server&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="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="n"&gt;serverErrors&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we introduce a channel receive that waits for a value to arrive. A channel receive is a blocking operation as well, which means the code after it won't execute until a value is received. We can then use that error to exit the program early.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Creating a channel&lt;/span&gt;
    &lt;span class="n"&gt;serverErrors&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;error&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="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&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;server&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="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;// Send error to the channel&lt;/span&gt;
            &lt;span class="n"&gt;serverErrors&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&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="c"&gt;// Receiving from the channel&lt;/span&gt;
    &lt;span class="c"&gt;// This will block the main goroutine&lt;/span&gt;
    &lt;span class="n"&gt;serverError&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;serverErrors&lt;/span&gt;
    &lt;span class="c"&gt;// This will not execute until we receive a server error&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Blocked unless there is a server error"&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;serverError&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="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Program killed:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serverError&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;blockquote&gt;
&lt;p&gt;The buffered channel with capacity 1 &lt;code&gt;(make(chan error, 1))&lt;/code&gt; allows the goroutine to send one error without blocking, even if no receiver is ready yet. This prevents a potential deadlock if the server fails to start immediately, ensuring the error can be sent before the main goroutine reaches the receive statement.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Understanding Context
&lt;/h2&gt;

&lt;p&gt;Before we listen for shutdown signals, let's understand Go's &lt;code&gt;context&lt;/code&gt; package. It's central to how graceful shutdown works.&lt;/p&gt;

&lt;p&gt;Imagine ordering food on an app and then cancelling the order. Without context, the kitchen keeps cooking, a driver gets assigned, and the whole chain keeps going even though we cancelled. &lt;strong&gt;Context is how we tell the whole chain "stop, the user cancelled."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The equivalent use cases in our program are timeouts and cancellation. For example, a slow database query that might take 30 seconds when we want to give up after 5. Or a client (say, a web browser) that closes the connection before receiving the response. In that case, we might want to cancel expensive internal operations on the server to save resources. Context can also carry request scoped values through the call chain, such as a &lt;code&gt;requestId&lt;/code&gt; or &lt;code&gt;userId&lt;/code&gt; for each HTTP handler.&lt;/p&gt;

&lt;p&gt;When a context is cancelled or reaches its timeout, we are notified through &lt;code&gt;ctx.Done()&lt;/code&gt;, which returns a channel that closes when the context is done. Receiving from it (&lt;code&gt;&amp;lt;-ctx.Done()&lt;/code&gt;) is a blocking operation that waits until one of those events occurs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Listening for Shutdown Signals
&lt;/h2&gt;

&lt;p&gt;Now that &lt;code&gt;server.ListenAndServe()&lt;/code&gt; no longer blocks the main goroutine, we can handle graceful shutdown. &lt;code&gt;SIGTERM&lt;/code&gt; is the polite "please shut down" signal that the OS or orchestrator sends before it gets impatient and sends &lt;code&gt;SIGKILL&lt;/code&gt;, which forcibly kills the program. For example, Kubernetes sends &lt;code&gt;SIGTERM&lt;/code&gt; to a pod and waits for &lt;code&gt;terminationGracePeriodSeconds&lt;/code&gt; (30 seconds by default) before sending &lt;code&gt;SIGKILL&lt;/code&gt;. For local development, we'll also listen for &lt;code&gt;SIGINT&lt;/code&gt;, triggered by pressing &lt;code&gt;Ctrl+C&lt;/code&gt; in the terminal.&lt;/p&gt;

&lt;p&gt;We use &lt;code&gt;signal.NotifyContext&lt;/code&gt; to create a context that is automatically cancelled when one of our specified signals is received.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stop&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NotifyContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SIGINT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SIGTERM&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;defer stop()&lt;/code&gt; resets the signal behavior back to the default after we're done handling shutdown. This is important because if a second signal arrives during our graceful shutdown process, we want the program to terminate immediately rather than being caught by our handler again.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This root context can also be passed to dependent resources so they are aware of application level cancellation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stop&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NotifyContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SIGINT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SIGTERM&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c"&gt;// db pool example using pgx driver&lt;/span&gt;
    &lt;span class="n"&gt;db&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;pgxpool&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;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"postgres://postgres:password@localhost:5432/mydb"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;// background goroutine if any&lt;/span&gt;
    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;processQueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&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;
  
  
  Waiting on Multiple Channels with Select
&lt;/h2&gt;

&lt;p&gt;We now have two channels to wait on: &lt;code&gt;ctx.Done()&lt;/code&gt; for the shutdown signal, and &lt;code&gt;serverErrors&lt;/code&gt; for a server startup failure. We can't just receive from one because either event could happen first. This is exactly what Go's &lt;code&gt;select&lt;/code&gt; statement is for.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;select&lt;/code&gt; works like a &lt;code&gt;switch&lt;/code&gt; but for channel operations. It blocks until one of its cases can proceed, then executes that case.&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;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;serverErrors&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="c"&gt;// The server failed to start or crashed&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Server 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="k"&gt;return&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="c"&gt;// We received a shutdown signal (SIGINT or SIGTERM)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Shutdown signal received"&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 the server fails to start (e.g., port already in use), the first case fires and we exit immediately. If the server starts successfully and we later press &lt;code&gt;Ctrl+C&lt;/code&gt; or the orchestrator sends &lt;code&gt;SIGTERM&lt;/code&gt;, the second case fires and we begin graceful shutdown.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shutting Down the Server
&lt;/h2&gt;

&lt;p&gt;When the shutdown signal arrives, we need to tell the server to stop accepting new connections and wait for in flight requests to finish. This is what &lt;code&gt;server.Shutdown(ctx)&lt;/code&gt; does. We pass it a context with a timeout to set a deadline. We don't want to wait forever for slow requests.&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;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Shutdown signal received"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;shutdownCtx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;cancel&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;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Shutdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shutdownCtx&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="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Graceful shutdown failed:"&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;There are a few important details here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We create a &lt;strong&gt;new&lt;/strong&gt; context with &lt;code&gt;context.Background()&lt;/code&gt; rather than reusing &lt;code&gt;ctx&lt;/code&gt;, because &lt;code&gt;ctx&lt;/code&gt; is already cancelled (that's how we got here). The new timeout gives in flight requests up to 10 seconds to complete.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;server.Shutdown&lt;/code&gt; closes the listeners, then waits for all active connections to become idle. If the timeout expires before that happens, &lt;code&gt;Shutdown&lt;/code&gt; returns &lt;code&gt;context.DeadlineExceeded&lt;/code&gt; and any remaining connections are abandoned. The server did its best, but some requests may be cut off.&lt;/li&gt;
&lt;li&gt;When &lt;code&gt;Shutdown&lt;/code&gt; is called, the &lt;code&gt;ListenAndServe&lt;/code&gt; call in our background goroutine returns &lt;code&gt;http.ErrServerClosed&lt;/code&gt;. This is &lt;strong&gt;expected&lt;/strong&gt; during graceful shutdown and is not a real error. We need to filter it in our goroutine so it doesn't get sent to &lt;code&gt;serverErrors&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&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;server&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="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="o"&gt;&amp;amp;&amp;amp;&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;Is&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;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrServerClosed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;serverErrors&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&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;Without this filter, &lt;code&gt;serverErrors&lt;/code&gt; would receive a value on every graceful shutdown, making it look like the server crashed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cleaning Up Resources
&lt;/h2&gt;

&lt;p&gt;After the server has shut down, we can safely clean up resources like database connections. The simplest way to do this in Go is with &lt;code&gt;defer&lt;/code&gt; statements, which execute in LIFO (last in, first out) order when the function returns.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// ... setup ...&lt;/span&gt;
    &lt;span class="n"&gt;db&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;pgxpool&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;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;connStr&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="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&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;defer&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// Runs when main returns, after shutdown completes&lt;/span&gt;
    &lt;span class="c"&gt;// ... server start, select, shutdown ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because &lt;code&gt;defer&lt;/code&gt; runs when &lt;code&gt;main&lt;/code&gt; returns, and our &lt;code&gt;select&lt;/code&gt; + &lt;code&gt;Shutdown&lt;/code&gt; sequence happens before that return, we can be confident the DB pool is closed only after the server has finished processing all requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting It All Together
&lt;/h2&gt;

&lt;p&gt;Here is the complete implementation combining everything we've covered: starting the server in a background goroutine, communicating errors via a channel, listening for shutdown signals with context, using &lt;code&gt;select&lt;/code&gt; to wait on both, shutting down the server with a timeout, and cleaning up resources.&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;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"errors"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;
    &lt;span class="s"&gt;"os"&lt;/span&gt;
    &lt;span class="s"&gt;"os/signal"&lt;/span&gt;
    &lt;span class="s"&gt;"syscall"&lt;/span&gt;
    &lt;span class="s"&gt;"time"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/jackc/pgx/v5/pgxpool"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// ---------------------------------------------------------------&lt;/span&gt;
    &lt;span class="c"&gt;// Create a context that is cancelled on SIGINT or SIGTERM.&lt;/span&gt;
    &lt;span class="c"&gt;// ---------------------------------------------------------------&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stop&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NotifyContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SIGINT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SIGTERM&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c"&gt;// ---------------------------------------------------------------&lt;/span&gt;
    &lt;span class="c"&gt;// Connect to the database.&lt;/span&gt;
    &lt;span class="c"&gt;// ---------------------------------------------------------------&lt;/span&gt;
    &lt;span class="n"&gt;db&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;pgxpool&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;ctx&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;Getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DATABASE_URL"&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="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unable to create connection pool:"&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="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Connected to database"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;// ---------------------------------------------------------------&lt;/span&gt;
    &lt;span class="c"&gt;// Register routes.&lt;/span&gt;
    &lt;span class="c"&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;"/slow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Slow request started"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&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;Write&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="s"&gt;"done"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Slow request finished"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="c"&gt;// ---------------------------------------------------------------&lt;/span&gt;
    &lt;span class="c"&gt;// Set up the server.&lt;/span&gt;
    &lt;span class="c"&gt;// ---------------------------------------------------------------&lt;/span&gt;
    &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Addr&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;         &lt;span class="s"&gt;":8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Handler&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;DefaultServeMux&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ReadTimeout&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="m"&gt;10&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;WriteTimeout&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;IdleTimeout&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="m"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c"&gt;// ---------------------------------------------------------------&lt;/span&gt;
    &lt;span class="c"&gt;// Start the server in a background goroutine.&lt;/span&gt;
    &lt;span class="c"&gt;// Send any unexpected error to the channel.&lt;/span&gt;
    &lt;span class="c"&gt;// ---------------------------------------------------------------&lt;/span&gt;
    &lt;span class="n"&gt;serverErrors&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;error&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="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Server listening on"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Addr&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;server&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="c"&gt;// http.ErrServerClosed is expected when Shutdown is called.&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="o"&gt;&amp;amp;&amp;amp;&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;Is&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;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrServerClosed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;serverErrors&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&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="c"&gt;// ---------------------------------------------------------------&lt;/span&gt;
    &lt;span class="c"&gt;// Block until we receive a shutdown signal or a server error.&lt;/span&gt;
    &lt;span class="c"&gt;// ---------------------------------------------------------------&lt;/span&gt;
    &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;serverErrors&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Server 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="k"&gt;return&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// Reset signal behavior so a second signal exits immediately.&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Shutdown signal received, draining requests..."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c"&gt;// Give in flight requests a deadline to complete.&lt;/span&gt;
        &lt;span class="n"&gt;shutdownCtx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;cancel&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;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Shutdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shutdownCtx&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="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Graceful shutdown failed:"&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="k"&gt;return&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Server shut down gracefully"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c"&gt;// ---------------------------------------------------------------&lt;/span&gt;
    &lt;span class="c"&gt;// At this point, deferred functions run:&lt;/span&gt;
    &lt;span class="c"&gt;// - db.Close() closes the connection pool&lt;/span&gt;
    &lt;span class="c"&gt;// - stop() resets signal handling&lt;/span&gt;
    &lt;span class="c"&gt;// ---------------------------------------------------------------&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing It Manually
&lt;/h2&gt;

&lt;p&gt;Let's prove that this actually works. The idea is simple: register a slow endpoint, start the server, send a request to that endpoint, and hit &lt;code&gt;Ctrl+C&lt;/code&gt; while the request is still being processed. If graceful shutdown is working, the server should wait for the request to finish before exiting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Notice the slow endpoint.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the code above, we registered a &lt;code&gt;/slow&lt;/code&gt; handler that takes 5 seconds to respond. This gives us a window to send the shutdown signal while the request is in flight.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Start the server.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go run main.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We should see something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Connected to database
Server listening on :8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3: Send a request to the slow endpoint.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Open a second terminal and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:8080/slow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will hang for 5 seconds while the server processes the request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Send the shutdown signal.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Go back to the first terminal (where the server is running) and press &lt;code&gt;Ctrl+C&lt;/code&gt; while the curl request is still waiting. We should see something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Slow request started
Shutdown signal received, draining requests...
Slow request finished
Server shut down gracefully
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the order. The shutdown signal was received, but the server waited for the slow request to finish before shutting down. The curl request in the second terminal should return &lt;code&gt;done&lt;/code&gt; with no error.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens without graceful shutdown?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If we were to just kill the process (e.g., &lt;code&gt;kill -9&lt;/code&gt; or no shutdown handling at all), the curl request would fail with something like &lt;code&gt;curl: (52) Empty reply from server&lt;/code&gt; because the connection gets dropped mid response.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens if the request exceeds the shutdown timeout?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Try changing the sleep to 15 seconds (longer than our 10 second shutdown timeout). When we press &lt;code&gt;Ctrl+C&lt;/code&gt;, we'll see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Slow request started
Shutdown signal received, draining requests...
Graceful shutdown failed: context deadline exceeded
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server gave up waiting after 10 seconds and exited. The curl request will fail because the connection was closed before the response was sent. This is the timeout acting as a safety net so the server never hangs forever waiting for a stuck request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;That covers the core of graceful shutdown in Go. To recap what we built: we moved the server out of the main goroutine so it doesn't block everything, used a channel to catch startup errors, listened for OS signals through context, and used &lt;code&gt;select&lt;/code&gt; to react to whichever event comes first. When a shutdown signal arrives, we give in flight requests time to finish before cleaning up resources like the database connection.&lt;/p&gt;

&lt;p&gt;This is a solid foundation for any Go service. In a future post, we'll take it further with &lt;code&gt;sync.WaitGroup&lt;/code&gt; for waiting on background goroutines, and then refactor the whole thing using &lt;code&gt;errgroup&lt;/code&gt; for cleaner orchestration.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>backend</category>
      <category>timeout</category>
      <category>go</category>
    </item>
    <item>
      <title>Using SSH to Connect Local Git to Remote Repositories</title>
      <dc:creator>Bryan Primus Lumbantobing</dc:creator>
      <pubDate>Sat, 09 Nov 2024 08:40:30 +0000</pubDate>
      <link>https://forem.com/bryanprimus/using-ssh-to-connect-local-git-to-remote-repositories-501d</link>
      <guid>https://forem.com/bryanprimus/using-ssh-to-connect-local-git-to-remote-repositories-501d</guid>
      <description>&lt;p&gt;This is cross-post from my blog: &lt;a href="https://www.bryanprim.us/blogs/using-ssh-to-connect-local-git-to-remote-repositories" rel="noopener noreferrer"&gt;https://www.bryanprim.us/blogs/using-ssh-to-connect-local-git-to-remote-repositories&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Visit original &lt;a href="https://www.bryanprim.us/blogs/using-ssh-to-connect-local-git-to-remote-repositories" rel="noopener noreferrer"&gt;post&lt;/a&gt; for better reading experience where mermaid diagram properly rendered&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;SSH uses a public and private key pair instead of tokens, making it more secure and convenient for Git authentication.&lt;/li&gt;
&lt;li&gt;Generate keys with &lt;code&gt;ssh-keygen -t ed25519&lt;/code&gt;, then add the public key to our remote repository platform (GitHub, GitLab, Bitbucket).&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;ssh-agent&lt;/code&gt; and an SSH config file with &lt;code&gt;AddKeysToAgent&lt;/code&gt; and &lt;code&gt;UseKeychain&lt;/code&gt; so we only enter our passphrase once.&lt;/li&gt;
&lt;li&gt;Test the connection with &lt;code&gt;ssh -T git@github.com&lt;/code&gt; to verify everything is working.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GitHub, GitLab, Bitbucket and many more are platforms to host remote repositories using Git.&lt;br&gt;
The two most common ways to connect to a remote repository from local Git are HTTPS and SSH.&lt;/p&gt;

&lt;p&gt;Each has its own advantages and disadvantages, but throughout this blog post we will focus on SSH since it is the most commonly used method and it offers many benefits compared to HTTPS.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: This post assumes git is installed on the local machine. It can also be used as a practical guide for setting up a new SSH connection to a remote repository. With existing SSH keys, jump straight to &lt;a href="https://dev.to/blogs/using-ssh-to-connect-local-git-to-remote-repositories#generate-ssh-keys"&gt;Generating SSH Keys&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  SSH and Git
&lt;/h2&gt;

&lt;p&gt;SSH is a protocol that allows us to securely communicate between a local machine and a remote host. Git is a distributed version control system designed to track changes in files and directories over time.&lt;/p&gt;

&lt;p&gt;Git is a collaboration tool that allows us to work on projects collaboratively, and using SSH will help us communicate our changes to other people in a secure way.&lt;/p&gt;
&lt;h2&gt;
  
  
  Benefits of Using SSH over HTTPS
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Security
&lt;/h3&gt;

&lt;p&gt;When using HTTPS to connect to a remote repository, we typically use a Personal Access Token (PAT) for authentication. Platforms like GitHub deprecated password based HTTPS authentication back in 2021, so tokens are now the standard. The token and data are encrypted in transit, so they’re not easily readable by anyone on the network. However, we still need to enter or cache our token for each session, and if the token is leaked or stolen, anyone with it can access our repositories until we revoke it.&lt;/p&gt;

&lt;p&gt;When using SSH, we use a private and public key pair for authentication, which works differently:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Private Key&lt;/strong&gt; is stored only on our local machine. Think of it as a digital signature unique to us. It is encrypted and often secured with a passphrase.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Public Key&lt;/strong&gt; is stored on the remote server. This is what our private key "unlocks" to prove it is really us.&lt;/li&gt;
&lt;/ul&gt;




&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sequenceDiagram
    participant Local as Our Machine
    participant Server as Remote Server
    Local-&amp;gt;&amp;gt;Server: Connection request
    Server-&amp;gt;&amp;gt;Local: Send challenge (newly generated)
    Note over Local: Solve challenge using Private Key
    Local-&amp;gt;&amp;gt;Server: Send response
    Note over Server: Verify response using Public Key
    Server-&amp;gt;&amp;gt;Local: Authenticated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Our local machine uses the private key to prove our identity without sending the private key itself over the network. Instead, the server sends a challenge to our local machine that only our private key can solve. This challenge is newly generated every time we try to connect to the remote server. After our machine solves the challenge, the server verifies the response using our public key. If it matches, we are authenticated. Because the challenge changes every time, intercepting the network traffic won't help an attacker gain access.&lt;/p&gt;
&lt;h3&gt;
  
  
  Convenience
&lt;/h3&gt;

&lt;p&gt;Using SSH is more convenient than HTTPS for connecting to a remote server because it eliminates the need to enter our token every time. Instead, we can use an SSH agent, which securely stores our passphrase in memory and handles the authentication process for subsequent connections.&lt;/p&gt;
&lt;h2&gt;
  
  
  Generate SSH Keys
&lt;/h2&gt;



&lt;p&gt;There are several algorithms that can be used to generate SSH keys. In this blog post, we will use &lt;strong&gt;Ed25519 (Edwards-curve Digital Signature Algorithm)&lt;/strong&gt; because it is modern and faster than other algorithms.&lt;/p&gt;
&lt;h3&gt;
  
  
  Check Existing SSH Keys
&lt;/h3&gt;

&lt;p&gt;Before generating a new SSH key, we should check if we already have one because we don't want to accidentally overwrite an existing key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;### input&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-al&lt;/span&gt; ~/.ssh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look for &lt;code&gt;id_ed25519&lt;/code&gt; and &lt;code&gt;id_ed25519.pub&lt;/code&gt; as these are the default file names for SSH key generation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;### output&lt;/span&gt;
total 40
drwx------@  7 username  staff   224 Nov  5 15:45 &lt;span class="nb"&gt;.&lt;/span&gt;
drwxr-x---+ 42 username  staff  1344 Nov  5 19:18 ..
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt;@  1 username  staff   185 Sep 11 19:00 config
&lt;span class="nt"&gt;-rw-------&lt;/span&gt;@  1 username  staff   464 Sep 11 18:24 id_ed25519
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt;@  1 username  staff   104 Sep 11 18:24 id_ed25519.pub
&lt;span class="nt"&gt;-rw-------&lt;/span&gt;@  1 username  staff  1842 Oct 25 16:47 known_hosts
&lt;span class="nt"&gt;-rw-------&lt;/span&gt;@  1 username  staff  1106 Oct 25 16:43 known_hosts.old
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we see &lt;code&gt;id_ed25519&lt;/code&gt; and &lt;code&gt;id_ed25519.pub&lt;/code&gt; then we already have SSH keys. If we don't want to overwrite them, we will need to specify a different path for the next key generation. If we want to use our existing SSH keys, we can skip the creation step and continue to &lt;a href="https://dev.to/blogs/using-ssh-to-connect-local-git-to-remote-repositories#connect-to-a-remote-repository"&gt;Connecting to a remote repository&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a New SSH Key
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# input&lt;/span&gt;
ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; ed25519 &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s2"&gt;"your_email@example.com"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;-t&lt;/code&gt; is the algorithm type, &lt;code&gt;-C (optional)&lt;/code&gt; is custom comment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# output&lt;/span&gt;
Generating public/private ed25519 key pair.
Enter file &lt;span class="k"&gt;in &lt;/span&gt;which to save the key &lt;span class="o"&gt;(&lt;/span&gt;/Users/username/.ssh/id_ed25519&lt;span class="o"&gt;)&lt;/span&gt;:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Press enter to accept the default path or provide a custom path with the file name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# output&lt;/span&gt;
Enter passphrase &lt;span class="o"&gt;(&lt;/span&gt;empty &lt;span class="k"&gt;for &lt;/span&gt;no passphrase&lt;span class="o"&gt;)&lt;/span&gt;:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Passphrase is optional, but I &lt;strong&gt;highly recommend&lt;/strong&gt; entering one to protect the use of our private SSH key.&lt;br&gt;
We will be using &lt;code&gt;ssh-agent&lt;/code&gt; later so that we don't have to enter the passphrase every time we make a connection.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# output&lt;/span&gt;
&lt;span class="c"&gt;# dummy data&lt;/span&gt;
Your identification has been saved &lt;span class="k"&gt;in&lt;/span&gt; /Users/username/.ssh/id_ed25519
Your public key has been saved &lt;span class="k"&gt;in&lt;/span&gt; /Users/username/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:nds92nn/dsNDsadbjndkansdjsknadkj your_email@example.com
The key&lt;span class="s1"&gt;'s randomart image is:
+--[ED25519 256]--+
|             o.**|
|              o**|
|             ++**|
|          .   +=*|
|        S. . +o*o|
|         o..=.o.=|
|          --o+o+o|
|         ..  o=*=|
|             o+E=|
+----[SHA256]-----+
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Locate the Newly Generated SSH Key
&lt;/h3&gt;

&lt;p&gt;Verify whether the key was generated successfully by running:&lt;/p&gt;

&lt;p&gt;For public key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# input&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.ssh/id_ed25519.pub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adjust the path if using a custom one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# output&lt;/span&gt;
&lt;span class="c"&gt;# dummy data&lt;/span&gt;
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB8Dkha0+XZ9sjd34fGFkeGZkHZck9TZx7Hnm0Dd9e2j bry@yourhost
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For private key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# input&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.ssh/id_ed25519
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adjust the path if using a custom one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# output&lt;/span&gt;
&lt;span class="c"&gt;# dummy data&lt;/span&gt;
&lt;span class="nt"&gt;-----BEGIN&lt;/span&gt; OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktZDI1NTE5AAAAIB8Dkha0+XZ9sjd34fGFkeGZkHZck9TZx7Hnm
0Dd9e2j+AAAAAAAAAAAAAAAAAAEAAAAIB8Dkha0+XZ9sjd34fGFkeGZkHZck9TZx7
Hnm0Dd9e2j+AAAAAlkdiJHDU6FOSZDa7yxptjMNxzHxgtid7YKm2GFjdjXaAAAAAE
2jxKVmksl3Dbjoef1H5/tf1sgYnJe+JqfgvUhrBgkgmfhdAAAAAAEAAAXLS1mhdhd
bHJdkfHZ9GFFX3NNMgVdk+KfbkhTgyg&lt;span class="o"&gt;==&lt;/span&gt;
&lt;span class="nt"&gt;-----END&lt;/span&gt; OPENSSH PRIVATE KEY-----
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything is done correctly, we should see our public and private key in the output as shown above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect to a Remote Repository
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Add Public Key to Remote Repository
&lt;/h3&gt;

&lt;p&gt;We need to add our public key to the remote repository platform we are using. Visit one of these links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/settings/keys" rel="noopener noreferrer"&gt;https://github.com/settings/keys&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitLab: &lt;a href="https://gitlab.com/-/profile/keys" rel="noopener noreferrer"&gt;https://gitlab.com/-/profile/keys&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Bitbucket: &lt;a href="https://bitbucket.org/account/settings/ssh-keys/" rel="noopener noreferrer"&gt;https://bitbucket.org/account/settings/ssh-keys/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Links provided may become invalid in the future as I don't have control over any changes the platforms might make.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Click &lt;strong&gt;New SSH Key&lt;/strong&gt; or &lt;strong&gt;Add SSH Key&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Give the key a descriptive name.&lt;/p&gt;

&lt;p&gt;Copy the public key from the local machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# input&lt;/span&gt;
pbcopy &amp;lt; ~/.ssh/id_ed25519.pub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adjust the path if using a custom one.&lt;/p&gt;

&lt;p&gt;Paste the public key in the &lt;strong&gt;Key&lt;/strong&gt; field and save.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add Our Private SSH Key to the &lt;code&gt;ssh-agent&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;First, we need to start the &lt;code&gt;ssh-agent&lt;/code&gt; running in the background.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mac:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;exec &lt;/span&gt;ssh-agent zsh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;WSL using bash:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;exec &lt;/span&gt;ssh-agent bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Windows (I haven't tested this):&lt;/strong&gt;&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;start-ssh-agent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Start-Service ssh-agent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then add the private key to the agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-add C:&lt;span class="se"&gt;\p&lt;/span&gt;ath&lt;span class="se"&gt;\t&lt;/span&gt;o&lt;span class="se"&gt;\y&lt;/span&gt;our&lt;span class="se"&gt;\k&lt;/span&gt;ey
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Set Up the SSH Config File
&lt;/h3&gt;

&lt;p&gt;Check if we have an existing &lt;code&gt;ssh&lt;/code&gt; config file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.ssh/config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we don't have one, create a new config file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch&lt;/span&gt; ~/.ssh/config
open ~/.ssh/config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update our config file based on the remote host we are using.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# For GitHub&lt;/span&gt;
Host github.com
  AddKeysToAgent &lt;span class="nb"&gt;yes
  &lt;/span&gt;UseKeychain &lt;span class="nb"&gt;yes&lt;/span&gt; &lt;span class="c"&gt;# macOS only&lt;/span&gt;
  IdentityFile ~/.ssh/id_ed25519
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;UseKeychain&lt;/strong&gt; will save our passphrase in the keychain, which will be used to unlock our private key when we make a connection.&lt;br&gt;
This way, we don’t have to enter the passphrase again every time we start a new session or restart our computer.&lt;/p&gt;
&lt;h3&gt;
  
  
  Test the SSH Connection
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# input&lt;/span&gt;
&lt;span class="c"&gt;# github&lt;/span&gt;
ssh &lt;span class="nt"&gt;-T&lt;/span&gt; git@github.com
&lt;span class="c"&gt;# gitlab&lt;/span&gt;
ssh &lt;span class="nt"&gt;-T&lt;/span&gt; git@gitlab.com
&lt;span class="c"&gt;# bitbucket&lt;/span&gt;
ssh &lt;span class="nt"&gt;-T&lt;/span&gt; git@bitbucket.org
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If everything goes well, we should see the following output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# output&lt;/span&gt;
Hi username! You&lt;span class="s1"&gt;'ve successfully authenticated, but GitHub does not provide shell access.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we are ready to connect, clone repositories, and push changes to our remote repository!&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;To recap what we covered in this post: we started by understanding why SSH is a better choice than HTTPS for connecting to remote repositories. Then we generated an Ed25519 key pair, added the public key to a remote repository platform, configured the SSH agent and SSH config file for convenience, and finally tested the connection. We should now be able to push, pull, and clone repositories over SSH without entering credentials every time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Questions Worth Exploring
&lt;/h2&gt;

&lt;p&gt;These topics go deeper into some of the concepts we touched on in this post. I will cover them in future posts.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Why is a passphrase necessary?&lt;/strong&gt; We mentioned that a passphrase protects our private key, but what exactly happens if someone gets access to our private key without a passphrase?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What is ssh-agent and how does it work?&lt;/strong&gt; We used &lt;code&gt;ssh-agent&lt;/code&gt; to avoid entering our passphrase repeatedly, but how does it actually store and manage our keys in memory?&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://superuser.com/a/1526796/1209426" rel="noopener noreferrer"&gt;Why SSH considered safer than HTTPS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent" rel="noopener noreferrer"&gt;Generating SSH Key and adding it to SSH Agent&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chatgpt.com/share/67287c1e-3994-8011-8e76-e99c8999680c" rel="noopener noreferrer"&gt;SSH vs HTTPS ChatGPT discussion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ssh</category>
      <category>github</category>
      <category>git</category>
      <category>security</category>
    </item>
    <item>
      <title>What Are Web Components, and Why Use Them? A React Example</title>
      <dc:creator>Bryan Primus Lumbantobing</dc:creator>
      <pubDate>Mon, 13 Sep 2021 15:25:32 +0000</pubDate>
      <link>https://forem.com/bryanprimus/what-and-why-web-component-react-example-4cfm</link>
      <guid>https://forem.com/bryanprimus/what-and-why-web-component-react-example-4cfm</guid>
      <description>&lt;h2&gt;
  
  
  What Are Web Components?
&lt;/h2&gt;

&lt;p&gt;Let's start with the &lt;strong&gt;definition&lt;/strong&gt; of Web Components.&lt;/p&gt;

&lt;p&gt;Based on &lt;a href="https://www.webcomponents.org/" rel="noopener noreferrer"&gt;webcomponents.org&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Web components are a set of web platform APIs that allow you to create new custom, reusable, encapsulated HTML tags to use in web pages and web apps. Custom components and widgets built on Web Component standards work across modern browsers and can be used with any JavaScript library or framework that works with HTML.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;From this definition, we know that using web components allows us to create reusable components for any project that uses HTML. Web components are essentially a set of custom, self-contained HTML tags, not tied to any specific library or framework.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Does This Matter?
&lt;/h2&gt;

&lt;p&gt;Let's say your company runs multiple web applications. One is built in React, one in Vue, and another in plain HTML. Your design team wants a consistent &lt;code&gt;&amp;lt;app-button&amp;gt;&lt;/code&gt; that looks and behaves the same everywhere.&lt;/p&gt;

&lt;p&gt;With a framework component, you'd have to rebuild it three times, once per stack. With a web component, you build it once and it just works everywhere. That's the core value.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A web component works the same way in a React app, a Vue app, or no framework at all.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This matters especially if you're building a &lt;strong&gt;shared UI library&lt;/strong&gt;, such as a design system, a component kit, or any set of elements meant to be consumed across different teams or projects. Web components let you ship those elements without forcing everyone to adopt the same framework.&lt;/p&gt;

&lt;p&gt;Real companies have taken this approach. GitHub started small, creating components like &lt;code&gt;&amp;lt;relative-time&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;local-time&amp;gt;&lt;/code&gt;, then gradually scaled up to more complex ones like &lt;code&gt;&amp;lt;markdown-toolbar-element&amp;gt;&lt;/code&gt;. Web components gave them modularity and reusability, letting them build portable elements that fit into their existing codebase without committing to any specific framework.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Three Building Blocks
&lt;/h2&gt;

&lt;p&gt;Web components are built on three browser APIs that work together:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Custom Elements&lt;/strong&gt; define what your tag does. You create a class, register it under a tag name, and the browser treats it like any built-in HTML element.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shadow DOM&lt;/strong&gt; gives your component its own isolated DOM tree. Styles defined inside it won't leak out to the rest of the page, and global styles won't bleed in either. Shadow DOM fixes CSS and DOM scoping problems. Without any extra tools or naming conventions, you can bundle CSS with markup, hide implementation details, and author self-contained components in vanilla JavaScript.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HTML Templates&lt;/strong&gt; let you define markup with &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt; that is parsed but not rendered until you explicitly use it, making it efficient to reuse structure across multiple instances.&lt;/p&gt;

&lt;p&gt;These three work independently, but combining them is where you get true encapsulation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building a Web Component from Scratch
&lt;/h2&gt;

&lt;p&gt;Let's build a minimal &lt;code&gt;&amp;lt;user-greeting&amp;gt;&lt;/code&gt; component that accepts a &lt;code&gt;name&lt;/code&gt; attribute and renders a styled greeting. This walks through the whole flow end to end.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Define the component file (&lt;code&gt;user-greeting.js&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserGreeting&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;connectedCallback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 1. Create a shadow root to isolate this component's DOM and styles&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shadow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attachShadow&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;open&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Read the attribute passed in from HTML&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stranger&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Build the internal structure&lt;/span&gt;
    &lt;span class="nx"&gt;shadow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
      &amp;lt;style&amp;gt;
        p {
          font-family: sans-serif;
          color: #333;
          background: #f0f4ff;
          padding: 8px 12px;
          border-radius: 6px;
          display: inline-block;
        }
      &amp;lt;/style&amp;gt;
      &amp;lt;p&amp;gt;Hello, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;!&amp;lt;/p&amp;gt;
    `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// 4. Register the tag name&lt;/span&gt;
&lt;span class="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user-greeting&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;UserGreeting&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Use it in HTML&lt;/strong&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="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"./user-greeting.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;user-greeting&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"Ada"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/user-greeting&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;user-greeting&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"Grace"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/user-greeting&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Both elements render independently with the same component. The &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; inside the shadow DOM only affects what's inside it and won't touch anything else on the page.&lt;/p&gt;

&lt;p&gt;Note two things here: the tag name &lt;strong&gt;must contain a hyphen&lt;/strong&gt; (like &lt;code&gt;user-greeting&lt;/code&gt;) to distinguish custom elements from built-in ones, and you register it once with &lt;code&gt;customElements.define()&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lifecycle Callbacks
&lt;/h2&gt;

&lt;p&gt;Web components expose lifecycle callbacks you can hook into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;connectedCallback&lt;/code&gt;:&lt;/strong&gt; Runs when the element is inserted into the DOM. This is where you typically do your setup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;disconnectedCallback&lt;/code&gt;:&lt;/strong&gt; Runs when the element is removed from the DOM. Good for cleanup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;attributeChangedCallback&lt;/code&gt;:&lt;/strong&gt; Runs when one of the element's observed attributes changes. Requires you to declare which attributes to watch via &lt;code&gt;static get observedAttributes()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;adoptedCallback&lt;/code&gt;:&lt;/strong&gt; Runs when the element is moved to a new document.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want the component to react when the &lt;code&gt;name&lt;/code&gt; attribute changes, here's how you wire that up:&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;class&lt;/span&gt; &lt;span class="nc"&gt;UserGreeting&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;observedAttributes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// declare what to watch&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;connectedCallback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;attributeChangedCallback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// re-render when attribute changes&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attachShadow&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;open&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stranger&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;p&amp;gt;Hello, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;!&amp;lt;/p&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user-greeting&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;UserGreeting&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Using a Web Component Inside React
&lt;/h2&gt;

&lt;p&gt;Because React renders to HTML, you can drop any web component directly into JSX.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Welcome&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;user&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;greeting&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Ada"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;user&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;greeting&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;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;One thing to note: when using a web component inside JSX, you use &lt;code&gt;class&lt;/code&gt; instead of &lt;code&gt;className&lt;/code&gt;. This is because web components are native HTML elements, not React elements, and &lt;code&gt;className&lt;/code&gt; is a JSX-specific convention for React's own DOM handling.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// React element: use className&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"card"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;// Web component (native HTML element): use class&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;user&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;greeting&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"card"&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Ada"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;user&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;greeting&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also worth knowing: prior to React 19, custom events from web components didn't propagate through React's event system automatically, so you had to attach them via &lt;code&gt;addEventListener&lt;/code&gt; manually. React 19 adds native support for custom events, allowing developers to add event listeners using the familiar &lt;code&gt;on + CustomEventName&lt;/code&gt; convention, just like standard events such as &lt;code&gt;onClick&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Using React Inside a Web Component
&lt;/h2&gt;

&lt;p&gt;You can also go the other direction and render a React component inside a web component. This is useful when migrating an existing React app toward a web component based architecture.&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="nx"&gt;ReactDOM&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;react-dom/client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SearchWidget&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;connectedCallback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mountPoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;span&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attachShadow&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;open&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mountPoint&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;query&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.google.com/search?q=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Render a React component inside the shadow DOM&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ReactDOM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mountPoint&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/a&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="err"&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;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;search-widget&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SearchWidget&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://legacy.reactjs.org/docs/web-components.html" rel="noopener noreferrer"&gt;Example adapted from the React documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is then usable in plain HTML like any other custom element:&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;search-widget&lt;/span&gt; &lt;span class="na"&gt;query=&lt;/span&gt;&lt;span class="s"&gt;"web components"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/search-widget&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  A Note on Tooling
&lt;/h2&gt;

&lt;p&gt;Web components work in all major browsers without any framework or build tool. For simple cases, a &lt;code&gt;&amp;lt;script type="module"&amp;gt;&lt;/code&gt; pointing to your component file is all you need.&lt;/p&gt;

&lt;p&gt;That said, when you're building a larger project or integrating with React, a bundler like &lt;strong&gt;Webpack&lt;/strong&gt; or &lt;strong&gt;Vite&lt;/strong&gt; is recommended, especially if you're writing JSX inside a web component as in the example above.&lt;/p&gt;

&lt;p&gt;If you find the raw API verbose, libraries like &lt;strong&gt;&lt;a href="https://lit.dev/" rel="noopener noreferrer"&gt;Lit&lt;/a&gt;&lt;/strong&gt; offer a lightweight layer on top that adds reactive properties and template rendering while keeping you on web standards.&lt;/p&gt;




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

&lt;p&gt;Web components solve one specific problem really well: &lt;strong&gt;write once, use anywhere&lt;/strong&gt;. They're not a replacement for React or Vue, but they work alongside them. The real advantage shows up when you're building something meant to be shared across different applications or frameworks, such as a design system, a utility widget, or a standalone UI element that needs to outlive any single framework choice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next:&lt;/strong&gt; Try building a small shared component with web components, or explore &lt;a href="https://lit.dev/" rel="noopener noreferrer"&gt;Lit&lt;/a&gt; for a more ergonomic authoring experience.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.webcomponents.org/" rel="noopener noreferrer"&gt;webcomponents.org&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components" rel="noopener noreferrer"&gt;MDN: Web Components&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.dev/articles/shadowdom-v1" rel="noopener noreferrer"&gt;web.dev: Shadow DOM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://legacy.reactjs.org/docs/web-components.html" rel="noopener noreferrer"&gt;React Docs: Web Components&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://lit.dev/" rel="noopener noreferrer"&gt;Lit: Web Components Library&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>component</category>
      <category>webcomponents</category>
    </item>
  </channel>
</rss>
