<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Shinji NAKAMATSU</title>
    <description>The latest articles on DEV Community by Shinji NAKAMATSU (@snaka).</description>
    <link>https://dev.to/snaka</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%2F42655%2F8293a995-032b-4afa-9b0c-24f4a9aa52c2.jpg</url>
      <title>DEV Community: Shinji NAKAMATSU</title>
      <link>https://dev.to/snaka</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/snaka"/>
    <language>en</language>
    <item>
      <title>[Boost]</title>
      <dc:creator>Shinji NAKAMATSU</dc:creator>
      <pubDate>Tue, 17 Dec 2024 04:43:00 +0000</pubDate>
      <link>https://dev.to/snaka/-1kp0</link>
      <guid>https://dev.to/snaka/-1kp0</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/atsushii" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1004371%2F8870c52b-35b4-43b4-a2ab-1d94aaeb2626.jpeg" alt="atsushii"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/atsushii/deploy-your-application-to-ecs-via-cicd-with-ecspresso-34n0" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Deploy Your Application to ECS via CI/CD with Ecspresso&lt;/h2&gt;
      &lt;h3&gt;Atsushi Miyamoto ・ May 1 '23&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#ecs&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#deploy&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#cicd&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#githubactions&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>aws</category>
      <category>cicd</category>
      <category>devops</category>
      <category>automation</category>
    </item>
    <item>
      <title>Implementing Secure Access Control using AWS WAF with IP Address and BASIC Authentication</title>
      <dc:creator>Shinji NAKAMATSU</dc:creator>
      <pubDate>Sat, 21 Oct 2023 06:15:46 +0000</pubDate>
      <link>https://dev.to/snaka/implementing-secure-access-control-using-aws-waf-with-ip-address-and-basic-authentication-45hn</link>
      <guid>https://dev.to/snaka/implementing-secure-access-control-using-aws-waf-with-ip-address-and-basic-authentication-45hn</guid>
      <description>&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@dayday95?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Damon Lam&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/closed-blue-roller-shutter-kA2xo_jOhtE?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;AWS WAF is a Firewall service that can be attached to CloudFront or ALB. By defining a Web ACL (Access Control List), you can block or allow access based on specific conditions.&lt;/p&gt;

&lt;p&gt;It is common to want to restrict who or where can access your service while developing a new service to be exposed on the internet.&lt;/p&gt;

&lt;p&gt;In this article, we share how to combine AWS WAF for allowing access based on specific IP addresses (CIDR) and BASIC authentication.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Allow access if it matches the allowed IP addresses (CIDR)&lt;/li&gt;
&lt;li&gt;Allow access if the request header contains correct BASIC authentication information&lt;/li&gt;
&lt;li&gt;Block all other access&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Overview of the Mechanism
&lt;/h2&gt;

&lt;p&gt;Here's a summary of the mechanism:&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Default to "allow access"

&lt;ul&gt;
&lt;li&gt;Assuming that when opening the service, just removing the rule would remove the access restriction&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Block access if the following (AND) conditions are met:

&lt;ul&gt;
&lt;li&gt;Not an allowed IP address&lt;/li&gt;
&lt;li&gt;Lacks BASIC authentication information&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;When blocking access, request BASIC authentication with a custom response (&lt;code&gt;WWW-Authenticate&lt;/code&gt; header is returned)&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Terraform Code
&lt;/h2&gt;

&lt;p&gt;Here's the code snippet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/waf/variables.tf&lt;/span&gt;
&lt;span class="c1"&gt;// Module parameters&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"allowed_ip_list"&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"basic_auth"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;user&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
    &lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/waf/main.tf&lt;/span&gt;
&lt;span class="k"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/aws"&lt;/span&gt;
      &lt;span class="nx"&gt;configuration_aliases&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;virginia&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Pre-calculate the expected value for the Authorization header&lt;/span&gt;
  &lt;span class="nx"&gt;credential&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;base64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;basic_auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;basic_auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_wafv2_web_acl"&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;provider&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;virginia&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;"example-web-acl"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Web ACL example"&lt;/span&gt;
  &lt;span class="nx"&gt;scope&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"CLOUDFRONT"&lt;/span&gt;

  &lt;span class="c1"&gt;// Set the default action to allow&lt;/span&gt;
  &lt;span class="nx"&gt;default_action&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;allow&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// BASIC Authentication&lt;/span&gt;
  &lt;span class="nx"&gt;rule&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;"basic-auth"&lt;/span&gt;
    &lt;span class="nx"&gt;priority&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;

    &lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;block&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Set the action to block for the conditions mentioned later&lt;/span&gt;
        &lt;span class="c1"&gt;// Also, return a custom response requesting username and&lt;/span&gt;
        &lt;span class="c1"&gt;// password input for BASIC authentication&lt;/span&gt;
        &lt;span class="nx"&gt;custom_response&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;response_code&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;401&lt;/span&gt;
          &lt;span class="nx"&gt;response_header&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;"WWW-Authenticate"&lt;/span&gt;
            &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Basic realm=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Secure Area&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// Conditions for Block (AND) :&lt;/span&gt;
    &lt;span class="nx"&gt;statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;and_statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;// Condition 1&lt;/span&gt;
          &lt;span class="c1"&gt;// The source IP address is not included in the allowed IP list&lt;/span&gt;
          &lt;span class="nx"&gt;not_statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="nx"&gt;ip_set_reference_statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_wafv2_ip_set&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allowed_ips&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
              &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;// Condition 2&lt;/span&gt;
          &lt;span class="c1"&gt;// The Authorization header does not contain&lt;/span&gt;
          &lt;span class="nx"&gt;not_statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="nx"&gt;byte_match_statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;positional_constraint&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"EXACTLY"&lt;/span&gt;
                &lt;span class="nx"&gt;search_string&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Basic &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;credential&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
                &lt;span class="nx"&gt;field_to_match&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                  &lt;span class="nx"&gt;single_header&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;"authorization"&lt;/span&gt;
                  &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="nx"&gt;text_transformation&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                  &lt;span class="nx"&gt;priority&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
                  &lt;span class="nx"&gt;type&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"NONE"&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
              &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;visibility_config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;cloudwatch_metrics_enabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="nx"&gt;metric_name&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"example-basic-auth"&lt;/span&gt;
      &lt;span class="nx"&gt;sampled_requests_enabled&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;visibility_config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cloudwatch_metrics_enabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;metric_name&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"example-web-acl"&lt;/span&gt;
    &lt;span class="nx"&gt;sampled_requests_enabled&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Set of allowed IP addresses for access&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_wafv2_ip_set"&lt;/span&gt; &lt;span class="s2"&gt;"allowed_ips"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;provider&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;virginia&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;"example-allowed-ips"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Authorized IP addresses"&lt;/span&gt;
  &lt;span class="nx"&gt;scope&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"CLOUDFRONT"&lt;/span&gt;
  &lt;span class="nx"&gt;ip_address_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"IPV4"&lt;/span&gt;
  &lt;span class="nx"&gt;addresses&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allowed_ip_list&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  IP Address-based Access Control Settings
&lt;/h2&gt;

&lt;p&gt;To perform access control based on IP addresses in AWS WAF, you first need to prepare an IP set.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Set of allowed IP addresses&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_wafv2_ip_set"&lt;/span&gt; &lt;span class="s2"&gt;"allowed_ips"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;provider&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;virginia&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;"example-allowed-ips"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Authorized IP addresses"&lt;/span&gt;
  &lt;span class="nx"&gt;scope&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"CLOUDFRONT"&lt;/span&gt;
  &lt;span class="nx"&gt;ip_address_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"IPV4"&lt;/span&gt;
  &lt;span class="nx"&gt;addresses&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allowed_ip_list&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above, the list of IP addresses is stored in &lt;code&gt;var.allowed_ip_list&lt;/code&gt;, and it's assumed that the module user will pass it as a parameter in the following manner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"waf"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"../modules/waf"&lt;/span&gt;
  &lt;span class="nx"&gt;allowed_ip_list&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s2"&gt;"xxx.xxx.xxx.xxx/32"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"yyy.yyy.yyy.yyy/32"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"zzz.zzz.zzz.zzz/32"&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;h2&gt;
  
  
  BASIC Authentication Settings
&lt;/h2&gt;

&lt;p&gt;In the code below, we pre-calculate the string expected to be stored in the BASIC authentication header.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Pre-calculate the expected value for the Authorization header&lt;/span&gt;
  &lt;span class="nx"&gt;credential&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;base64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"${var.basic_auth.user}:${var.basic_auth.password}"&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;In the following block, we reference that value to check whether the corresponding string is stored in the request's Authorization header.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;              &lt;span class="nx"&gt;byte_match_statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;positional_constraint&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"EXACTLY"&lt;/span&gt;
                &lt;span class="nx"&gt;search_string&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Basic ${local.credential}"&lt;/span&gt;
                &lt;span class="nx"&gt;field_to_match&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                  &lt;span class="nx"&gt;single_header&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;"authorization"&lt;/span&gt;
                  &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="nx"&gt;text_transformation&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                  &lt;span class="nx"&gt;priority&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
                  &lt;span class="nx"&gt;type&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"NONE"&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 conditions do not match, the block below is set to return a custom response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;    &lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;block&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Set the action to block for the conditions mentioned later&lt;/span&gt;
        &lt;span class="c1"&gt;// Also, return a custom response requesting username and&lt;/span&gt;
        &lt;span class="c1"&gt;// password input for BASIC authentication&lt;/span&gt;
        &lt;span class="nx"&gt;custom_response&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;response_code&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;401&lt;/span&gt;
          &lt;span class="nx"&gt;response_header&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;"WWW-Authenticate"&lt;/span&gt;
            &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Basic realm=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Secure Area&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="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;In the custom response, the &lt;code&gt;WWW-Authenticate&lt;/code&gt; response header requests BASIC authentication from the source. Typically, when a browser receives this response, it displays a dialog prompting for BASIC authentication information (User/Password).&lt;/p&gt;

