<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Arnaud Renaud</title>
    <description>The latest articles on Forem by Arnaud Renaud (@arnaudrenaud).</description>
    <link>https://forem.com/arnaudrenaud</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%2F7043%2Fcc699bc1-6869-4c04-94ef-88afb6c8fba3.jpg</url>
      <title>Forem: Arnaud Renaud</title>
      <link>https://forem.com/arnaudrenaud</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/arnaudrenaud"/>
    <language>en</language>
    <item>
      <title>Build and Deploy a Free CMS-Powered Website (Next.js, Sanity, Netlify)</title>
      <dc:creator>Arnaud Renaud</dc:creator>
      <pubDate>Fri, 29 Aug 2025 17:30:00 +0000</pubDate>
      <link>https://forem.com/arnaudrenaud/build-and-deploy-a-free-cms-powered-website-nextjs-sanity-netlify-43c0</link>
      <guid>https://forem.com/arnaudrenaud/build-and-deploy-a-free-cms-powered-website-nextjs-sanity-netlify-43c0</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🌎 &lt;strong&gt;Live Demo:&lt;/strong&gt; &lt;a href="https://ssg-nextjs-cms-sanity.netlify.app" rel="noopener noreferrer"&gt;ssg-nextjs-cms-sanity.netlify.app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📑 &lt;strong&gt;Source Code:&lt;/strong&gt; &lt;a href="https://github.com/arnaudrenaud/ssg-nextjs-cms-sanity" rel="noopener noreferrer"&gt;github.com/arnaudrenaud/ssg-nextjs-cms-sanity&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Is this for you?
&lt;/h2&gt;

&lt;p&gt;This guide is meant to help if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you want a public, mostly read-only website (no user-supplied content)&lt;/li&gt;
&lt;li&gt;content is not supposed to change dozens of times per day, as each update will trigger a rebuild&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Advantages
&lt;/h2&gt;