&lt;p&gt;Reference: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/WWW-Authenticate" rel="noopener noreferrer"&gt;WWW-Authenticate - HTTP | MDN&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Combining IP Address and BASIC Authentication
&lt;/h2&gt;

&lt;p&gt;Combining the conditions mentioned earlier results in the following rule. (Details are omitted for clarity in structure)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;rule&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// If the following statement matches, block the access and return a custom response&lt;/span&gt;
  &lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;block&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Request BASIC authentication in the custom response&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;and_statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// The source IP address is not in the allowed IP list&lt;/span&gt;
      &lt;span class="nx"&gt;statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Invert the condition here&lt;/span&gt;
        &lt;span class="nx"&gt;not_statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Does the IP address match the allowed IP list?&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;// BASIC authentication information is not included in the Authorization header&lt;/span&gt;
      &lt;span class="nx"&gt;statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Invert the condition here&lt;/span&gt;
        &lt;span class="nx"&gt;not_statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Is BASIC authentication information included in the Authorization header?&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that, as the negation (&lt;code&gt;not_statement&lt;/code&gt;) cannot be described directly under the &lt;code&gt;rule&lt;/code&gt; block or &lt;code&gt;and_statement&lt;/code&gt;, it needs to be re-wrapped with the &lt;code&gt;statement&lt;/code&gt; block, making the structure somewhat difficult to understand.&lt;/p&gt;

&lt;p&gt;In summary, this results in the code mentioned earlier.&lt;/p&gt;

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

&lt;p&gt;We introduced a method to allow access to specific users using AWS WAF by combining IP address-based strict access control and looser access control through BASIC authentication, which can flexibly accommodate cases where temporary access is needed.&lt;/p&gt;

&lt;p&gt;One point to note is that if you apply this to APIs that require authentication methods other than BASIC authentication (e.g., OAuth), some modification on the client-side using the API may be needed, bringing infrastructural concerns into the application layer. It's also viable to exclude such APIs from this rule if they already have authentication in place, depending on the risk assessment.&lt;/p&gt;

&lt;p&gt;I hope this article helps someone out there.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://zenn.dev/trkdkjm/articles/41bb1f82a6e7b4" rel="noopener noreferrer"&gt;terraformを使ってAWS WAFだけでBasic認証を設定する&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.hashicorp.com/terraform/language/functions/base64encode" rel="noopener noreferrer"&gt;Terraform base64encode function&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/WWW-Authenticate" rel="noopener noreferrer"&gt;WWW-Authenticate - HTTP | MDN&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>security</category>
      <category>terraform</category>
    </item>
    <item>
      <title>Degraded Performance Caused by Next.js Webfont Optimization: A Case Study</title>
      <dc:creator>Shinji NAKAMATSU</dc:creator>
      <pubDate>Thu, 05 Oct 2023 15:09:24 +0000</pubDate>
      <link>https://dev.to/snaka/degraded-performance-caused-by-nextjs-webfont-optimization-a-case-study-4h7g</link>
      <guid>https://dev.to/snaka/degraded-performance-caused-by-nextjs-webfont-optimization-a-case-study-4h7g</guid>
      <description>&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@chollz?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Charlie M&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/CHAFV-0U7b8?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;A website built with Next.js experienced performance issues under certain conditions.&lt;/li&gt;
&lt;li&gt;The cause of the problem was a significant expansion of HTML size due to the Automatic Webfont Optimization feature.&lt;/li&gt;
&lt;li&gt;As a solution, we utilized &lt;code&gt;next/font&lt;/code&gt; to optimize the method of loading Webfonts and reduced the HTML size.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Specific Circumstances of the Project
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Deploying and test-running the Next.js application on ECS&lt;/li&gt;
&lt;li&gt;Providing multilingual support (Japanese, Korean, Chinese (Traditional, Simplified))&lt;/li&gt;
&lt;li&gt;Originally started building with Next.js v12, and just last month, finally completed the update to v13&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Problem That Occurred
&lt;/h2&gt;

&lt;p&gt;Our team had an opportunity to measure the performance of a particular website built with Next.js. Surprisingly, even though the number of requests was not so high, we noticed IPC (Inter-Process Communication) related errors occurring on the server side.&lt;/p&gt;