&lt;p&gt;If the above fits your needs, you’ll get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a graphical CMS interface to manage content in the cloud&lt;/li&gt;
&lt;li&gt;no server or database to maintain (less work, more security)&lt;/li&gt;
&lt;li&gt;no deployment or hosting fees at all&lt;/li&gt;
&lt;li&gt;fast-loading pre-rendered pages&lt;/li&gt;
&lt;li&gt;browser-side interactivity, if wanted&lt;/li&gt;
&lt;li&gt;some form functionality (see &lt;a href="https://ssg-nextjs-cms-sanity.netlify.app/contact" rel="noopener noreferrer"&gt;contact page&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The toolkit
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;📝 &lt;strong&gt;Language:&lt;/strong&gt; TypeScript&lt;/li&gt;
&lt;li&gt;🛠️ &lt;strong&gt;Build:&lt;/strong&gt; Static Site Generation (SSG) with Next.js (App Router)&lt;/li&gt;
&lt;li&gt;📄 &lt;strong&gt;Frontend:&lt;/strong&gt; React.js, Tailwind CSS&lt;/li&gt;
&lt;li&gt;🗂️ &lt;strong&gt;CMS:&lt;/strong&gt; Sanity (free tier)&lt;/li&gt;
&lt;li&gt;🌍 &lt;strong&gt;Hosting:&lt;/strong&gt; Netlify (free tier)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Content Management System (CMS)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Content is managed through a web interface offered by Sanity&lt;/strong&gt;, a cloud provider with a free tier. You may grant editor or administrator rights to users on your Sanity interface (called the Studio).&lt;/p&gt;

&lt;p&gt;Entity types, with their attributes and relations, must be defined in code, and then deployed to the studio.&lt;/p&gt;

&lt;p&gt;Once these types are deployed, you can create, update and delete records without updating the code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example: entity types for a Blog:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Posts&lt;/code&gt;: a collection of &lt;code&gt;Post&lt;/code&gt; records, each with:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;title&lt;/code&gt; (text)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;publishedAt&lt;/code&gt; (date)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mainImage&lt;/code&gt; (image upload)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;body&lt;/code&gt; (rich text)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;author&lt;/code&gt; (reference to &lt;code&gt;Author&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Singletons (unique records), e.g.:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;HomePage&lt;/code&gt; (with a &lt;code&gt;hero&lt;/code&gt; field)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Metadata&lt;/code&gt; (for SEO: &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Rendering
&lt;/h3&gt;

&lt;p&gt;Pages are written with React, allowing browser-side behavior if wanted.&lt;/p&gt;

&lt;p&gt;Next.js retrieves content from Sanity and uses it in page rendering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pages are rendered at build-time (server-side), by Next.js with the App Router&lt;/strong&gt;. This is called static site generation (SSG) and results in a bundle of files: pages (HTML) and their assets (JS, CSS, images, etc.).&lt;/p&gt;

&lt;h3&gt;
  
  
  Deployment
&lt;/h3&gt;

&lt;p&gt;Deployment is managed by Netlify, a hosting service that serves static websites for free, provided you don't exceed a monthly bandwidth (currently 5 GB) and a good hundred of builds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You won't have to worry about updating the website following a change&lt;/strong&gt;: whenever you update content in Sanity or push code changes to GitHub, Netlify is automatically notified and rebuilds the website with the latest content and code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get started
&lt;/h2&gt;

&lt;p&gt;Fork or clone the code and make it your own (all technical steps in &lt;code&gt;README.md&lt;/code&gt;):&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/arnaudrenaud/ssg-nextjs-cms-sanity" rel="noopener noreferrer"&gt;github.com/arnaudrenaud/ssg-nextjs-cms-sanity&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Also published on my personal blog: &lt;a href="https://www.arnaudrenaud.com" rel="noopener noreferrer"&gt;arnaudrenaud.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ssg</category>
      <category>cms</category>
      <category>nextjs</category>
      <category>sanity</category>
    </item>
    <item>
      <title>Both-sides form validation with Next.js (React Hook Form &amp; next-safe-action)</title>
      <dc:creator>Arnaud Renaud</dc:creator>
      <pubDate>Wed, 09 Apr 2025 13:03:06 +0000</pubDate>
      <link>https://forem.com/arnaudrenaud/both-sides-form-validation-with-nextjs-react-hook-form-next-safe-action-1di1</link>
      <guid>https://forem.com/arnaudrenaud/both-sides-form-validation-with-nextjs-react-hook-form-next-safe-action-1di1</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;✅ Result:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a form that submits to a server-side action&lt;/li&gt;
&lt;li&gt;shared validation logic on both client and server&lt;/li&gt;
&lt;li&gt;browser-side form state management: loading, success, and error states&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🛠️ Tech stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next.js Server Actions&lt;/li&gt;
&lt;li&gt;next-safe-action&lt;/li&gt;
&lt;li&gt;Zod&lt;/li&gt;
&lt;li&gt;React Hook Form&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📦 Code: &lt;a href="https://github.com/arnaudrenaud/nextjs-forms-both-sides-validation" rel="noopener noreferrer"&gt;https://github.com/arnaudrenaud/nextjs-forms-both-sides-validation&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Forms have been – and still are – a pain for the web developer.&lt;/p&gt;

&lt;p&gt;In particular, one has to enforce &lt;strong&gt;server-side validation for security&lt;/strong&gt;, while also providing &lt;strong&gt;client-side validation for user experience&lt;/strong&gt; (HTML-only validation does not cover all cases).&lt;/p&gt;

&lt;p&gt;Without systematic handling, this is a tedious job, often leading the developer to overlook both security and user experience.&lt;/p&gt;

&lt;p&gt;Let's see how the React-Next.js toolkit can make this easier.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;🤔 My code is in TypeScript. Why would I need server-side type validation?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;⚠️ TypeScript does not validate types at runtime. Besides, a Zod schema allows us to define constraints that go beyond TypeScript types, such as string length.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  A developer-friendly solution
&lt;/h2&gt;

&lt;p&gt;We would like to have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;arguments validated in real-time browser-side&lt;/li&gt;
&lt;li&gt;arguments validated server-side before running the Next.js Server Action&lt;/li&gt;
&lt;li&gt;a React hook to gather it all and access form state: field validation errors and submission state (loading, success, error)&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Let's implement a form with a single email field.&lt;/p&gt;

&lt;h3&gt;
  
  
  Form schema
&lt;/h3&gt;

&lt;p&gt;Using Zod, the form schema can be expressed this way:&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;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;email&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;
  
  
  Form submission server-side
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use server&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;schema&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;@/app/example/schema&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;makeServerAction&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;@/lib/server/makeServerAction&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;makeServerAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;email&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="s2"&gt;`Processing form on the server with email "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Processed &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; successfully.`&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;&lt;code&gt;makeServerAction&lt;/code&gt; is our wrapper function binding the schema with the form submission handler.&lt;/p&gt;

&lt;p&gt;With this wrapper, when the server receives a form submission, if validation fails for any field, an error is returned.&lt;/p&gt;

&lt;h3&gt;
  
  
  Form client-side
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Form submission
&lt;/h4&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%2Fgwa5vnnpdhx5sl6qp162.gif" 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%2Fgwa5vnnpdhx5sl6qp162.gif" alt="Video showing form submission processed on the server." width="960" height="599"&gt;&lt;/a&gt;&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&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;useFormServerAction&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;@/lib/browser/useFormServerAction&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;schema&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;@/app/example/schema&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;action&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;@/app/example/action&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Form&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;form&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;submit&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useFormServerAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;submit&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;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Email address:
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&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;label&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;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Submit&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&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;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;useFormServerAction&lt;/code&gt; will be our custom hook binding the browser-side form with the &lt;code&gt;action&lt;/code&gt; and &lt;code&gt;schema&lt;/code&gt; declared earlier.&lt;/p&gt;

&lt;p&gt;For now, we only use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;form.register&lt;/code&gt; to bind the field to the form state&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;submit&lt;/code&gt; to call the action when submitting the form&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2. Browser-side schema validation
&lt;/h4&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%2Fvbbiwd7jsh8r12teh2t4.gif" 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%2Fvbbiwd7jsh8r12teh2t4.gif" alt="Video showing browser-side schema validation on field blur." width="960" height="599"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Here, we see both our own validation (message in red) and the HTML-based validation offered by the browser (tooltip).&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;export function Form() {
&lt;/span&gt;  const {
    form,
&lt;span class="gi"&gt;+   fieldErrors,
&lt;/span&gt;    submit
  } = useFormServerAction(schema, action);
&lt;span class="err"&gt;
&lt;/span&gt;  return (
    &amp;lt;form onSubmit={submit}&amp;gt;
      &amp;lt;label&amp;gt;
        Email address:
        &amp;lt;input type="email" {...form.register("email")} /&amp;gt;
      &amp;lt;/label&amp;gt;
&lt;span class="gi"&gt;+     {fieldErrors.email &amp;amp;&amp;amp; (
+       &amp;lt;div className="text-sm text-red-500"&amp;gt;
+         {fieldErrors.email.message}
+       &amp;lt;/div&amp;gt;
+     )}
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;      &amp;lt;button&amp;gt;Submit&amp;lt;/button&amp;gt;
    &amp;lt;/form&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our hook performs field validation in the browser (by default, on field blur) following the same schema as the one used on the server.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Server-side schema validation
&lt;/h4&gt;

&lt;p&gt;Let's make sure server-side validation is enforced when bypassing the web interface and submitting a malformed email value:&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%2Fwfsfmy8fzgtc0amcc41s.gif" 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%2Fwfsfmy8fzgtc0amcc41s.gif" alt="Video showing server response to a malformed submission." width="960" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The server does not run the action and it responds with an error object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"validationErrors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"_errors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Invalid email"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  4. Success state
&lt;/h4&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%2Fmwt8c3d0zeuu271spnz5.gif" 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%2Fmwt8c3d0zeuu271spnz5.gif" alt="Video showing success message to the user after server response." width="960" height="599"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;export function Form() {
&lt;/span&gt;  const {
    form,
    fieldErrors,
    submit,
&lt;span class="gi"&gt;+   action: { result },
&lt;/span&gt;  } = useFormServerAction(schema, action);
&lt;span class="err"&gt;
&lt;/span&gt;  return (
    &amp;lt;form onSubmit={submit}&amp;gt;
      &amp;lt;label&amp;gt;
        Email address:
        &amp;lt;input type="email" {...form.register("email")} /&amp;gt;
      &amp;lt;/label&amp;gt;
      {fieldErrors.email &amp;amp;&amp;amp; (
        &amp;lt;div className="text-sm text-red-500"&amp;gt;
          {fieldErrors.email.message}
        &amp;lt;/div&amp;gt;
      )}
&lt;span class="err"&gt;
&lt;/span&gt;      &amp;lt;button&amp;gt;Submit&amp;lt;/button&amp;gt;
&lt;span class="gi"&gt;+
+     {result.data &amp;amp;&amp;amp; (
+       &amp;lt;div className="text-green-500"&amp;gt;{result.data.message}&amp;lt;/div&amp;gt;
+     )}
&lt;/span&gt;    &amp;lt;/form&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  5. Loading state
&lt;/h4&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%2Ftczm786eiv91lbnju5g7.gif" 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%2Ftczm786eiv91lbnju5g7.gif" alt="Video showing disabled button while waiting for server response." width="960" height="599"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;export function Form() {
&lt;/span&gt;  const {
    form,
    fieldErrors,
    submit,
    action: {
&lt;span class="gi"&gt;+     isExecuting,
&lt;/span&gt;      result
    },
  } = useFormServerAction(schema, action);
&lt;span class="err"&gt;
&lt;/span&gt;  return (
    &amp;lt;form onSubmit={submit}&amp;gt;
      &amp;lt;label&amp;gt;
        Email address:
        &amp;lt;input type="email" {...form.register("email")} /&amp;gt;
      &amp;lt;/label&amp;gt;
      {fieldErrors.email &amp;amp;&amp;amp; (
        &amp;lt;div className="text-sm text-red-500"&amp;gt;
          {fieldErrors.email.message}
        &amp;lt;/div&amp;gt;
      )}
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;-     &amp;lt;button&amp;gt;Submit&amp;lt;/button&amp;gt;
&lt;/span&gt;&lt;span class="gi"&gt;+     &amp;lt;button disabled={isExecuting}&amp;gt;
+       {isExecuting ? "Loading…" : "Submit"}
+     &amp;lt;/button&amp;gt;
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;      {!isExecuting &amp;amp;&amp;amp; result.data &amp;amp;&amp;amp; (
        &amp;lt;div className="text-green-500"&amp;gt;{result.data.message}&amp;lt;/div&amp;gt;
      )}
    &amp;lt;/form&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  6. Error state
&lt;/h4&gt;

&lt;p&gt;Unexpected errors are hidden from the user, replaced by a default message to avoid exposing any implementation details:&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%2Fa0f3fbxg60ij6r20hcrh.gif" 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%2Fa0f3fbxg60ij6r20hcrh.gif" alt="Video showing default error message to the user after server response." width="960" height="599"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the other hand, known exceptions are shown to help the user:&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%2Fhux4zjicg15mq8806yr0.gif" 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%2Fhux4zjicg15mq8806yr0.gif" alt="Video showing specific exception message to the user after server response." width="960" height="599"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To throw an error recognized as a known exception, you must instantiate it as an &lt;code&gt;Exception&lt;/code&gt; (a custom class that inherits the native &lt;code&gt;Error&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;export function Form() {
&lt;/span&gt;  const {
    form,
    fieldErrors,
    submit,
    action: { isExecuting, result },
  } = useFormServerAction(schema, action);
&lt;span class="err"&gt;
&lt;/span&gt;  return (
    &amp;lt;form onSubmit={submit}&amp;gt;
      &amp;lt;label&amp;gt;
        Email address:
        &amp;lt;input type="email" {...form.register("email")} /&amp;gt;
      &amp;lt;/label&amp;gt;
      {fieldErrors.email &amp;amp;&amp;amp; (
        &amp;lt;div className="text-sm text-red-500"&amp;gt;
          {fieldErrors.email.message}
        &amp;lt;/div&amp;gt;
      )}
&lt;span class="err"&gt;
&lt;/span&gt;      &amp;lt;button disabled={isExecuting}&amp;gt;
        {isExecuting ? "Loading…" : "Submit"}
      &amp;lt;/button&amp;gt;
&lt;span class="err"&gt;
&lt;/span&gt;      {result.data &amp;amp;&amp;amp; (
        &amp;lt;div className="text-green-500"&amp;gt;{result.data.message}&amp;lt;/div&amp;gt;
      )}
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+     {result.serverError &amp;amp;&amp;amp; (
+       &amp;lt;div className="text-red-500"&amp;gt;{result.serverError}&amp;lt;/div&amp;gt;
+     )}
&lt;/span&gt;    &amp;lt;/form&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Full code
&lt;/h2&gt;

&lt;p&gt;Take a look at the wrapper functions &lt;a href="https://github.com/arnaudrenaud/nextjs-forms-both-sides-validation/blob/main/src/lib/server/makeServerAction.ts" rel="noopener noreferrer"&gt;&lt;code&gt;makeServerAction&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/arnaudrenaud/nextjs-forms-both-sides-validation/blob/main/src/lib/browser/useFormServerAction.tsx" rel="noopener noreferrer"&gt;&lt;code&gt;useFormServerAction&lt;/code&gt;&lt;/a&gt; to glue action, schema and form.&lt;/p&gt;

&lt;p&gt;📦 Code: &lt;a href="https://github.com/arnaudrenaud/nextjs-forms-both-sides-validation" rel="noopener noreferrer"&gt;https://github.com/arnaudrenaud/nextjs-forms-both-sides-validation&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;With a small set of abstractions, we get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Consistent validation&lt;/strong&gt; across browser and server using a shared Zod schema&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplified form components&lt;/strong&gt;, thanks to a custom hook that abstracts form state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This setup scales well as forms grow in complexity, and helps you avoid bugs from mismatched validation logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;You can copy the wrapper functions to your Next.js codebase to start using this pattern today.&lt;/p&gt;

&lt;p&gt;To tweak or extend behavior, check out the underlying tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;next-safe-action's &lt;a href="https://next-safe-action.dev/docs/define-actions/create-the-client" rel="noopener noreferrer"&gt;&lt;code&gt;createSafeActionClient&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;React Hook Form's &lt;a href="https://react-hook-form.com/docs/useform" rel="noopener noreferrer"&gt;&lt;code&gt;useForm&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Zod &lt;a href="https://zod.dev/?id=primitives" rel="noopener noreferrer"&gt;schema primitives&lt;/a&gt; (you can also define &lt;a href="https://github.com/colinhacks/zod#superrefine" rel="noopener noreferrer"&gt;multi-field validation rules&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>react</category>
      <category>nextjs</category>
      <category>forms</category>
      <category>validation</category>
    </item>
    <item>
      <title>10 Ways to Improve for a Junior Developer</title>
      <dc:creator>Arnaud Renaud</dc:creator>
      <pubDate>Fri, 05 Jul 2024 12:00:00 +0000</pubDate>
      <link>https://forem.com/arnaudrenaud/10-ways-to-improve-for-a-junior-developer-22pj</link>
      <guid>https://forem.com/arnaudrenaud/10-ways-to-improve-for-a-junior-developer-22pj</guid>
      <description>&lt;p&gt;I taught the practice of web development to around 200 students in six years. Here are ways to improve on the mistakes and misconceptions I noticed most often.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Use automatic code formatting&lt;/li&gt;
&lt;li&gt;Look for error messages&lt;/li&gt;
&lt;li&gt;Simplify your code until the bug goes away&lt;/li&gt;
&lt;li&gt;Solve only one problem at a time&lt;/li&gt;
&lt;li&gt;Split tasks and commits&lt;/li&gt;
&lt;li&gt;Give symbols a proper name&lt;/li&gt;
&lt;li&gt;Refrain from DRYing everything out&lt;/li&gt;
&lt;li&gt;Optimize reasonably&lt;/li&gt;
&lt;li&gt;Test the risky parts first&lt;/li&gt;
&lt;li&gt;Avoid persisting redundant data&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Use automatic code formatting
&lt;/h2&gt;

&lt;p&gt;Even if you work alone, automatic formatting is a free time saver: you can write code faster, and will be able to read it more easily later.&lt;/p&gt;

&lt;p&gt;If you write JavaScript or TypeScript, install the Prettier extension for Visual Studio Code and enable "Format on save".&lt;br&gt;
That's all, you don't need a configuration file, but you can add one at the root of your project if you need to override the default rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  Look for error messages
&lt;/h2&gt;

&lt;p&gt;When the app &lt;em&gt;doesn't work&lt;/em&gt;, look for an error message. This is especially true when your app is made of multiple services.&lt;/p&gt;

&lt;p&gt;Let's say your Next.js web app with server-side rendering relies on your back-end server. When something fails, here are four places you have to look for an error:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the browser console (error on the client)&lt;/li&gt;
&lt;li&gt;the server response body in the inspector's Network tab (invalid request error, it should appear in the UI)&lt;/li&gt;
&lt;li&gt;the Next.js server console (error during SSR)&lt;/li&gt;
&lt;li&gt;the back-end server console (error on the back end)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Simplify your code until the bug goes away
&lt;/h2&gt;

&lt;p&gt;What to do if you can't find any error message, or if the cause of a bug remains obscure after searching the error online?&lt;/p&gt;

&lt;p&gt;Simplify your code (for instance, comment out some part that runs when the bug happens), and try to reproduce the bug again. If reproducible, repeat again and again until the bug is gone.&lt;/p&gt;

&lt;p&gt;Sometimes, you only need to do this once to find the faulty part in your program.&lt;/p&gt;

&lt;p&gt;Some other times, you simplify your program so much that it looks like a Hello world boilerplate and yet the bug is still here.&lt;br&gt;
Try updating dependencies or changing environment variables. If you are still unlucky, post your issue on Stack Overflow or, if applicable, on your framework's repository issues on GitHub, and provide your simplified code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solve only one problem at a time
&lt;/h2&gt;

&lt;p&gt;If you try to simultaneously fix a bug, implement a new feature and refactor code, chances are you will waste time by breaking your code or mix unrelated changes in a single commit, which will make code review harder.&lt;/p&gt;

&lt;p&gt;If you feel the need for a refactor or a bug fix in the middle of an unrelated feature, refrain from doing it. Save it for later, preferably by opening a dedicated ticket (you can automate it with a GitHub action that will create an issue from any "TODO" mention in your code, for example &lt;a href="https://github.com/marketplace/actions/todo-to-issue"&gt;TODO to Issue&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Split tasks and commits
&lt;/h2&gt;

&lt;p&gt;When you receive a ticket for a new feature, work on it before you start implementing it. Add a checklist of subtasks and cases to handle, and implement them one after the other. You can also use this detailed case-by-case specification to write automated tests. This will help you keep going when the task at hand is overwhelming.&lt;/p&gt;

&lt;p&gt;The same discipline goes for commits for large features: help the code reviewer by splitting your work into commits with a clear title.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give symbols a proper name
&lt;/h2&gt;

&lt;p&gt;Symbols are all the phrases that you define in your code: types, interfaces, constants, variables, functions, classes, methods.&lt;/p&gt;

&lt;p&gt;The name &lt;code&gt;data&lt;/code&gt; is rarely helpful when reading code. Take some time to give a descriptive name to your data and don't forget to use the plural for arrays.&lt;/p&gt;

&lt;p&gt;Function and method names should start with a verb that describes what is returned (and/or performed).&lt;/p&gt;

&lt;p&gt;Do not be afraid of longer names if necessary: &lt;em&gt;Everything should be made as simple as possible, but not simpler.&lt;/em&gt; (Einstein).&lt;/p&gt;

&lt;p&gt;Follow these rules and your code should read &lt;em&gt;almost&lt;/em&gt; like a sentence in English.&lt;/p&gt;

&lt;h2&gt;
  
  
  Refrain from DRYing everything out
&lt;/h2&gt;

&lt;p&gt;It is tempting to deduplicate everything in your codebase because someone said "Don't repeat yourself".&lt;/p&gt;

&lt;p&gt;You can have rules that look the same but are conceptually different. If you refactor them into an abstraction, they could become harder to understand and maintain.&lt;/p&gt;

&lt;p&gt;Rather than DRY, make your code ETC: &lt;em&gt;Easier To Change&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This means using a &lt;em&gt;single source of truth&lt;/em&gt; for things that are conceptually the same, for example: duplicate constants, business rules or UI components. If you copy-paste them, they will become hard to change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimize reasonably
&lt;/h2&gt;

&lt;p&gt;Just like excessive DRYness, premature optimization is a waste of time.&lt;/p&gt;

&lt;p&gt;You might want to improve the performance of an algorithm, for instance by replacing a loop with a regular expression.&lt;/p&gt;

&lt;p&gt;Is the performance gain valuable to the user in real-world conditions, with actual data? Is the gain offset by a potential loss in code readability?&lt;/p&gt;

&lt;p&gt;A lot of time, micro-optimizations are not worth it.&lt;/p&gt;

&lt;p&gt;A rule of thumb to avoid poor performance before it hits you in production:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;keep the &lt;a href="https://stackoverflow.com/a/11611770/2339721"&gt;time complexity of the algorithm&lt;/a&gt; under &lt;em&gt;O(n log n)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;if you run database queries, do not run them in a loop of the size of the input but use a fixed number of join queries instead&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Test the risky parts first
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;What part of my code should I test?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Again, make good use of your time. Test what is most critical and/or most at risk of failure.&lt;/p&gt;

&lt;p&gt;You can start by testing the core business rules of the application, especially if they are supposed to evolve over time (they are at risk of regression).&lt;/p&gt;

&lt;p&gt;Also, when a bug is reported, you can start by writing a test that highlights the bug (which means it should fail) before fixing the implementation (&lt;em&gt;test-driven development&lt;/em&gt;).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Should I test all cases?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Test cases that are expected to happen in real-world usage, starting with the most important ones, business-wise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Avoid persisting redundant data
&lt;/h2&gt;

&lt;p&gt;Again, aim for a single source of truth.&lt;/p&gt;

&lt;p&gt;Whether you're developing a stateful user interface or a service connected to a database, you're going to persist a state (in memory or on the disk).&lt;/p&gt;

&lt;p&gt;In React components, I have seen many times a filtered array set to state whereas it could be calculated at render time. Instead of setting in state only the filter arguments, both the arguments and the derived array are stored, making code more verbose and error-prone because both state values must be updated everytime the filter arguments change.&lt;/p&gt;

&lt;p&gt;Similarly, I have seen redundant database design, where data is set in a column whereas it could be derived (calculated) in real time.&lt;/p&gt;

&lt;p&gt;However, while this single-source-of-truth approach is conceptually cleaner and easier to maintain, it can lead to performance issues if the calculation is heavy and repeated multiple times with the same arguments: why recalculate it if the result remains the same?&lt;/p&gt;

&lt;p&gt;In this case, you can cache the result of the calculation using memoization: for instance &lt;code&gt;useMemo&lt;/code&gt; in React, which will automatically refresh calculation when arguments change.&lt;/p&gt;

&lt;p&gt;In a database, for instance Postgres, you can use materialized views, but you will have to refresh their content manually.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Actively look for error messages ; don't be afraid of them&lt;/li&gt;
&lt;li&gt;Keep your work going by splitting tasks into smaller units that are easier to complete&lt;/li&gt;
&lt;li&gt;Think of the person that will read your code: favor readability over excessive factorization or optimization&lt;/li&gt;
&lt;li&gt;Beware of premature optimization: use your time to fix perceivable problems&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>What to Do with Runtime Errors</title>
      <dc:creator>Arnaud Renaud</dc:creator>
      <pubDate>Mon, 29 Apr 2024 10:38:29 +0000</pubDate>
      <link>https://forem.com/arnaudrenaud/what-to-do-with-runtime-errors-f0o</link>
      <guid>https://forem.com/arnaudrenaud/what-to-do-with-runtime-errors-f0o</guid>
      <description>&lt;p&gt;Errors are painful for the developer and the user alike. Errors without a clear message even more. And what about silent errors that make debugging a pain?&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Do not blindly use &lt;code&gt;try-catch&lt;/code&gt; blocks&lt;/li&gt;
&lt;li&gt;Uncaught errors end up in logs, making debugging easier&lt;/li&gt;
&lt;li&gt;If an error is not your fault, catch it (it is called an exception)&lt;/li&gt;
&lt;li&gt;If an error is the user's fault, tell them about it&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Goals
&lt;/h2&gt;

&lt;p&gt;The objective of this practical guide is to help you, the developer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Distinguish exceptions from errors&lt;/li&gt;
&lt;li&gt;Give yourself full information about the error&lt;/li&gt;
&lt;li&gt;Give the user limited but useful information about the error&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ll use the example of a web server, but the same principles apply to any other type of server-side and to most client-side applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Error or exception?
&lt;/h2&gt;

&lt;p&gt;A runtime error happens when an instruction fails to complete.&lt;/p&gt;

&lt;p&gt;You can either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;let errors terminate the program – in this case, errors are left &lt;em&gt;unckecked&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;or catch them before they do – in this case, errors are &lt;em&gt;checked&lt;/em&gt; and are called &lt;em&gt;exceptions&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Unchecked error
&lt;/h3&gt;

&lt;p&gt;Errors may imply a bug: a logical misconception or some misconfiguration. In this case, they should not be caught and silenced but rather fixed.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an algorithm crashes unexpectedly&lt;/li&gt;
&lt;li&gt;you try to connect to a database with invalid credentials&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Exceptions (checked error)
&lt;/h3&gt;

&lt;p&gt;Reciprocally, an error may not imply a malfunction in the program. It is typically the case when abnormal behavior comes from a third party.&lt;/p&gt;

&lt;p&gt;Hence the name, &lt;em&gt;exception&lt;/em&gt;: exceptionally, it should be caught and worked around because there is nothing to fix in our program.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a user requests a non-existent resource&lt;/li&gt;
&lt;li&gt;a user submits malformed data&lt;/li&gt;
&lt;li&gt;an external provider does not respond&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What to do in four cases
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. User-induced error
&lt;/h3&gt;

&lt;p&gt;In case of a user-induced error (for example, a malformed request):&lt;/p&gt;

&lt;p&gt;⚙️ &lt;em&gt;Is it an error or an exception?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;An exception (it must be caught).&lt;/p&gt;

&lt;p&gt;🧑‍💻 &lt;em&gt;Should I tell the user all about it?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Absolutely, so they can fix it.&lt;/p&gt;

&lt;p&gt;📝 &lt;em&gt;Should I output the error to logs?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You may, marking it as a user-induced exception. This way, you could learn about app usage and improve user experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Third-party-induced error
&lt;/h3&gt;

&lt;p&gt;In case of a third-party-induced error (for example, an external remote API used by your app):&lt;/p&gt;

&lt;p&gt;⚙️ &lt;em&gt;Is it an error or an exception?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;An exception (it must be caught).&lt;/p&gt;

&lt;p&gt;🧑‍💻 &lt;em&gt;Should I tell the user all about it?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Yes, so that they know it’s not a bug on your side: you can’t do much, they just have to wait.&lt;/p&gt;

&lt;p&gt;📝 &lt;em&gt;Should I output the error to logs?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Yes, marking it as a third-party-induced exception. This way, you could learn about deficiencies among your external providers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Internal error
&lt;/h3&gt;

&lt;h4&gt;
  
  
  3. Outside a user request
&lt;/h4&gt;

&lt;p&gt;In case of an error occuring outside a user request (for example a script failing at server startup):&lt;/p&gt;

&lt;p&gt;⚙️ &lt;em&gt;Is it an error or an exception?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;An error (it must &lt;em&gt;not&lt;/em&gt; be caught).&lt;/p&gt;

&lt;p&gt;📝 &lt;em&gt;Should I output the error to logs?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Absolutely, it’s a failure that needs to be fixed.&lt;/p&gt;

&lt;p&gt;You have nothing to do in this case. By default, it will crash the server and output the error.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. Within a user request
&lt;/h4&gt;

&lt;p&gt;In case of an error occuring inside a user request:&lt;/p&gt;

&lt;p&gt;⚙️ &lt;em&gt;Is it an error or an exception?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;An error (it must &lt;em&gt;not&lt;/em&gt; be caught).&lt;/p&gt;

&lt;p&gt;🧑‍💻 &lt;em&gt;Should I tell the user all about it?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;No, just show them a simple message (such as "Internal server error") so that they know something has to be fixed on your side.&lt;/p&gt;

&lt;p&gt;📝 &lt;em&gt;Should I output the error to logs?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Absolutely, it’s a failure that needs to be fixed.&lt;/p&gt;

&lt;p&gt;You generally have nothing to do in this case. Any error uncaught by the developer will, in the end, be caught by the server and result in a default client-side error message while it outputs the full error in logs.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: in Node.js Express 4 however, you have to catch async errors yourself to avoid a server crash.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  In practice in JavaScript
&lt;/h2&gt;

&lt;p&gt;Some programming languages provide a distinction between plain errors and exceptions, such as Java with classes &lt;code&gt;Error&lt;/code&gt; and &lt;code&gt;Exception&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In JavaScript, you can only &lt;code&gt;throw new Error(…)&lt;/code&gt;. It is up to you to distinguish exceptions and actual errors.&lt;/p&gt;

&lt;p&gt;Below is a example with a Node.js Express server (version &lt;code&gt;5.0.0-beta.3&lt;/code&gt;) handling all four different cases seen above:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User-induced exception&lt;/li&gt;
&lt;li&gt;Third-party induced exception&lt;/li&gt;
&lt;li&gt;Error outside a user request&lt;/li&gt;
&lt;li&gt;Error within a user request
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ⚠️ This will fail with Express version 4: make sure you use Express 5+ (tested on `express@5.0.0-beta.3`)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express&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;EXCEPTIONS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;THING_WITH_NAME_ALREADY_EXISTS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;THING_WITH_NAME_ALREADY_EXISTS&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A thing with the same name already exists.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// case 1: we do not want to log this user-induced exception&lt;/span&gt;
    &lt;span class="na"&gt;shouldLog&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;span class="na"&gt;NOTIFICATION_SERVICE_UNAVAILABLE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;NOTIFICATION_SERVICE_UNAVAILABLE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The notification service is currently unavaible, please retry later.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;status&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="c1"&gt;// case 2: we want to log this third-party-induced exception&lt;/span&gt;
    &lt;span class="na"&gt;shouldLog&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;// …&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// this function will distinguish exceptions and unchecked errors&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleErrors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&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;exception&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;EXCEPTIONS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;exception&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;shouldLog&lt;/span&gt;&lt;span class="p"&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;error&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;exception&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;EXCEPTION&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="s2"&gt;ERROR&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;res&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&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="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Internal server error.&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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;notify&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&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;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// case 2: third-party-induced exception&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;EXCEPTIONS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NOTIFICATION_SERVICE_UNAVAILABLE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;cause&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;runServer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// case 3: an error at startup will crash the server, as wanted&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&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;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// case 4: error will be caught by `handleErrors`, which will tell the user "Internal server error."&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;random unchecked error&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;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/things&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;existingThingWithName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;database&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="cm"&gt;/* name from req.body */&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;existingThingWithName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// case 1: user-induced exception&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;EXCEPTIONS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;THING_WITH_NAME_ALREADY_EXISTS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/* … */&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;handleErrors&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4000&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="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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Server listening on port 4000…&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;runServer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Using these rules, both you and the user will be properly informed at the correct level:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The user knows whether:

&lt;ul&gt;
&lt;li&gt;their request is incorrect&lt;/li&gt;
&lt;li&gt;or they just need to wait for a third-party service to resume&lt;/li&gt;
&lt;li&gt;or they need to wait for a fix&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;You know whether errors need a fix or if they are exceptions independent of your work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, you have seen it is no use to surround everything with &lt;code&gt;try-catch&lt;/code&gt; blocks: you should only catch known exceptions and let your server treat the rest as errors.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>node</category>
    </item>
  </channel>
</rss>