&lt;p&gt;
  (Part of the occurring error)
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TypeError: fetch failed
    at Object.fetch (node:internal/deps/undici/undici:11457:11)
    at async invokeRequest (/home/snaka/xxxxx/node_modules/next/dist/server/lib/server-ipc/invoke-request.js:17:12)
    at async invokeRender (/home/snaka/xxxxx/node_modules/next/dist/server/lib/router-server.js:254:29)
    at async handleRequest (/home/snaka/xxxxx/node_modules/next/dist/server/lib/router-server.js:447:24)
    at async requestHandler (/home/snaka/xxxxx/node_modules/next/dist/server/lib/router-server.js:464:13)
    at async Server.&amp;lt;anonymous&amp;gt; (/home/snaka/xxxxx/node_modules/next/dist/server/lib/start-server.js:117:13) {
  cause: Error: read ECONNRESET
      at TCP.onStreamRead (node:internal/stream_base_commons:217:20) {
    errno: -104,
    code: 'ECONNRESET',
    syscall: 'read'
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Another error also occurred:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cause: SocketError: other side closed
at Socket.onSocketEnd (/app/node_modules/next/dist/compiled/undici/index.js:1:63301)
at Socket.emit (node:events:525:35)
at endReadableNT (node:internal/streams/readable:1359:12)
at process.processTicksAndRejections (node:internal/process/task_queues:82:21) {
  code: 'UND_ERR_SOCKET',
  socket: {
    localAddress: '127.0.0.1',
    localPort: 45850,
    remoteAddress: undefined,
    remotePort: undefined,
    remoteFamily: undefined,
    timeout: undefined,
    bytesWritten: 2937,
    bytesRead: 87753
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Upon further investigation, it became clear that the HTML built by Next.js was abnormally large. Specifically, its size was about 2MB, which was significantly larger compared to the size of a typical webpage's HTML.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Cause of the Problem
&lt;/h2&gt;

&lt;p&gt;To resolve the performance issue occurring on our website, we began a series of investigations to pinpoint the cause. During load testing, it was revealed that network bandwidth was a clear bottleneck, and we were particularly concerned about the significant bandwidth consumption when downloading a single HTML file.&lt;/p&gt;

&lt;p&gt;Further investigation into the HTML revealed that it contained a large number of font definitions (&lt;code&gt;@font-face&lt;/code&gt; rules). These definitions were not present in the original source code and were automatically inserted by some mechanism. Further research revealed that these font definitions originated from the Automatic Webfont Optimization feature introduced in Next.js v10.2.&lt;/p&gt;

&lt;p&gt;Reference: &lt;a href="https://nextjs.org/blog/next-10-2#automatic-webfont-optimization" rel="noopener noreferrer"&gt;Next.js 10.2 Release Notes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The problematic section was the following description in _document.tsx.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// _document.tsx&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Head&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;link&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@100;300;400;500;700;900&amp;amp;display=swap"&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;link&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@100;300;400;500;700;900&amp;amp;display=swap"&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;link&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"https://fonts.googleapis.com/css2?family=Noto+Sans+TC:wght@100;300;400;500;700;900&amp;amp;display=swap"&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;link&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@100;300;400;500;700;900&amp;amp;display=swap"&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="nc"&gt;Head&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;Sample code is available here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/snaka/code-pocket/tree/master/framework/nextjs/webfont/legacy-optimization" rel="noopener noreferrer"&gt;https://github.com/snaka/code-pocket/tree/master/framework/nextjs/webfont/legacy-optimization&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Size of the built HTML (about 2MB)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-rw-r--r--@ 1 snaka  staff  2359534 Sep 29 09:48 ja.html
-rw-r--r--@ 1 snaka  staff  2359533 Sep 29 09:48 ko.html
-rw-r--r--@ 1 snaka  staff  2359540 Sep 29 09:48 zh-CN.html
-rw-r--r--@ 1 snaka  staff  2359540 Sep 29 09:48 zh-TW.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;The solution was simple. We stopped loading Webfonts in &lt;code&gt;_document.tsx&lt;/code&gt; and switched to using &lt;code&gt;next/font&lt;/code&gt;, introduced in Next.js v13.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nextjs.org/docs/pages/building-your-application/optimizing/fonts" rel="noopener noreferrer"&gt;Optimizing: Fonts | Next.js&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This change significantly reduced the size of the HTML and resolved the IPC error when loading the server. Additionally, by using &lt;code&gt;next/font&lt;/code&gt;, it became possible to self-host Webfonts and control caching periods, etc., through the CDN.&lt;/p&gt;

&lt;p&gt;The code for using Webfonts became as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// index.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Noto_Sans_JP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Noto_Sans_KR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Noto_Sans_SC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Noto_Sans_TC&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/font/google&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;notoSansJP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Noto_Sans_JP&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;100&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;300&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;400&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;500&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;700&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;900&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;notoSansSC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Noto_Sans_SC&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;100&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;300&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;400&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;500&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;700&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;900&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;notoSansTC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Noto_Sans_TC&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;100&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;300&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;400&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;500&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;700&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;900&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;notoSansKR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Noto_Sans_KR&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;100&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;300&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;400&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;500&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;700&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;900&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="c1"&gt;// Use the loaded webfont as className={notoSans_JP.className}, etc.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample code is available here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/snaka/code-pocket/tree/master/framework/nextjs/webfont/next-font" rel="noopener noreferrer"&gt;https://github.com/snaka/code-pocket/tree/master/framework/nextjs/webfont/next-font&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Size of the built HTML (about 5KB)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-rw-r--r--@ 1 snaka  staff   5176 Sep 29 14:19 ja.html
-rw-r--r--@ 1 snaka  staff   5175 Sep 29 14:19 ko.html
-rw-r--r--@ 1 snaka  staff   5182 Sep 29 14:19 zh-CN.html
-rw-r--r--@ 1 snaka  staff   5182 Sep 29 14:19 zh-TW.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  In Choosing the Approach
&lt;/h2&gt;

&lt;p&gt;In this case, we adopted a method of changing the Webfont loading method and using &lt;code&gt;next/font&lt;/code&gt; without preload. This decision was made as a result of evaluating the following technical trade-offs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Problems Before Improvement&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Increased communication caused instability in inter-process communication within the container&lt;/li&gt;
&lt;li&gt;The allocated CPU and memory performance for the container could not be fully utilized, necessitating the parallel operation of numerous containers&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Negative Impact from Changing to &lt;code&gt;next/font&lt;/code&gt;&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Using &lt;code&gt;next/font&lt;/code&gt; without preload → A slight time lag occurred until the Webfont was applied&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Consideration of Alternatives&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Change the deployment destination to a FaaS platform? → Judged as high-risk&lt;/li&gt;
&lt;li&gt;Personally, it was very interesting but...&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Final Decision&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Utilizing &lt;code&gt;next/font&lt;/code&gt; provided benefits of resolving communication bottlenecks and effectively utilizing container resources&lt;/li&gt;
&lt;li&gt;Compared cost-performance and a slight UX degradation, and prioritized cost-performance&lt;/li&gt;
&lt;li&gt;By loading divided files, we anticipate achieving service stability efficiently by distributing server load using CDNs.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Through this choice, while the client-side overhead increased due to the additional files to be loaded, the final performance evaluation (Lighthouse) showed a slight improvement. (Approximately 13% enhancement)&lt;/p&gt;

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

&lt;p&gt;In this article, we presented an example of a performance issue and its solution that occurred on a website built with Next.js. We confirmed that the Automatic Webfont Optimization feature could cause a significant expansion in HTML size in certain cases, which, in turn, could potentially trigger IPC errors on the server side.&lt;/p&gt;

&lt;p&gt;As a solution, by utilizing &lt;code&gt;next/font&lt;/code&gt;, we optimized the method of loading Webfonts, reduced the HTML size, and were able to alleviate the bottleneck on the server. This also enabled us to self-host Webfonts and control caching.&lt;/p&gt;

&lt;p&gt;The speed of evolution in front-end technology is staggering, with new technologies and optimization methods being provided daily. While they can enhance the performance of a website when used appropriately, they are not omnipotent and can cause unexpected problems in some cases. Therefore, thorough testing is indispensable when introducing new features.&lt;/p&gt;

&lt;p&gt;We hope this article proves helpful to someone.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article was proofread by ChatGPT.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Pages Referred to
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nextjs.org/docs/pages/building-your-application/optimizing/fonts" rel="noopener noreferrer"&gt;Optimizing: Fonts | Next.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nextjs.org/blog/next-10-2#automatic-webfont-optimization" rel="noopener noreferrer"&gt;Next.js 10.2 | Next.js&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>nextjs</category>
      <category>performance</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Avoiding SSR Pitfalls: Using `next/dynamic` in Your Next.js App</title>
      <dc:creator>Shinji NAKAMATSU</dc:creator>
      <pubDate>Wed, 13 Sep 2023 13:46:26 +0000</pubDate>
      <link>https://dev.to/snaka/avoiding-ssr-pitfalls-using-nextdynamic-in-your-nextjs-app-a4o</link>
      <guid>https://dev.to/snaka/avoiding-ssr-pitfalls-using-nextdynamic-in-your-nextjs-app-a4o</guid>
      <description>&lt;h2&gt;
  
  
  tl;dr
&lt;/h2&gt;

&lt;p&gt;Components wrapped in &lt;code&gt;dynamic()&lt;/code&gt; as shown below are &lt;strong&gt;not rendered on the server-side&lt;/strong&gt; but are exclusively rendered on the client-side.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PurchaseButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Component implementation&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ssr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;When components intended for server-side rendering (SSR) are implemented as demonstrated below, discrepancies can arise between Server-side and Client-side rendering, potentially leading to errors during React's hydration phase:&lt;/p&gt;

&lt;p&gt;(error message)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Unhandled Runtime Error
Error: Text content does not match server-rendered HTML.

See more info here: https://nextjs.org/docs/messages/react-hydration-error
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Followings are possible causes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adapting rendering based on the presence or absence of the &lt;code&gt;window&lt;/code&gt; object.&lt;/li&gt;
&lt;li&gt;Content changes based on rendering timing, such as the current date and time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more in-depth information, please refer to the following link:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nextjs.org/docs/messages/react-hydration-error" rel="noopener noreferrer"&gt;Text content does not match server-rendered HTML | Next.js&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to Address this from the Component Consumer Side
&lt;/h2&gt;

&lt;p&gt;To resolve such issues, one approach is to prevent the targeted component from being rendered on the Server-side.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Solution 2: Disabling SSR on specific components&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To achieve this, you can leverage &lt;code&gt;next/dynamic&lt;/code&gt;. However, in the official documentation, it is often introduced in the context of dynamic imports, possibly with file splitting in mind.&lt;/p&gt;

&lt;p&gt;Reference: &lt;a href="https://nextjs.org/docs/pages/building-your-application/optimizing/lazy-loading" rel="noopener noreferrer"&gt;Optimizing: Lazy Loading | Next.js&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DynamicComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../components/hello&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Hello&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;By following this pattern, the side that utilizes the component (component consumer side) needs to be aware of &lt;code&gt;next/dynamic&lt;/code&gt;.&lt;br&gt;
If you need the flexibility to toggle between enabling and disabling SSR on a case-by-case basis, that's perfectly fine. However, if the specific component is intended to be non-SSR compliant regardless of its usage context, it's best to handle this within the component itself.&lt;/p&gt;
&lt;h2&gt;
  
  
  Addressing the Issue Within the Targeted Component
&lt;/h2&gt;

&lt;p&gt;By wrapping the targeted component entirely with &lt;code&gt;dynamic(...)&lt;/code&gt;, there's no need for the component consumer to be mindful of SSR considerations. It's worth noting that the first argument of &lt;code&gt;dynamic()&lt;/code&gt; should be a &lt;code&gt;Promise&amp;lt;...&amp;gt;&lt;/code&gt;, which typically involves passing the component wrapped in &lt;code&gt;Promise.resolve()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PurchaseButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Component implementation&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ssr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Server-side rendering (SSR) offers advantages like faster page loads and SEO benefits. Nevertheless, certain components may introduce issues due to disparities between server-side and client-side behaviors.&lt;/p&gt;

&lt;p&gt;Through the use of Next.js's &lt;code&gt;next/dynamic&lt;/code&gt; feature, developers can selectively disable SSR for specific components, ensuring smoother hydration processes and overall enhanced application resilience.&lt;/p&gt;

&lt;p&gt;This approach not only provides flexibility in managing components with client-specific dependencies but also streamlines the development process by reducing SSR-related bugs.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>ssr</category>
      <category>tip</category>
      <category>react</category>
    </item>
    <item>
      <title>How to Reference Terraform Cloud State from ecspresso and ecschedule</title>
      <dc:creator>Shinji NAKAMATSU</dc:creator>
      <pubDate>Tue, 11 Jul 2023 23:50:38 +0000</pubDate>
      <link>https://dev.to/snaka/how-to-reference-terraform-cloud-state-from-ecspresso-and-ecschedule-3jk6</link>
      <guid>https://dev.to/snaka/how-to-reference-terraform-cloud-state-from-ecspresso-and-ecschedule-3jk6</guid>
      <description>&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@carrier_lost?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Ian Taylor&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/jOqJbvo1P9g?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Introduction: ecspresso, ecschedule, and Terraform
&lt;/h2&gt;

&lt;p&gt;ecspresso, ecschedule, and Terraform are powerful tools for achieving Infrastructure as Code (IaC) and managing resources in AWS. Let's take a closer look at each tool.&lt;/p&gt;

&lt;h3&gt;
  
  
  ecspresso
&lt;/h3&gt;

&lt;p&gt;ecspresso is a tool designed for managing the definition of ECS services and tasks as code. It enables you to define and manage your ECS infrastructure using familiar coding practices.&lt;/p&gt;

&lt;p&gt;You can find more information about ecspresso on the official GitHub repository: &lt;a href="https://github.com/kayac/ecspresso" rel="noopener noreferrer"&gt;ecspresso GitHub&lt;/a&gt;.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/kayac" rel="noopener noreferrer"&gt;
        kayac
      &lt;/a&gt; / &lt;a href="https://github.com/kayac/ecspresso" rel="noopener noreferrer"&gt;
        ecspresso
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      ecspresso is a deployment tool for Amazon ECS
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;ecspresso&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;ecspresso is a deployment tool for Amazon ECS.&lt;/p&gt;
&lt;p&gt;(pronounced the same as "espresso" ☕)&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Documents&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/kayac/ecspressodocs/v1-v2.md" rel="noopener noreferrer"&gt;Differences between v1 and v2&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://adventar.org/calendars/5916" rel="nofollow noopener noreferrer"&gt;ecspresso Advent Calendar 2020&lt;/a&gt; (Japanese)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://zenn.dev/fujiwara/books/ecspresso-handbook-v2" rel="nofollow noopener noreferrer"&gt;ecspresso handbook&lt;/a&gt; (Japanese)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://zenn.dev/fujiwara/books/ecspresso-handbook-v2/viewer/reference" rel="nofollow noopener noreferrer"&gt;Command Reference&lt;/a&gt; (Japanese)&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Install&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Homebrew (macOS and Linux)&lt;/h3&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-text-shell-session notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;$ &lt;span class="pl-s1"&gt;brew install kayac/tap/ecspresso&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;asdf (macOS and Linux)&lt;/h3&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-text-shell-session notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;$ &lt;span class="pl-s1"&gt;asdf plugin add ecspresso&lt;/span&gt;
# &lt;span class="pl-s1"&gt;or&lt;/span&gt;
$ &lt;span class="pl-s1"&gt;asdf plugin add ecspresso https://github.com/kayac/asdf-ecspresso.git&lt;/span&gt;

$ &lt;span class="pl-s1"&gt;asdf install ecspresso 2.3.0&lt;/span&gt;
$ &lt;span class="pl-s1"&gt;asdf global ecspresso 2.3.0&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;aqua (macOS and Linux)&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a href="https://aquaproj.github.io/" rel="nofollow noopener noreferrer"&gt;aqua&lt;/a&gt; is a CLI version manager.&lt;/p&gt;
&lt;div class="highlight highlight-text-shell-session notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;$ &lt;span class="pl-s1"&gt;aqua g -i kayac/ecspresso&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Binary packages&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a href="https://github.com/kayac/ecspresso/releases" rel="noopener noreferrer"&gt;Releases&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;CircleCI Orbs&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a href="https://circleci.com/orbs/registry/orb/fujiwara/ecspresso" rel="nofollow noopener noreferrer"&gt;https://circleci.com/orbs/registry/orb/fujiwara/ecspresso&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight highlight-source-yaml notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-ent"&gt;version&lt;/span&gt;: &lt;span class="pl-c1"&gt;2.1&lt;/span&gt;
&lt;span class="pl-ent"&gt;orbs&lt;/span&gt;:
  &lt;span class="pl-ent"&gt;ecspresso&lt;/span&gt;: &lt;span class="pl-s"&gt;fujiwara/ecspresso@2.0.4&lt;/span&gt;
&lt;span class="pl-ent"&gt;jobs&lt;/span&gt;:
  &lt;span class="pl-ent"&gt;install&lt;/span&gt;:
    &lt;span class="pl-ent"&gt;steps&lt;/span&gt;:
      - &lt;span class="pl-s"&gt;checkout&lt;/span&gt;
      - &lt;span class="pl-ent"&gt;ecspresso/install&lt;/span&gt;:
          &lt;span class="pl-ent"&gt;version&lt;/span&gt;: &lt;span class="pl-s"&gt;v2.3.0 &lt;/span&gt;&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; or latest&lt;/span&gt;
          &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; version-file: .ecspresso-version&lt;/span&gt;
          &lt;span class="pl-ent"&gt;os&lt;/span&gt;: &lt;span class="pl-s"&gt;linux &lt;/span&gt;&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; or windows or darwin&lt;/span&gt;
          &lt;span class="pl-ent"&gt;arch&lt;/span&gt;: &lt;span class="pl-s"&gt;amd64 &lt;/span&gt;&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; or arm64&lt;/span&gt;
      - &lt;span class="pl-ent"&gt;run&lt;/span&gt;:
          &lt;span class="pl-ent"&gt;command&lt;/span&gt;: &lt;span class="pl-s"&gt;|&lt;/span&gt;
&lt;span class="pl-s"&gt;            ecspresso version&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;code&gt;version: latest&lt;/code&gt; installs different versions of ecspresso for each Orb…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/kayac/ecspresso" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  ecschedule
&lt;/h3&gt;

&lt;p&gt;ecschedule is another valuable tool in the IaC ecosystem, specifically built for managing scheduled tasks on ECS. With ecschedule, you can define and manage tasks that need to be executed on a schedule, similar to cron jobs.&lt;/p&gt;

&lt;p&gt;To learn more about ecschedule, visit the GitHub repository: &lt;a href="https://github.com/Songmu/ecschedule" rel="noopener noreferrer"&gt;ecschedule GitHub&lt;/a&gt;.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Songmu" rel="noopener noreferrer"&gt;
        Songmu
      &lt;/a&gt; / &lt;a href="https://github.com/Songmu/ecschedule" rel="noopener noreferrer"&gt;
        ecschedule
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      ecschedule is a tool to manage ECS Scheduled Tasks.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;ecschedule&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://github.com/Songmu/ecschedule/actions?workflow=test" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/Songmu/ecschedule/workflows/test/badge.svg?branch=main" alt="Test Status"&gt;&lt;/a&gt;
&lt;a href="https://github.com/Songmu/ecschedule/blob/main/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/33c44e11f2026269872ef17e691c7d0712059a191121e709451737950255bbe5/687474703a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75652e7376673f7374796c653d666c61742d737175617265" alt="MIT License"&gt;&lt;/a&gt;
&lt;a href="https://pkg.go.dev/github.com/Songmu/ecschedule" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/e5e590656465465d2cf75010b286341a9ded721e5d3eda192f212ab97bee9595/68747470733a2f2f706b672e676f2e6465762f62616467652f6769746875622e636f6d2f536f6e676d752f65637363686564756c65" alt="PkgGoDev"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;ecschedule is a tool to manage ECS Scheduled Tasks.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Synopsis&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;% ecschedule [dump&lt;span class="pl-k"&gt;|&lt;/span&gt;apply&lt;span class="pl-k"&gt;|&lt;/span&gt;run&lt;span class="pl-k"&gt;|&lt;/span&gt;diff] -conf ecschedule.yaml -rule &lt;span class="pl-smi"&gt;$ruleName&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Description&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;The ecschedule manages ECS Schedule tasks using a configuration file (YAML, JSON or Jsonnet format) like following.&lt;/p&gt;
&lt;div class="highlight highlight-source-yaml notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-ent"&gt;region&lt;/span&gt;: &lt;span class="pl-s"&gt;us-east-1&lt;/span&gt;
&lt;span class="pl-ent"&gt;cluster&lt;/span&gt;: &lt;span class="pl-s"&gt;clusterName&lt;/span&gt;
&lt;span class="pl-ent"&gt;rules&lt;/span&gt;:
&lt;span class="pl-ent"&gt;trackingId&lt;/span&gt;: &lt;span class="pl-s"&gt;trackingId1&lt;/span&gt;
- &lt;span class="pl-ent"&gt;name&lt;/span&gt;: &lt;span class="pl-s"&gt;taskName1&lt;/span&gt;
  &lt;span class="pl-ent"&gt;description&lt;/span&gt;: &lt;span class="pl-s"&gt;task 1&lt;/span&gt;
  &lt;span class="pl-ent"&gt;scheduleExpression&lt;/span&gt;: &lt;span class="pl-s"&gt;cron(30 15 ? * * *)&lt;/span&gt;
  &lt;span class="pl-ent"&gt;taskDefinition&lt;/span&gt;: &lt;span class="pl-s"&gt;taskDefName&lt;/span&gt;
  &lt;span class="pl-ent"&gt;containerOverrides&lt;/span&gt;:
  - &lt;span class="pl-ent"&gt;name&lt;/span&gt;: &lt;span class="pl-s"&gt;containerName&lt;/span&gt;
    &lt;span class="pl-ent"&gt;command&lt;/span&gt;: &lt;span class="pl-s"&gt;[subcommand1, arg]&lt;/span&gt;
    &lt;span class="pl-ent"&gt;environment&lt;/span&gt;:
      &lt;span class="pl-ent"&gt;HOGE&lt;/span&gt;: &lt;span class="pl-s"&gt;foo&lt;/span&gt;
      &lt;span class="pl-ent"&gt;FUGA&lt;/span&gt;: &lt;span class="pl-s"&gt;{{ must_env `APP_FUGA` }}&lt;/span&gt;
- &lt;span class="pl-ent"&gt;name&lt;/span&gt;: &lt;span class="pl-s"&gt;taskName2&lt;/span&gt;
  &lt;span class="pl-ent"&gt;description&lt;/span&gt;: &lt;span class="pl-s"&gt;task2&lt;/span&gt;
  &lt;span class="pl-ent"&gt;scheduleExpression&lt;/span&gt;: &lt;span class="pl-s"&gt;cron(30 16 ? * * *)&lt;/span&gt;
  &lt;span class="pl-ent"&gt;taskDefinition&lt;/span&gt;: &lt;span class="pl-s"&gt;taskDefName2&lt;/span&gt;
  &lt;span class="pl-ent"&gt;containerOverrides&lt;/span&gt;:
  - &lt;span class="pl-ent"&gt;name&lt;/span&gt;: &lt;span class="pl-s"&gt;containerName2&lt;/span&gt;
    &lt;span class="pl-ent"&gt;command&lt;/span&gt;: &lt;span class="pl-s"&gt;[subcommand2, arg]&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Installation&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-text-shell-session notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;% &lt;span class="pl-s1"&gt;brew install Songmu/tap/ecschedule&lt;/span&gt;
# &lt;span class="pl-s1"&gt;or&lt;/span&gt;
% &lt;span class="pl-s1"&gt;go install github.com/Songmu/ecschedule/cmd/ecschedule@latest&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;GitHub Actions&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;Action Songmu/ecschedule@main installs ecschedule binary for Linux into /usr/local/bin. This action runs install only.&lt;/p&gt;
&lt;div class="highlight highlight-source-yaml notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-ent"&gt;jobs&lt;/span&gt;
  &lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Songmu/ecschedule" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  Terraform
&lt;/h3&gt;

&lt;p&gt;Terraform is a widely adopted IaC tool that allows you to define and manage infrastructure resources in AWS, such as VPCs, RDS instances, and ElastiCache clusters. It provides a declarative approach to infrastructure management, enabling you to define your desired state and automatically provision and manage resources accordingly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Issue of Managing ECS Task Definitions with Terraform
&lt;/h2&gt;

&lt;p&gt;ECS task definitions often include elements that are tightly coupled with the application's implementation, such as environment variable settings and command parameter configurations. When managing task definitions within Terraform, applying changes in Terraform and then reflecting them on the application side becomes a necessary step.&lt;/p&gt;

&lt;p&gt;While this approach works well for occasional changes, frequent updates can introduce the potential for mistakes and inefficiencies.&lt;/p&gt;

&lt;p&gt;To address this issue, it is recommended to decouple ECS services and task definitions from Terraform. By leveraging ecspresso and ecschedule, I can manage the application code and related ECS components together. This approach simplifies the management process and streamlines development workflows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Leveraging Terraform Cloud State with ecspresso and ecschedule
&lt;/h2&gt;

&lt;p&gt;One of the powerful features offered by ecspresso and ecschedule is the tfstate plugin. This plugin allows you to reference the configuration values of infrastructure resources managed by Terraform using the Mustache syntax.&lt;/p&gt;

&lt;p&gt;Using the tfstate plugin, you can incorporate the following example of referencing values using the Mustache syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{{ tfstate `path.to.resource` }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This syntax enables you to access specific resource configurations managed by Terraform and utilize them within your ecspresso and ecschedule configurations seamlessly.&lt;/p&gt;

&lt;p&gt;While the README documentation provides examples of referencing local paths and S3 bucket URLs, it does not explicitly cover referencing state managed by Terraform Cloud. However, by exploring the ecspresso source code, I discovered the usage of tfstate-lookup internally.&lt;/p&gt;

&lt;p&gt;To reference Terraform Cloud state, I can utilize the following URL format: &lt;code&gt;remote://app.terraform.io/{ORGANIZATION}/{WORKSPACE}&lt;/code&gt;. This format allows me to access the state of a specific workspace within my Terraform Cloud organization.&lt;/p&gt;

&lt;p&gt;To make this work, I need to generate an API token for Terraform Cloud and set it in the &lt;code&gt;TFE_TOKEN&lt;/code&gt; environment variable. The API token should have appropriate permissions based on my operational requirements.&lt;/p&gt;

&lt;p&gt;Once the API token is set, I can configure ecspresso and ecschedule to reference the Terraform Cloud state. For example, in the &lt;code&gt;config.yaml&lt;/code&gt; file, I can add the following configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;plugins:
  - name: tfstate
    config:
      url: remote://app.terraform.io/{ORGANIZATION}/{WORKSPACE}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this configuration in place, I can now read and reference the configuration values of my Terraform-managed resources within ecspresso and ecschedule.&lt;/p&gt;

&lt;p&gt;Before deploying changes, it is recommended to run commands such as &lt;code&gt;ecspresso render&lt;/code&gt; or &lt;code&gt;ecschedule diff&lt;/code&gt; to confirm that the configuration reflects the values from the Terraform Cloud state.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;It is easier to manage ECS services, task definitions, and scheduled tasks as Infrastructure as Code (IaC) using ecspresso and ecschedule, rather than Terraform.&lt;/li&gt;
&lt;li&gt;Set the Terraform Cloud API Token in the &lt;code&gt;TFE_TOKEN&lt;/code&gt; environment variable.&lt;/li&gt;
&lt;li&gt;The URL format to reference Terraform Cloud state is &lt;code&gt;remote://app.terraform.io/{ORGANIZATION}/{WORKSPACE}&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tfstate plugin provided by ecspresso and ecschedule allows me to reference and leverage the configuration values of my Terraform-managed resources. By utilizing this feature, along with proper configuration and the API token for Terraform Cloud, I can seamlessly integrate Terraform Cloud into my ecspresso and ecschedule workflows.&lt;/p&gt;

&lt;p&gt;I hope this article provides valuable insights and guides you towards optimizing your infrastructure management practices.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>terraform</category>
      <category>infrastructureascode</category>
      <category>aws</category>
    </item>
    <item>
      <title>Enhancing MUI Autocomplete and Formik Integration: Insights and Practical Tips</title>
      <dc:creator>Shinji NAKAMATSU</dc:creator>
      <pubDate>Mon, 26 Jun 2023 03:58:22 +0000</pubDate>
      <link>https://dev.to/snaka/enhancing-mui-autocomplete-and-formik-integration-insights-and-practical-tips-58hn</link>
      <guid>https://dev.to/snaka/enhancing-mui-autocomplete-and-formik-integration-insights-and-practical-tips-58hn</guid>
      <description>&lt;p&gt;Photo by &lt;a href="https://unsplash.com/fr/@ilyapavlov?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Ilya Pavlov&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/OqtafYT5kTw?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;I wanted to do something like this for work, but I couldn't find a good sample on the internet, so I had to experiment and figure it out. So, I'm publishing it as a blog post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;First, let me show you the demo.&lt;/p&gt;

&lt;p&gt;Assuming a form where users enter a postal code in Tokyo, I have implemented an Autocomplete component that displays and allows the selection of address candidates based on keywords. (I have extracted only a few postal codes as including all of them would result in too many options)&lt;/p&gt;

&lt;p&gt;When you press the Submit button, the contents of the form are outputted in JSON format.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/formik-mui-autocomplete-demo-th6f5g"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;Here is the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SyntheticEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useMemo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./styles.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useFormik&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;formik&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Autocomplete&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TextField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;debounce&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@mui/material&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;postalCodes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;usePostalCode&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;FormValues&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;postalCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;filterKeyword&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setFilterKeyword&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;submitCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setSubmitCode&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Custom Hook to extract the list of candidates&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;filteredPostalCodes&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;usePostalCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filterKeyword&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;formik&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useFormik&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;FormValues&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;initialValues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;postalCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;values&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="nf"&gt;setSubmitCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;values&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;debouncedSetter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMemo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;debounce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setFilterKeyword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return &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;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;App&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Formik&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;MUI&lt;/span&gt; &lt;span class="nx"&gt;Autocomplete&lt;/span&gt; &lt;span class="nx"&gt;Demo&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="nx"&gt;onSubmit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;formik&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="nx"&gt;htmlFor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postalCode&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Postal&lt;/span&gt; &lt;span class="nx"&gt;Code&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;/label&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Autocomplete&lt;/span&gt;
          &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;formik&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postalCode&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="na"&gt;_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SyntheticEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="nx"&gt;formik&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setFieldValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postalCode&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;filteredPostalCodes&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postalCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[]}&lt;/span&gt;
          &lt;span class="nx"&gt;onInputChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newInputValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;debouncedSetter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newInputValue&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
          &lt;span class="c1"&gt;// filterOptions={(x) =&amp;gt; x} // Disable the default filtering as we handle it ourselves&lt;/span&gt;
          &lt;span class="nx"&gt;getOptionLabel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="na"&gt;option&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="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="nx"&gt;postalCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postalCode&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;option&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;}}&lt;/span&gt;
          &lt;span class="nx"&gt;renderInput&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="nx"&gt;params&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TextField&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;placeholder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Postal Code&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&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="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Submit&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/form&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;submitCode&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Submit&lt;/span&gt; &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;submitCode&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;/&amp;gt;}&amp;lt;/&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&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;h2&gt;
  
  
  Implementation Points
&lt;/h2&gt;

&lt;p&gt;There are several implementation points that I want to explain briefly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Match the type of Autocomplete's &lt;code&gt;value&lt;/code&gt; with the field type in Formik
&lt;/h3&gt;

&lt;p&gt;When using Autocomplete to input values into a Formik form, it is easier to handle if the types of each value match.&lt;/p&gt;

&lt;p&gt;In the sample code, I created a form that allows users to input values into the &lt;code&gt;postalCode&lt;/code&gt; field in Formik using Autocomplete. In this case, both the type of &lt;code&gt;values.postalCode&lt;/code&gt; in Formik and the type of &lt;code&gt;value&lt;/code&gt; in Autocomplete are implemented as &lt;code&gt;string&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;FormValues&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;postalCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// ... (snip) ...&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Autocomplete&lt;/span&gt;
  &lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isPostalCodeLoading&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;formik&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postalCode&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; 
  &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="na"&gt;_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SyntheticEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;formik&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setFieldValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postalCode&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pass an array of values to Autocomplete's &lt;code&gt;options&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Intuitively, we might assume that &lt;code&gt;options&lt;/code&gt; should contain the strings displayed in the Autocomplete's dropdown list. However, it's a bit different.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;options&lt;/code&gt;, you should pass an array of values that will be used as the selected value assigned to &lt;code&gt;value&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the sample code, we assume that the &lt;code&gt;postalCode&lt;/code&gt; value will be assigned to &lt;code&gt;value&lt;/code&gt;, so we set an array consisting of &lt;code&gt;postalCode&lt;/code&gt; as the elements.&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="nx"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;filteredPostalCodes&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postalCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[]}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Use &lt;code&gt;getOptionLabel&lt;/code&gt; to display values in a different format than what's in &lt;code&gt;options&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;As mentioned earlier, the values set in &lt;code&gt;options&lt;/code&gt; may not be user-friendly or easily understandable, so it's necessary to convert them into a more readable (and selectable) format.&lt;/p&gt;

&lt;p&gt;In the sample code, we pass an array of &lt;code&gt;postalCode&lt;/code&gt; to &lt;code&gt;options&lt;/code&gt;, but we decided to display the corresponding address in the UI.&lt;/p&gt;

&lt;p&gt;The implementation of the function takes an argument &lt;code&gt;option&lt;/code&gt;, which is an element (i.e., &lt;code&gt;postalCode&lt;/code&gt;) of &lt;code&gt;options&lt;/code&gt;. We then find the corresponding element in the &lt;code&gt;postalCodes&lt;/code&gt; array and return its &lt;code&gt;address&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;getOptionLabel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="na"&gt;option&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="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="nx"&gt;postalCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postalCode&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;option&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Use &lt;code&gt;null&lt;/code&gt; to represent the "unselected" state in Autocomplete
&lt;/h3&gt;

&lt;p&gt;The value that represents the "unselected" state in Autocomplete (i.e., &lt;code&gt;value&lt;/code&gt;) is &lt;code&gt;null&lt;/code&gt;. Therefore, in the &lt;code&gt;initialValues&lt;/code&gt; of Formik, we set &lt;code&gt;null&lt;/code&gt; as the initial value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;initialValues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;postalCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you mistakenly set &lt;code&gt;""&lt;/code&gt; (an empty string) instead, you will see a warning like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MUI: The value provided to Autocomplete is invalid.
None of the options match with `""`.
You can use the `isOptionEqualToValue` prop to customize the equality test. 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  MUI provides debounce functionality
&lt;/h3&gt;

&lt;p&gt;While many articles suggest using &lt;code&gt;lodash&lt;/code&gt; or implementing debounce manually, MUI actually provides debounce functionality, so you don't need to add a dependency on &lt;code&gt;lodash&lt;/code&gt; or implement it yourself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Autocomplete&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TextField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;debounce&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@mui/material&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Memoize functions with debounce using &lt;code&gt;useMemo&lt;/code&gt; or &lt;code&gt;useCallback&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;It's easy to overlook, but functions wrapped with &lt;code&gt;debounce&lt;/code&gt; need to be memoized to work correctly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;debouncedSetter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMemo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;debounce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setFilterKeyword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;500&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;Typically, you would use the &lt;code&gt;useCallback&lt;/code&gt; hook to memoize a function. However, ESLint raises a warning saying "React Hook useCallback received a function whose dependencies are unknown. Pass an inline function instead. (react-hooks/exhaustive-deps)." Therefore, we use &lt;code&gt;useMemo&lt;/code&gt; instead.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use &lt;code&gt;inputValue&lt;/code&gt; as the keyword for API search
&lt;/h3&gt;

&lt;p&gt;In the sample code, the custom hook &lt;code&gt;usePostalCode&lt;/code&gt; is designed to make an API call and retrieve a list of postal codes and their corresponding addresses based on the keyword provided.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;filterKeyword&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setFilterKeyword&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Custom Hook to extract the list of candidates&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;filteredPostalCodes&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;usePostalCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filterKeyword&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// ... (snip) ...&lt;/span&gt;

&lt;span class="nx"&gt;onInputChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newInputValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;debouncedSetter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newInputValue&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;With the combination of Formik and Autocomplete, we were able to create a form with autocomplete functionality.&lt;/p&gt;

&lt;p&gt;To summarize the key points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Match the type of Autocomplete's &lt;code&gt;value&lt;/code&gt; with the field type in Formik.&lt;/li&gt;
&lt;li&gt;Pass an array of values to Autocomplete's &lt;code&gt;options&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;getOptionLabel&lt;/code&gt; to display values in a different format than what's in &lt;code&gt;options&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;null&lt;/code&gt; to represent the "unselected" state in Autocomplete.&lt;/li&gt;
&lt;li&gt;MUI provides debounce functionality, eliminating the need for lodash dependency.&lt;/li&gt;
&lt;li&gt;Memoize functions with debounce using &lt;code&gt;useMemo&lt;/code&gt; or &lt;code&gt;useCallback&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use the &lt;code&gt;inputValue&lt;/code&gt; as the keyword for API search.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope this article will be helpful to someone.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://formik.org/docs/api/formik" rel="noopener noreferrer"&gt;https://formik.org/docs/api/formik&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mui.com/material-ui/api/autocomplete/" rel="noopener noreferrer"&gt;https://mui.com/material-ui/api/autocomplete/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>firmik</category>
      <category>mui</category>
      <category>tips</category>
    </item>
    <item>
      <title>Securing Your Next.js Application with Strict CSP</title>
      <dc:creator>Shinji NAKAMATSU</dc:creator>
      <pubDate>Sun, 04 Jun 2023 07:43:49 +0000</pubDate>
      <link>https://dev.to/snaka/securing-your-nextjs-application-with-strict-csp-4lie</link>
      <guid>https://dev.to/snaka/securing-your-nextjs-application-with-strict-csp-4lie</guid>
      <description>&lt;p&gt;( Photo by &lt;a href="https://unsplash.com/ko/@flyd2069?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;FLY:D&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/ZNOxwCEj5mw?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt; )&lt;/p&gt;

&lt;p&gt;Utilizing CSP (Content Security Policy) allows us to limit the execution of code on our website, thereby mitigating the scope of any potential damage caused by XSS (Cross-Site Scripting) attacks.&lt;/p&gt;

&lt;p&gt;In this article, I'll share an example of how to handle CSP headers in Next.js.&lt;/p&gt;

&lt;h2&gt;
  
  
  tl;dr;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Set the policy according to Google's advocated Strict CSP&lt;/li&gt;
&lt;li&gt;Use _document.tsx to embed the CSP header into the &lt;code&gt;head&lt;/code&gt; as &lt;code&gt;&amp;lt;meta httpEquiv="Content-Security-Policy" content={ ... Policy here ... } /&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Next.js 12.x &lt;/li&gt;
&lt;li&gt;Both SSR (Server Side Rendering) and CSR (Client Side Rendering) are used as rendering methods&lt;/li&gt;
&lt;li&gt;Loading external scripts using the &lt;a href="https://nextjs.org/docs/pages/building-your-application/optimizing/scripts" rel="noopener noreferrer"&gt;Script&lt;/a&gt; component&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is Strict CSP?
&lt;/h2&gt;

&lt;p&gt;Google's proposed Strict CSP is a setting for CSP aimed at realizing higher security.&lt;/p&gt;

&lt;p&gt;Strict CSP depends on the application use-case, hence, careful consideration is required to decide how to use it depending on your application's specific use-cases.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://csp.withgoogle.com/docs/index.html" rel="noopener noreferrer"&gt;Introduction - Content Security Policy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What is the strict-dynamic directive?
&lt;/h3&gt;

&lt;p&gt;Strict CSP uses the &lt;code&gt;strict-dynamic&lt;/code&gt; directive to give trust to scripts. &lt;code&gt;strict-dynamic&lt;/code&gt; decides whether to trust a script based on a nonce value or hash value generated from the script. Then, trust is automatically propagated to scripts that are dynamically loaded from that trusted script.&lt;/p&gt;

&lt;p&gt;By using the &lt;code&gt;strict-dynamic&lt;/code&gt; directive, the tedious task of managing whitelist becomes unnecessary, as you only need to grant trust to the top-level script.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Implementing Strict CSP in Next.js
&lt;/h2&gt;

&lt;p&gt;The implementation strategy was decided as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Primarily adhere to Strict CSP&lt;/li&gt;
&lt;li&gt;Trust scripts via nonce

&lt;ul&gt;
&lt;li&gt;This is due to the complexity of calculating the hash values of multiple external scripts&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Set the CSP header in the form of a meta tag

&lt;ul&gt;
&lt;li&gt;Although it is possible to return response headers via next.config.js, the values cannot be dynamically changed&lt;/li&gt;
&lt;li&gt;I adopted the method of generating a meta tag in _document.tsx to dynamically generate nonce values&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

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

&lt;p&gt;Below is an example of the implementation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;randomBytes&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;crypto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Head&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NextScript&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/document&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Document&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nonce&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;randomBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&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;csp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`object-src 'none'; base-uri 'none'; script-src 'self' 'unsafe-eval' 'unsafe-inline' https: http: 'nonce-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;' 'strict-dynamic'`&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="nc"&gt;Html&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"ja"&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="nc"&gt;Head&lt;/span&gt; &lt;span class="na"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;nonce&lt;/span&gt;&lt;span class="si"&gt;}&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;meta&lt;/span&gt; &lt;span class="na"&gt;httpEquiv&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Content-Security-Policy"&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;csp&lt;/span&gt;&lt;span class="si"&gt;}&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="nc"&gt;Head&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;body&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="nc"&gt;Main&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="nc"&gt;NextScript&lt;/span&gt; &lt;span class="na"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;nonce&lt;/span&gt;&lt;span class="si"&gt;}&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;body&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="nc"&gt;Html&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;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Document&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;By using _document.tsx, you can embed the CSP header in the form of &lt;code&gt;&amp;lt;meta httpEquiv="Content-Security-Policy" content={ ... } /&amp;gt;&lt;/code&gt;.

&lt;ul&gt;
&lt;li&gt;Although you can set the response header in next.config.js, since the nonce needs to be generated for each page request, the CSP header is returned as a meta tag.&lt;/li&gt;
&lt;li&gt;Specify the following directives in accordance with Strict CSP

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;object-src 'none'&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;base-uri 'none'&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;script-src 'self' 'unsafe-eval' 'unsafe-inline' https: http: 'nonce-${nonce}' 'strict-dynamic'&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;The value of &lt;code&gt;${nonce}&lt;/code&gt; is a randomly generated dynamic value&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;Pass the generated nonce value to the &lt;code&gt;nonce&lt;/code&gt; property of the &lt;code&gt;Head&lt;/code&gt;, &lt;code&gt;NextScript&lt;/code&gt; components imported from &lt;code&gt;next/document&lt;/code&gt;.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This is a brief introduction to handling CSP headers in Next.js.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's check it out
&lt;/h2&gt;

&lt;p&gt;Looking at the HTML generated by the running application, you'll find that the same nonce value set in the CSP header is set in each script tag.&lt;/p&gt;

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

&lt;p&gt;By the way, if you forget to set the nonce in the &lt;code&gt;Head&lt;/code&gt; component, you can confirm that the execution of the script stops and the following error message is displayed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Refused to load the script '&amp;lt;URL&amp;gt;' because it violates the following Content Security Policy directive: "script-src 'self' 'unsafe-eval' 'unsafe-inline' https: http: 'nonce-JyWbm9+... (snip) ...wURE=' 'strict-dynamic'". Note that 'strict-dynamic' is present, so host-based allowlisting is disabled. Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  In conclusion
&lt;/h2&gt;

&lt;p&gt;That's all about how to handle CSP headers in Next.js.&lt;/p&gt;

&lt;p&gt;When I was researching how to handle CSP headers in Next.js, I noticed there wasn't much compiled information on the topic, so I decided to compile it myself.&lt;/p&gt;

&lt;p&gt;I hope this article is useful to someone.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kotamat.com/post/nextjs-strict-csp/" rel="noopener noreferrer"&gt;https://kotamat.com/post/nextjs-strict-csp/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://csp.withgoogle.com/docs/strict-csp.html" rel="noopener noreferrer"&gt;https://csp.withgoogle.com/docs/strict-csp.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Content-Security-Policy/Sources" rel="noopener noreferrer"&gt;https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Content-Security-Policy/Sources&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Content-Security-Policy/script-src#strict-dynamic" rel="noopener noreferrer"&gt;https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Content-Security-Policy/script-src#strict-dynamic&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>nextjs</category>
      <category>security</category>
    </item>
    <item>
      <title>Use a different git config for each directory using conditional include</title>
      <dc:creator>Shinji NAKAMATSU</dc:creator>
      <pubDate>Wed, 31 Aug 2022 00:17:22 +0000</pubDate>
      <link>https://dev.to/snaka/use-a-different-git-config-for-each-directory-81h</link>
      <guid>https://dev.to/snaka/use-a-different-git-config-for-each-directory-81h</guid>
      <description>&lt;p&gt;It is possible for git to use different config references depending on specific conditions.&lt;/p&gt;

&lt;p&gt;This was made aware to me by a question in an &lt;a href="https://dev.to/snaka/10-things-i-always-setup-in-git-when-i-prepare-a-new-environment-d99"&gt;earlier post&lt;/a&gt;. (Thanks to &lt;a class="mentioned-user" href="https://dev.to/francoislp"&gt;@francoislp&lt;/a&gt; asked this question).&lt;/p&gt;

&lt;p&gt;For example, if you have a working directory cloned under the &lt;code&gt;~/work/&lt;/code&gt; directory and want to use the following configuration:&lt;/p&gt;

&lt;p&gt;Add a conditional Include to &lt;code&gt;~/.gitconfig&lt;/code&gt; like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[includeIf "gitdir:~/work/**"]
        path = ~/work/.gitconfig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then put the following in &lt;code&gt;~/work/.gitconfig&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[user]
        name = Foo
        email = foo@example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, for commits in the working directory under &lt;code&gt;~/work&lt;/code&gt;, the commit will be logged as &lt;code&gt;Foo &amp;lt;foo@example.com&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As follows: &lt;/p&gt;

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

&lt;h2&gt;
  
  
  See also
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://git-scm.com/docs/git-config#_conditional_includes" rel="noopener noreferrer"&gt;Git - git-config Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>git</category>
    </item>
    <item>
      <title>10+ things I always setup in git when I prepare a new environment</title>
      <dc:creator>Shinji NAKAMATSU</dc:creator>
      <pubDate>Fri, 29 Jul 2022 02:02:48 +0000</pubDate>
      <link>https://dev.to/snaka/10-things-i-always-setup-in-git-when-i-prepare-a-new-environment-d99</link>
      <guid>https://dev.to/snaka/10-things-i-always-setup-in-git-when-i-prepare-a-new-environment-d99</guid>
      <description>&lt;p&gt;When you buy a car or a bicycle, you first adjust the seat position and saddle height to suit your body size.　It is the same with git configuration.&lt;/p&gt;

&lt;p&gt;In this article, I will share the git setup I use all the time.&lt;/p&gt;

&lt;h4&gt;
  
  
  User name &amp;amp; Mail address
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git config --global user.name "&amp;lt;name&amp;gt;" &amp;amp;&amp;amp; \
git config --global user.email "&amp;lt;email&amp;gt;"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Replace &lt;code&gt;&amp;lt;name&amp;gt;&lt;/code&gt; with my name and &lt;code&gt;&amp;lt;email&amp;gt;&lt;/code&gt; with my mail address.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Alias for existing command
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git config --global alias.co checkout
git config --global alias.st status
git config --global alias.br branch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Global ignore setting
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git config --global core.excludesfile ~/.gitignore_global
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This allows you to put your own specific ignore settings in &lt;code&gt;~/.gitignore_global&lt;/code&gt; in addition to the &lt;code&gt;.gitignore&lt;/code&gt; for each project, which will be applied to all git operations across all projects. &lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Push only the branch you are now working on
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git config --global push.default simple
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Make &lt;code&gt;--rebase&lt;/code&gt; the default behavior during git pull
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git config --global pull.rebase true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This prevents unintentional creation of a merge commit if the branch being pulled has been modified locally.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Make &lt;code&gt;--prune&lt;/code&gt; the default behavior during git fetch
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git config --global fetch.prune true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This will remove local branches that were deleted remotely when &lt;code&gt;git fetch&lt;/code&gt; or &lt;code&gt;git pull&lt;/code&gt; was performed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Set the width of indentation for tab characters
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git config --global core.pager 'less -x4'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In this example, the pager (&lt;code&gt;less&lt;/code&gt; command) option specifies tab indent width as 4&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Use &lt;code&gt;nvim&lt;/code&gt; as the editor to be used when committing
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git config --global core.editor 'nvim'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;I use several different text editors for different purposes, but I prefer to use &lt;code&gt;nvim&lt;/code&gt; with &lt;code&gt;git commit&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Do not fast-forward when merging
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git config --global --add merge.ff false
git config --global --add pull.ff only
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Fast-forward merging makes it difficult to follow the history of work on a branch. Therefore, avoid unintentional fast-forwards when merging.&lt;/li&gt;
&lt;li&gt;However, fast-forwarding is not a problem for &lt;code&gt;git pull&lt;/code&gt; cases in most cases &lt;sup id="fnref1"&gt;1&lt;/sup&gt;, so we enforce fast-forwarding in the case of pulls.&lt;/li&gt;
&lt;li&gt;See also: &lt;a href="https://qiita.com/nog/items/c79469afbf3e632f10a1" rel="noopener noreferrer"&gt;gitのmerge --no-ff のススメ - Qiita&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Output line numbers in the result of &lt;code&gt;git grep&lt;/code&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git config --global grep.lineNumber true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Visualize differences in whitespace (including newline codes)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git config diff.wsErrorHighlight all
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;EDIT: 2022-07-31&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  dotfile
&lt;/h2&gt;

&lt;p&gt;As mentioned in the comments, these are stored in &lt;code&gt;.gitconfig&lt;/code&gt;. And I have added these settings to the dotfiles repository.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/snaka/my-dotfiles/blob/master/.gitconfig" rel="noopener noreferrer"&gt;https://github.com/snaka/my-dotfiles/blob/master/.gitconfig&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks to all who commented.&lt;/p&gt;




&lt;h2&gt;
  
  
  See also
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/snaka/alias-to-remove-deleted-branches-in-a-git-remote-at-once-2h72"&gt;Alias to remove deleted branches in a git remote at once&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;In my experience, this happens when you are temporarily changing code while reviewing a PullRequest created by someone else in your local environment. In that case, fast-forward is better because local changes are gathered at the top of the history, making it easier to work with when you finally undo the changes. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>git</category>
      <category>tips</category>
    </item>
    <item>
      <title>Set the Sidekiq log level to the Rails log level</title>
      <dc:creator>Shinji NAKAMATSU</dc:creator>
      <pubDate>Tue, 26 Jul 2022 14:36:07 +0000</pubDate>
      <link>https://dev.to/snaka/set-the-sidekiq-log-level-to-the-rails-log-level-3e74</link>
      <guid>https://dev.to/snaka/set-the-sidekiq-log-level-to-the-rails-log-level-3e74</guid>
      <description>&lt;p&gt;Sidekiq's default Log Level is &lt;code&gt;INFO&lt;/code&gt;, and its settings are indepent from Rails.&lt;/p&gt;

&lt;p&gt;Sidekiq uses the standard Ruby &lt;code&gt;Logger&lt;/code&gt; class for logging.&lt;/p&gt;

&lt;p&gt;To change the Log Level in the &lt;code&gt;Logger&lt;/code&gt; class, give a constant value for Severity using the &lt;code&gt;#level=&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;Set the Sidekiq Log Level for the &lt;code&gt;Logger&lt;/code&gt; used by Sidekiq in &lt;code&gt;config/initializers/sidekiq.rb&lt;/code&gt; as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Sidekiq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure_server&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;level&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/mperham/sidekiq/wiki/Logging" rel="noopener noreferrer"&gt;Logging · mperham/sidekiq Wiki&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://guides.rubyonrails.org/debugging_rails_applications.html#log-levels" rel="noopener noreferrer"&gt;2.2 Log Levels - Debugging Rails Applications - Rails Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.ruby-lang.org/en/3.1/Logger/Severity.html" rel="noopener noreferrer"&gt;module Logger::Severity - RDoc Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rails</category>
      <category>sidekiq</category>
      <category>tips</category>
    </item>
    <item>
      <title>Alias to remove deleted branches in a git remote at once</title>
      <dc:creator>Shinji NAKAMATSU</dc:creator>
      <pubDate>Sat, 16 Jul 2022 01:17:22 +0000</pubDate>
      <link>https://dev.to/snaka/alias-to-remove-deleted-branches-in-a-git-remote-at-once-2h72</link>
      <guid>https://dev.to/snaka/alias-to-remove-deleted-branches-in-a-git-remote-at-once-2h72</guid>
      <description>&lt;p&gt;When working with git, local branches that have already been used are accumulated. Then I want to delete them all at once, so I defined an alias.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git config --global alias.prune-no-longer-exist '!git branch -vv | grep '"'"': gone]'"'"' | grep -v '"'"'\*'"'"' | awk '"'"'{ print $1; }'"'"' | xargs -r git branch -d'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The alias &lt;code&gt;prune-no-longer-exist&lt;/code&gt; will be available after executing the above command.&lt;/p&gt;

&lt;p&gt;It can be used as follows&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; $ git prune-no-longer-exist                                                                                                                                               
 Deleted branch xxxxx-account (was 9907023).
 Deleted branch xxxxx-account-accomplish (was 4b1bc41).
 Deleted branch xxxxx-to-gitignore (was 4154555).
 Deleted branch dependabot/bundler/puma-5.6.4 (was b8cf408).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unnecessary branches are removed from the local machine to refresh it.&lt;/p&gt;

&lt;p&gt;I referred to the following.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://digitaldrummerj.me/git-remove-local-merged-branches/" rel="noopener noreferrer"&gt;Git - Remove Local Branches That Are Merged or No Longer Exist | Justin James&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://opensource.com/article/20/11/git-aliases" rel="noopener noreferrer"&gt;8 Git aliases that make me more efficient | Opensource.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>git</category>
    </item>
    <item>
      <title>Know the difference between `rails new` with flags</title>
      <dc:creator>Shinji NAKAMATSU</dc:creator>
      <pubDate>Sat, 28 May 2022 06:16:23 +0000</pubDate>
      <link>https://dev.to/snaka/know-the-difference-between-rails-new-with-flags-861</link>
      <guid>https://dev.to/snaka/know-the-difference-between-rails-new-with-flags-861</guid>
      <description>&lt;p&gt;Whenever I do &lt;code&gt;rails new&lt;/code&gt;, I always have to check the effect of &lt;code&gt;--skip-xxx&lt;/code&gt;, so I created a repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I made
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/snaka/rails_new_flags" rel="noopener noreferrer"&gt;https://github.com/snaka/rails_new_flags&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this repository, the code output from running &lt;code&gt;rails new&lt;/code&gt; with the &lt;code&gt;Rails version&lt;/code&gt; and &lt;code&gt;flag&lt;/code&gt; combination can be referenced by git tags.&lt;/p&gt;

&lt;p&gt;The name of the tag is in the form &lt;code&gt;{Rails version}-{flag}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, Rails 7.0.1 code output with &lt;code&gt;--minimal&lt;/code&gt; can be referenced by the tag &lt;code&gt;7.0.1-minimal&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Currently, the supported Rails versions are as follows&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;6.0.x&lt;/li&gt;
&lt;li&gt;6.1.x&lt;/li&gt;
&lt;li&gt;7.0.x&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;p&gt;It may be obvious when you see it, but just in case, here is how to use it.&lt;/p&gt;

&lt;p&gt;For example, in Rails 7.0.1, there is a flag called &lt;code&gt;--api&lt;/code&gt; and you want to find out how specifying it changes the generated code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/snaka/rails_new_flags#summary" rel="noopener noreferrer"&gt;Table in README.markdown&lt;/a&gt;(Following is an excerpt) to the &lt;code&gt;--api&lt;/code&gt; flag line, click on the 🔍 at the &lt;code&gt;7.0.1&lt;/code&gt; position to see the diff on GitHub.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;flag&lt;/th&gt;
&lt;th&gt;6.0.4.4&lt;/th&gt;
&lt;th&gt;6.1.4.4&lt;/th&gt;
&lt;th&gt;7.0.1&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;--api&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/snaka/rails_new_flags/compare/6.0.4.4..6.0.4.4-api" rel="noopener noreferrer"&gt;🔍&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/snaka/rails_new_flags/compare/6.1.4.4..6.1.4.4-api" rel="noopener noreferrer"&gt;🔍&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/snaka/rails_new_flags/compare/7.0.1..7.0.1-api" rel="noopener noreferrer"&gt;🔍&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;(Diff. display as of 2022-01-08)&lt;/p&gt;

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

&lt;p&gt;From the above, we can see that specifying &lt;code&gt;--api&lt;/code&gt; makes the following difference in the output results.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gemfile に &lt;code&gt;sprockets-rails&lt;/code&gt;, &lt;code&gt;importmap-rails&lt;/code&gt;, &lt;code&gt;turbo-rails&lt;/code&gt;, &lt;code&gt;stimulus-rails&lt;/code&gt;, &lt;code&gt;jbuilder&lt;/code&gt;, ... (Omitted) ... is removed&lt;/li&gt;
&lt;li&gt;Parent class of &lt;code&gt;ApplicationController&lt;/code&gt; changes from &lt;code&gt;ActionController::Base&lt;/code&gt; to &lt;code&gt;ActionController::API&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Middleware configuration changes to the minimum required by adding &lt;code&gt;config.api_only = true&lt;/code&gt; setting.&lt;/li&gt;
&lt;li&gt;And so on...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also compare arbitrary Tags by writing your own URLs. For more information on how to compare tags on GitHub, please refer to the following document.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/pull-requests/committing-changes-to-your-project/viewing-and-comparing-commits/comparing-commits" rel="noopener noreferrer"&gt;Comparing commits - GitHub Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rails</category>
    </item>
  </channel>
</rss>
