<?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: Ayooluwa Isaiah</title>
    <description>The latest articles on DEV Community by Ayooluwa Isaiah (@ayoisaiah).</description>
    <link>https://dev.to/ayoisaiah</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%2F64416%2F709fe083-68cf-4589-94f5-50b1cc8b724e.jpg</url>
      <title>DEV Community: Ayooluwa Isaiah</title>
      <link>https://dev.to/ayoisaiah</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ayoisaiah"/>
    <language>en</language>
    <item>
      <title>Creating Your First Chrome Extension (With Built-in AI Features)</title>
      <dc:creator>Ayooluwa Isaiah</dc:creator>
      <pubDate>Mon, 17 Nov 2025 13:00:00 +0000</pubDate>
      <link>https://dev.to/ayoisaiah/creating-your-first-chrome-extension-with-built-in-ai-features-2hfe</link>
      <guid>https://dev.to/ayoisaiah/creating-your-first-chrome-extension-with-built-in-ai-features-2hfe</guid>
      <description>&lt;p&gt;&lt;a href="https://developer.chrome.com/docs/extensions" rel="noopener noreferrer"&gt;Chrome extensions&lt;/a&gt; let you customize the browser in ways that go far beyond simple settings or themes. They can block ads, automate repetitive tasks, enhance your favorite sites, or reshape the browser interface entirely.&lt;/p&gt;

&lt;p&gt;If you've ever wished a website or the browser itself behaved just a bit differently, chances are &lt;a href="https://chromewebstore.google.com/" rel="noopener noreferrer"&gt;an extension already exists&lt;/a&gt; to do exactly that.&lt;/p&gt;

&lt;p&gt;Learning how to build extensions opens up a space where familiar web technologies gain access to native browser capabilities. You can tap into APIs that ordinary web pages cannot use, allowing your code to run in the background, react to browser events, store data, and integrate deeply into how people work online.&lt;/p&gt;

&lt;p&gt;And now, &lt;a href="https://developer.chrome.com/docs/ai" rel="noopener noreferrer"&gt;with built-in AI models available directly in Chrome&lt;/a&gt;, extensions can go even further. You can generate text on the fly, summarize content, assist users contextually, or build entirely new experiences that adapt to the user's intent.&lt;/p&gt;

&lt;p&gt;So whether you're solving a personal frustration or just experimenting with a new distribution channel for your ideas, extensions offer an approachable yet powerful platform. And if you already know HTML, CSS, and JavaScript, you're already 95% of the way there.&lt;/p&gt;

&lt;p&gt;In this tutorial, you'll learn the rest of the fundamentals by building a small but fully functional Chrome extension from scratch.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you'll be building
&lt;/h2&gt;

&lt;p&gt;To understand the various Chrome extensions concepts, you'll build a new tab extension that replaces the default new tab page with a random image from Unsplash.&lt;/p&gt;

&lt;p&gt;If you've ever used popular new tab extensions like &lt;a href="https://chromewebstore.google.com/detail/tabliss-a-beautiful-new-t/hipekcciheckooncpjeljhnekcoolahp" rel="noopener noreferrer"&gt;Tabliss&lt;/a&gt;, or &lt;a href="https://chromewebstore.google.com/detail/momentum/laookkfknpbbblfpciffpaejjkokdgca" rel="noopener noreferrer"&gt;Momentum&lt;/a&gt;, you already know how transformative this simple UI tweak can be. These extensions turn an empty tab into something visually delightful, functional, or even calming.&lt;/p&gt;

&lt;p&gt;Your own version will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fetch a random photo from Unsplash.&lt;/li&gt;
&lt;li&gt;Cache the image for instant loading each time a new tab opens.&lt;/li&gt;
&lt;li&gt;Store user preferences such as their own Unsplash access key and optional photo collections.&lt;/li&gt;
&lt;li&gt;Provide a simple settings interface where users can manage these options.&lt;/li&gt;
&lt;li&gt;Include a small popup for quick access to the settings page.&lt;/li&gt;
&lt;li&gt;Optionally display an AI generated motivational message using Chrome's built-in on-device model.&lt;/li&gt;
&lt;/ul&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%2F6vwpbtlk6oe24k5b38nz.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%2F6vwpbtlk6oe24k5b38nz.png" alt="New tab page extension completed" width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By the end of this guide, you'll understand how the different parts of an extension fit together: the manifest, service worker, new tab override, options page, popup, and storage.&lt;/p&gt;

&lt;p&gt;You'll also see how to use the built-in AI APIs to add a new dimension to what an extension can offer while still keeping everything running locally and securely on the user's device.&lt;/p&gt;

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

&lt;p&gt;If you have experience building basic web pages, you have everything you need. But make sure you also have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Google Chrome&lt;/strong&gt; (&lt;a href="https://www.google.com/chrome/" rel="noopener noreferrer"&gt;the latest version&lt;/a&gt; is recommended).&lt;/li&gt;
&lt;li&gt;A code editor such as VS Code or any editor you prefer.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Grabbing the starter files
&lt;/h2&gt;

&lt;p&gt;To keep the focus on learning how extensions work rather than writing boilerplate HTML and CSS, this tutorial includes a small set of &lt;a href="https://github.com/freshman-tech/freshtab-starter-files" rel="noopener noreferrer"&gt;starter files&lt;/a&gt;. They contain the basic layout for the new tab page, the options screen, and the extension's folder structure.&lt;/p&gt;

&lt;p&gt;You can download them from GitHub:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/freshman-tech/freshtab-starter-files.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After cloning the repository, move into the project directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;freshtab-starter-files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can browse the files using your editor or run the &lt;code&gt;tree&lt;/code&gt; command (if you have it installed) to get a quick overview of the structure.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── demo.png
├── icons
│   ├── 128.png
│   ├── 16.png
│   ├── 32.png
│   ├── 48.png
│   └── 64.png
├── index.html
├── js
│   ├── background.js
│   ├── constants.js
│   ├── index.js
│   ├── options.js
│   └── popup.js
├── LICENCE
├── manifest.json
├── options.html
├── package.json
├── popup.html
└── README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you a fully prepared workspace so you can focus on the manifest, extension APIs, and the logic that brings everything together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Anatomy of a Chrome extension
&lt;/h2&gt;

&lt;p&gt;A Chrome extension is simply a collection of files (HTML, CSS, JavaScript, plus a manifest) that work together to add new capabilities to the browser. Before writing any code, it helps to understand the role each file plays in the project.&lt;/p&gt;

&lt;h3&gt;
  
  
  The manifest
&lt;/h3&gt;

&lt;p&gt;This &lt;code&gt;manifest.json&lt;/code&gt; file is &lt;a href="https://developer.chrome.com/docs/extensions/reference/manifest" rel="noopener noreferrer"&gt;the blueprint of your extension&lt;/a&gt;. It's a single file that tells Chrome what your extension is, what it does, and the permissions it needs, and other metadata.&lt;/p&gt;

&lt;p&gt;The most important thing to know for now is that we are using &lt;a href="https://developer.chrome.com/docs/extensions/develop/migrate/what-is-mv3" rel="noopener noreferrer"&gt;Manifest V3&lt;/a&gt;, the modern standard for all new extensions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Service worker
&lt;/h3&gt;

&lt;p&gt;A service worker is an event-driven script that wakes up to handle an event (like the extension being installed or a message from another script), does its work, and then terminates.&lt;/p&gt;

&lt;p&gt;In this project, the &lt;code&gt;js/background.js&lt;/code&gt; file will act as a service worker, and react to browser events and messages to perform various actions in the background.&lt;/p&gt;

&lt;h3&gt;
  
  
  Popup window
&lt;/h3&gt;

&lt;p&gt;When a user clicks your extension's icon in the toolbar, Chrome can display a small popup. This popup is just a regular HTML page (&lt;code&gt;popup.html&lt;/code&gt;) with its own JavaScript and styling. Extensions often use popups to provide quick controls or shortcuts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Override pages
&lt;/h3&gt;

&lt;p&gt;Extensions can replace certain built in browser pages, such as the new tab page, history page, or bookmarks page. You can override one of these at a time by pointing Chrome to an HTML file in the manifest. In this guide, &lt;code&gt;index.html&lt;/code&gt; replaces the default new tab page.&lt;/p&gt;

&lt;h3&gt;
  
  
  Extension icons
&lt;/h3&gt;

&lt;p&gt;Every extension needs at least one icon so Chrome can display it in the toolbar, extensions page, and Chrome Web Store. The icons used in this tutorial live in the &lt;code&gt;icons&lt;/code&gt; folder.&lt;/p&gt;

&lt;h3&gt;
  
  
  Content scripts
&lt;/h3&gt;

&lt;p&gt;Content scripts are JavaScript files that run directly inside web pages. They can read or modify the page's DOM, making them ideal for features like password managers, ad blockers, and page enhancements.&lt;/p&gt;

&lt;p&gt;While this project doesn't use content scripts, they're an important part of many extensions and good to be aware of.&lt;/p&gt;

&lt;h2&gt;
  
  
  Populating the manifest file
&lt;/h2&gt;

&lt;p&gt;Now that you understand the structure of a Chrome extension, it's time to bring it to life by creating the &lt;code&gt;manifest.json&lt;/code&gt; file. This is the central configuration file that Chrome reads first, and without it the browser won't recognize your project as an extension.&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;manifest.json&lt;/code&gt; file in the starter project and update it with the following:&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;"manifest_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"freshtab"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Experience a beautiful photo from Unsplash every time you open a new tab."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"icons"&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;"16"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"icons/16.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"32"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"icons/32.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"48"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"icons/48.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"64"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"icons/64.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"128"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"icons/128.png"&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;"chrome_url_overrides"&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;"newtab"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"index.html"&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;"action"&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;"default_popup"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"popup.html"&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;"options_page"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"options.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&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;"storage"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"host_permissions"&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;"https://api.unsplash.com/"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"background"&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;"service_worker"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"js/background.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"module"&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;p&gt;Here's a breakdown of each field in the manifest file:&lt;/p&gt;

&lt;h3&gt;
  
  
  Required fields
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;manifest_version&lt;/code&gt;: Currently, this must always be &lt;code&gt;3&lt;/code&gt; as &lt;a href="https://blog.chromium.org/2024/05/manifest-v2-phase-out-begins.html" rel="noopener noreferrer"&gt;v2 is now deprecated&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;name&lt;/code&gt;, &lt;code&gt;version&lt;/code&gt;: The extension name and version.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;description&lt;/code&gt;: What the extension does.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;icons&lt;/code&gt;: Specifies icons for your extension in different sizes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Optional fields
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;chrome_url_overrides&lt;/code&gt;: Provides a custom replacement for default browser pages (like new tab, history, or bookmarks). In this case, the new tab page is being replaced with the &lt;code&gt;index.html&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;action&lt;/code&gt;: Defines the appearance and behavior for the extension's toolbar icon.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;options_page&lt;/code&gt;: Specifies the path to the options page for your extension.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;permissions&lt;/code&gt;: Defines the &lt;a href="https://developer.chrome.com/extensions/declare_permissions" rel="noopener noreferrer"&gt;permissions&lt;/a&gt; required by the extension. We only need the &lt;code&gt;storage&lt;/code&gt; permission here to access the &lt;a href="https://developer.chrome.com/extensions/storage" rel="noopener noreferrer"&gt;Chrome storage API&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;host_permissions&lt;/code&gt;: If your extension interacts with a web page or external API, you must list the specific URLs here.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;background&lt;/code&gt;: Registers the extension's service worker. The &lt;code&gt;type: module&lt;/code&gt; line is necessary for the browser to recognize it as an ES module script.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this manifest in place, Chrome now knows how to load and run your extension. Next, you'll prompt the user for an Unsplash access key so the extension can fetch images.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prompting users for their Unsplash access key
&lt;/h2&gt;

&lt;p&gt;The core functionality for this extension is fetching and displaying images from Unsplash in each new tab. The following &lt;a href="https://unsplash.com/documentation#get-a-random-photo" rel="noopener noreferrer"&gt;endpoint&lt;/a&gt; is provided for this purpose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.unsplash.com/photos/random
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It accepts a number of &lt;a href="https://unsplash.com/documentation#parameters-10" rel="noopener noreferrer"&gt;query parameters&lt;/a&gt; to narrow the pool of photos for selection. For our project, we'll use the &lt;code&gt;orientation&lt;/code&gt; parameter to limit the results to landscape images alone:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.unsplash.com/photos/random?orientation=landscape
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It also requires &lt;a href="https://unsplash.com/documentation#public-authentication" rel="noopener noreferrer"&gt;authentication&lt;/a&gt; via the HTTP &lt;code&gt;Authorization&lt;/code&gt; header. This is done by setting the &lt;code&gt;Authorization&lt;/code&gt; header to &lt;code&gt;Client-ID &amp;lt;ACCESS_KEY&amp;gt;&lt;/code&gt; where &lt;code&gt;&amp;lt;ACCESS_KEY&amp;gt;&lt;/code&gt; is a valid key from Unsplash.&lt;/p&gt;

&lt;p&gt;Follow the instructions on &lt;a href="https://unsplash.com/documentation#creating-a-developer-account" rel="noopener noreferrer"&gt;this page&lt;/a&gt; to create a free Unsplash account, and &lt;a href="https://unsplash.com/documentation#registering-your-application" rel="noopener noreferrer"&gt;register a new application&lt;/a&gt;. Once your app is created, copy the Unsplash access key string in the application settings page:&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%2Fbc61i072xtwvhwj89d0r.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%2Fbc61i072xtwvhwj89d0r.png" alt="Retrieving Unsplash API Key" width="800" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This brings us to a crucial part of building browser extensions responsibly: &lt;strong&gt;you must not hardcode your own developer access key into the extension's code&lt;/strong&gt;. This is insecure (anyone could steal it) and not scalable, as all users would share a single API quota.&lt;/p&gt;

&lt;p&gt;The correct, professional approach is to have the user provide their own API key. To make this seamless, you can open the options page automatically when the extension is first installed. This gives users a clear prompt to enter their access key before the extension tries to load any images.&lt;/p&gt;

&lt;p&gt;Add the following to your &lt;code&gt;js/background.js&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onInstalled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;details&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;details&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reason&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;install&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;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;openOptionsPage&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;This listener runs only when the extension is installed for the first time. Chrome will open the options page in a new tab, letting you add your Unsplash access key right away.&lt;/p&gt;

&lt;p&gt;Next, wire up the logic that loads those saved values and stores new ones. Open &lt;code&gt;js/options.js&lt;/code&gt; and add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;saveOptions&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;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unsplashKey&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;collections&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;collections&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;unsplashAccessKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;collections&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;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Options saved.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;setTimeout&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;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&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="mi"&gt;1500&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;restoreOptions&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;result&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;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;local&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="s1"&gt;unsplashAccessKey&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;collections&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unsplashKey&lt;/span&gt;&lt;span class="dl"&gt;'&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="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unsplashAccessKey&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;collections&lt;/span&gt;&lt;span class="dl"&gt;'&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="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;collections&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DOMContentLoaded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;restoreOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;save&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;saveOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script does two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Loads saved settings&lt;/strong&gt; whenever the options page opens.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Saves new settings&lt;/strong&gt; to &lt;code&gt;chrome.storage.local&lt;/code&gt; when users click the &lt;strong&gt;Save Settings&lt;/strong&gt; button.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We're using &lt;code&gt;chrome.storage.local&lt;/code&gt; to save the API key and collection IDs to the local machine only. If you need to sync certain extension settings across all Chrome browsers where the user is logged in, you may use the &lt;a href="https://developer.chrome.com/docs/extensions/reference/api/storage#property-sync" rel="noopener noreferrer"&gt;chrome.storage.sync&lt;/a&gt; API instead.&lt;/p&gt;

&lt;p&gt;The trade-off is a much smaller storage quota (around 100 KB), but that's usually enough for most settings objects.&lt;/p&gt;

&lt;p&gt;Once these pieces are in place, load your extension in Chrome by typing &lt;code&gt;chrome://extensions&lt;/code&gt; in the address bar and select &lt;strong&gt;Load unpacked&lt;/strong&gt; after enabling &lt;strong&gt;Developer mode&lt;/strong&gt;:&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%2F3ign872zhl8sevqhziu4.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%2F3ign872zhl8sevqhziu4.png" alt="Load unpacked extension in Chrome" width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Navigate to the root of your project directory (where the &lt;code&gt;manifest.json&lt;/code&gt; file lives) and select it. When the extension installs, the options page will open automatically and you can enter your Unsplash key.&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%2F50gygkcps5klagad8yqq.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%2F50gygkcps5klagad8yqq.png" alt="Freshtab options page" width="800" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can confirm this by opening the Chrome DevTools with &lt;code&gt;F12&lt;/code&gt;, then go to the &lt;strong&gt;Application&lt;/strong&gt; tab and find the &lt;strong&gt;Extension storage &amp;gt; Local&lt;/strong&gt; entry. You should see the empty &lt;code&gt;collections&lt;/code&gt; field and the &lt;code&gt;unsplashAccessKey&lt;/code&gt; key that you just added:&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%2Fxawlsw4zhgblgke3h31v.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%2Fxawlsw4zhgblgke3h31v.png" alt="Viewing the extension local storage in Chrome" width="800" height="479"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the access key safely stored, the extension is ready to start fetching images in the background.&lt;/p&gt;

&lt;p&gt;When you open a new browser tab, it should already be replaced by the one defined in your extension manifest (&lt;code&gt;index.html&lt;/code&gt;). This page is currently blank as shown below:&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%2F9f0h5s7n9airbe447oxp.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%2F9f0h5s7n9airbe447oxp.png" alt="Empty new tab page" width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You may need to turn off the &lt;strong&gt;Footer&lt;/strong&gt; in recent versions of Chrome. Click &lt;strong&gt;Customize Chrome&lt;/strong&gt; or go to &lt;strong&gt;More tools &amp;gt; Customize Chrome&lt;/strong&gt; in the browser menu to fond the relevant option:&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%2Fsico0rcf23kv480f4u5u.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%2Fsico0rcf23kv480f4u5u.png" alt="Turning off the footer in Chrome" width="800" height="538"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Fetching images from Unsplash
&lt;/h2&gt;

&lt;p&gt;With the settings page wired up and your new tab page ready to render content, the next step is to actually load an image. That work lives in the service worker, which will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read the saved API key and optional collection IDs from &lt;code&gt;chrome.storage&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Call the Unsplash API to fetch a random photo.&lt;/li&gt;
&lt;li&gt;Store the image response in the browser cache using the\
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Cache" rel="noopener noreferrer"&gt;Cache API&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Update your &lt;code&gt;js/background.js&lt;/code&gt; file to look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// js/background.js&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;CACHE_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;IMAGE_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;METADATA_KEY&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;./constants.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onInstalled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;details&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;details&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reason&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;install&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;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;openOptionsPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Always fetch a new image on install or update&lt;/span&gt;
  &lt;span class="nf"&gt;fetchAndCacheImage&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;fetchAndCacheImage&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;unsplashAccessKey&lt;/span&gt; &lt;span class="p"&gt;}&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;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;local&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="s1"&gt;unsplashAccessKey&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;unsplashAccessKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;openOptionsPage&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;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metaResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchPhotoMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;unsplashAccessKey&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;imageResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metaResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;amp;q=85&amp;amp;w=2000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;imageResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&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="s1"&gt;Failed to fetch the image file.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cache&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;caches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CACHE_NAME&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;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;IMAGE_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;imageResponse&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;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;METADATA_KEY&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;metaResponse&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error fetching and caching image:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="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;fetchPhotoMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apiKey&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;collections&lt;/span&gt; &lt;span class="p"&gt;}&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;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;local&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="s1"&gt;collections&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;endpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.unsplash.com/photos/random?orientation=landscape&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;collections&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;endpoint&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;`&amp;amp;collections=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;collections&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="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`Client-ID &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;apiKey&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;headers&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&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="s2"&gt;`Unsplash API error: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusText&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="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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;The script uses the &lt;code&gt;chrome.runtime.onInstalled&lt;/code&gt; listener to detect when a user first installs or updates the extension. On a fresh install it opens the options page, and in all cases it calls &lt;code&gt;fetchAndCacheImage()&lt;/code&gt; so there is at least one image ready.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;fetchAndCacheImage()&lt;/code&gt; function checks &lt;code&gt;chrome.storage.local&lt;/code&gt; for the user's saved &lt;code&gt;unsplashAccessKey&lt;/code&gt;. If the key is missing, it simply re-opens the options page and stops.&lt;/p&gt;

&lt;p&gt;If the key is present, it calls the &lt;code&gt;fetchPhotoMetadata()&lt;/code&gt; helper to get the photo's data (like its download URL and EXIF info). This helper builds the correct Unsplash API URL, attaches the API key to the &lt;code&gt;Authorization&lt;/code&gt; header, and adds any custom collection IDs (if configured).&lt;/p&gt;

&lt;p&gt;Once it has the metadata, &lt;code&gt;fetchAndCacheImage()&lt;/code&gt; makes a second &lt;code&gt;fetch&lt;/code&gt; call to get the actual image file and requests a resized version for better performance. It then opens the cache and uses &lt;code&gt;cache.put&lt;/code&gt; to store the image data.&lt;/p&gt;

&lt;p&gt;Finally, it saves the metadata object into &lt;code&gt;chrome.storage.local&lt;/code&gt;, where all extension scripts can easily access it.&lt;/p&gt;

&lt;p&gt;The constants referenced above are defined in &lt;code&gt;js/constants.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// js/constants.js&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;CACHE_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;freshtab-image-cache-v1&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;IMAGE_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://freshtab-app.local/next-image&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;METADATA_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next-image&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;
  
  
  Why we're using the Cache API
&lt;/h3&gt;

&lt;p&gt;This extension needs to store two different kinds of data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A small JSON object with metadata.&lt;/li&gt;
&lt;li&gt;A binary image file for the new tab background.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;chrome.storage.local&lt;/code&gt; is designed for JSON serializable values like strings, numbers, or objects. It cannot store large binary data like an image &lt;code&gt;Response&lt;/code&gt; or a &lt;code&gt;Blob&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That's why the Cache API is used for the image instead. However, note that the &lt;code&gt;IMAGE_KEY&lt;/code&gt; does not need to point to a real website. It only needs to be a valid &lt;code&gt;https://&lt;/code&gt; URL so the cache accepts it (otherwise you'll get an error).&lt;/p&gt;

&lt;p&gt;If you want to store the image binary in &lt;code&gt;chrome.storage.local&lt;/code&gt; instead, you would need to convert it to a Base64 string and add the &lt;code&gt;unlimitedStorage&lt;/code&gt; permission, since high resolution images can easily exceed the default 10 MB quota.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing it out
&lt;/h3&gt;

&lt;p&gt;You can now reload the extension and confirm everything is working. Find the &lt;code&gt;freshtab&lt;/code&gt; entry in &lt;code&gt;chrome://extensions&lt;/code&gt; and click the reload button. After a few moments, open a new tab and launch the &lt;strong&gt;Chrome DevTools&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In the DevTools &lt;strong&gt;Application&lt;/strong&gt; panel under &lt;strong&gt;Extension storage → Local&lt;/strong&gt;, you should see a &lt;code&gt;next-image&lt;/code&gt; entry containing the metadata for the next tab's background image:&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%2Fofbbft5dpuzwju4up1jj.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%2Fofbbft5dpuzwju4up1jj.png" alt="Viewing image metadata in Chrome Local storage" width="800" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Under &lt;strong&gt;Cache storage&lt;/strong&gt;, open &lt;code&gt;freshtab-image-cache-v1&lt;/code&gt; to see the saved image response stored under the &lt;code&gt;IMAGE_KEY&lt;/code&gt; URL:&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%2Flcgjv7vh4d6r2gul737v.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%2Flcgjv7vh4d6r2gul737v.png" alt="Viewing the image in the browser cache" width="800" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the image successfully cached, you are ready to display it on the new tab page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Loading the Unsplash image on every new tab
&lt;/h2&gt;

&lt;p&gt;Now that the Unsplash metadata and image are being saved to the extension's local storage and cache, the new tab page can read them and display the photo.&lt;/p&gt;

&lt;p&gt;The script responsible for this is &lt;code&gt;js/index.js&lt;/code&gt; which runs every time a new tab opens. Its job is to retrieve the cached image, show it as quickly as possible, and notify the service worker to prepare the next image.&lt;/p&gt;

&lt;p&gt;Open your &lt;code&gt;js/index.js&lt;/code&gt; file and add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// js/index.js&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;CACHE_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;IMAGE_KEY&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;./constants.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DOMContentLoaded&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="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;loadPhoto&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next-image&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;loadPhoto&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;cache&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;caches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CACHE_NAME&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;cachedResponse&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;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;IMAGE_KEY&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;cachedResponse&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;blob&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;cachedResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;backgroundImage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`url(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createObjectURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blob&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="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;backgroundColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#111&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the page's HTML has loaded, the &lt;code&gt;main()&lt;/code&gt; function is triggered. This function does two key things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; It immediately calls &lt;code&gt;loadPhoto()&lt;/code&gt; to set the background image.&lt;/li&gt;
&lt;li&gt; It then sends a &lt;code&gt;next-image&lt;/code&gt; message to the service worker, telling it to fetch a new photo in the background so it is ready for the next tab.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;code&gt;loadPhoto()&lt;/code&gt; function opens the browser cache and looks for an entry that matches &lt;code&gt;IMAGE_KEY&lt;/code&gt;. If it finds one, it converts the cached response into a blob URL and applies it as the page background.&lt;/p&gt;

&lt;p&gt;This way, a network request is avoided, making the image appear almost instantly. If nothing is cached yet (for example, on the first run), it falls back to the previous dark background color.&lt;/p&gt;

&lt;p&gt;Before testing this, update your service worker so it listens for the &lt;code&gt;next-image&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// js/background.js&lt;/span&gt;
&lt;span class="c1"&gt;// [...]&lt;/span&gt;

&lt;span class="c1"&gt;// Listen for the "next-image" command from the new tab page&lt;/span&gt;
&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;request&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next-image&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="nf"&gt;fetchAndCacheImage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now reload the extension from the &lt;code&gt;chrome://extensions&lt;/code&gt; page. Open a new tab and you should be greeted with an image from Unsplash:&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%2Ftifpqrsu7r26g7xhdy0e.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftifpqrsu7r26g7xhdy0e.jpg" alt="Chrome's New Tab Page showing Unsplash image" width="800" height="518"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you continue opening new tabs, you should see a different image each time, thanks to the &lt;code&gt;next-image&lt;/code&gt; message the page sends and the service worker handles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a simple settings popup
&lt;/h2&gt;

&lt;p&gt;To make the extension's settings easier to reach, let's add a small popup that opens whenever the user clicks the extension's toolbar icon.&lt;/p&gt;

&lt;p&gt;The popup won't do much, but it gives users an obvious way to access the options page without digging through menus.&lt;/p&gt;

&lt;p&gt;Here is the &lt;code&gt;popup.html&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Freshtab&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt;
      &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt;
      &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/bulma@1.0.0/css/bulma.min.css"&lt;/span&gt;
    &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.25rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h3&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"title is-5 has-text-centered"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Freshtab&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"button is-primary is-fullwidth"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"options-btn"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      Go to Settings
    &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"js/popup.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The popup's JavaScript is simple. It listens for a click on the button and opens the extension's main options page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// js/popup.js&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DOMContentLoaded&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;optionsButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;options-btn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;optionsButton&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;optionsButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&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="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;openOptionsPage&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;When you click the extension icon in Chrome's toolbar, you'll see a compact window with a single button:&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%2Fpahe10r4or1565g8k6nx.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpahe10r4or1565g8k6nx.jpg" alt="Freshtab extension popup" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While users can also right-click the icon and choose &lt;strong&gt;Options&lt;/strong&gt;, many people don't know that menu exists. A dedicated popup makes the settings far more discoverable and feels more polished.&lt;/p&gt;

&lt;p&gt;If you like, you can take this further by adding a settings button directly on the new tab page as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Restricting images to user-defined collections
&lt;/h2&gt;

&lt;p&gt;By default, the extension selects a random landscape photo from the entire Unsplash library. To make the experience more personal, you can let users provide one or more Unsplash Collection IDs. When set, the extension will pull images only from those specific collections.&lt;/p&gt;

&lt;p&gt;This feature is already built into both the options page and the &lt;code&gt;fetchPhotoMetadata()&lt;/code&gt; function in your &lt;code&gt;background.js&lt;/code&gt; script, so no additional code is required.&lt;/p&gt;

&lt;p&gt;To try it out, visit the &lt;a href="https://unsplash.com/collections" rel="noopener noreferrer"&gt;Unsplash collections page&lt;/a&gt; and open any collection. The collection ID appears in the page URL, as shown below:&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%2F6nmazp30wlkul0ubdzys.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%2F6nmazp30wlkul0ubdzys.png" alt="Unsplash collection ID selection" width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Copy that ID, paste it into the &lt;strong&gt;Custom Collection IDs&lt;/strong&gt; field on the options page, and click &lt;strong&gt;Save Settings&lt;/strong&gt;:&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%2F7c0vbj61g4wpoy6xzny8.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%2F7c0vbj61g4wpoy6xzny8.png" alt="Saving collection IDs in Freshtab" width="800" height="355"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From now on, whenever the extension fetches an image, it will restrict the request to the specified collection. The result is a curated, more consistent set of images:&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%2Foqi3l5no8omfm0pawv5b.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foqi3l5no8omfm0pawv5b.jpg" alt="Freshtab showing winter images" width="800" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding motivational quotes with Chrome's Built-in AI
&lt;/h2&gt;

&lt;p&gt;One of the most interesting changes in recent versions of Chrome is the addition of a built in language model that runs directly on the user's device. Through the &lt;code&gt;LanguageModel&lt;/code&gt; API, your extension can generate text without sending data to a remote server.&lt;/p&gt;

&lt;p&gt;But there are a few caveats.&lt;/p&gt;

&lt;p&gt;First, this is not a universal feature; it comes with &lt;a href="https://developer.chrome.com/docs/ai/get-started#requirements" rel="noopener noreferrer"&gt;strict hardware and software requirements&lt;/a&gt;. The on-device AI model, Gemini Nano, requires a desktop machine with at least 16 GB of RAM if running on the CPU, or a GPU with more than 4 GB of VRAM.&lt;/p&gt;

&lt;p&gt;It's also not available out of the box. The user must have an unmetered internet connection and at least 22 GB of free disk space for the initial model download.&lt;/p&gt;

&lt;p&gt;And you can't just start this download automatically even if the baseline requirements are met; you have to wait for a user activation (like a button click) before you can trigger the download by creating a session.&lt;/p&gt;

&lt;p&gt;Because of all this, &lt;strong&gt;you can't assume the feature will work for everyone&lt;/strong&gt;, so it's essential to build in a graceful fallback. For this tutorial, this means checking if the model is available and simply not showing the quote if it isn't.&lt;/p&gt;

&lt;p&gt;Let's begin by exposing a setting that lets users turn this feature on or off. In your &lt;code&gt;options.html&lt;/code&gt; file, find and uncomment the following block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- options.html --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"field"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;AI-powered motivational message&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"control"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"quote-label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"showQuote"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      Show a motivational quote on the new tab page from Chrome's built-in AI
      model (Gemini Nano).
    &lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"help"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"quote-help"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    When enabled, a new quote will be fetched for each tab.
  &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since this feature depends on a browser API that may not be available everywhere, the options page needs to detect support first and update the UI accordingly.&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;js/options.js&lt;/code&gt; and replace its contents as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// js/options.js&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;saveOptions&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;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unsplashKey&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;collections&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;collections&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;showQuote&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;showQuote&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;checked&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;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;unsplashAccessKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;collections&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;showQuote&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;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Options saved.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;setTimeout&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;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&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="mi"&gt;1500&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;restoreOptions&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;result&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;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;local&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="s1"&gt;unsplashAccessKey&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;collections&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;showQuote&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unsplashKey&lt;/span&gt;&lt;span class="dl"&gt;'&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="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unsplashAccessKey&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;collections&lt;/span&gt;&lt;span class="dl"&gt;'&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="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;collections&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;showQuote&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;checked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;showQuote&lt;/span&gt; &lt;span class="o"&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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;checkAIAvailability&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;quoteCheckbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;showQuote&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;quoteLabel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;quote-label&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;quoteHelp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;quote-help&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;markUnavailable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;quoteCheckbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;quoteLabel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;is-disabled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;quoteHelp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;This feature is not available in your browser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;quoteHelp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;is-danger&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;if &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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;LanguageModel&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;markUnavailable&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;availability&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;LanguageModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;availability&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;availability&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;available&lt;/span&gt;&lt;span class="dl"&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;availability&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unavailable&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;return&lt;/span&gt; &lt;span class="nf"&gt;markUnavailable&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DOMContentLoaded&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;restoreOptions&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;checkAIAvailability&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;save&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;saveOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what this code does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;restoreOptions()&lt;/code&gt; loads any saved settings, including &lt;code&gt;showQuote&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;saveOptions()&lt;/code&gt; persists the Unsplash key, collections, and the AI quote toggle.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;checkAIAvailability()&lt;/code&gt; calls &lt;code&gt;LanguageModel.availability()&lt;/code&gt; and, if the feature is missing, disables the checkbox and shows a helpful message.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the API is available, the checkbox remains enabled and users can turn the feature on as normal.&lt;/p&gt;

&lt;p&gt;You also need to handle the case where the model is downloadable but not yet installed. In that case, you need to trigger the download (which happens in the background) and surface progress so that users know what's happening.&lt;/p&gt;

&lt;p&gt;To implement this, extend &lt;code&gt;js/options.js&lt;/code&gt; with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;saveOptions&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;showQuote&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;createModelSession&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;createModelSession&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;progress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;model-progress&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;quoteHelp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;quote-help&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;quoteHelp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Initializing download...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;is-hidden&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;availability&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;LanguageModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;availability&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;availability&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;available&lt;/span&gt;&lt;span class="dl"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&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;LanguageModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="nf"&gt;monitor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;downloadprogress&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;e&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;quoteHelp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`Downloading AI model... (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loaded&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&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="nx"&gt;progress&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="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loaded&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;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loaded&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;quoteHelp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Download complete, model installed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy&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="nx"&gt;quoteHelp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&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="nx"&gt;quoteHelp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;is-danger&lt;/span&gt;&lt;span class="dl"&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;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;is-hidden&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;progress&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="mi"&gt;0&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;When the user enables the AI feature and clicks &lt;strong&gt;Save Settings&lt;/strong&gt;, &lt;code&gt;createModelSession()&lt;/code&gt; is executed. It:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Displays a progress bar.&lt;/li&gt;
&lt;li&gt;Calls &lt;code&gt;LanguageModel.create()&lt;/code&gt; with a monitor to track download progress.&lt;/li&gt;
&lt;li&gt;Updates the help text as the model downloads and installs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once the download and installation is completed, the model will become accessible for prompting.&lt;/p&gt;

&lt;p&gt;Next, wire the &lt;code&gt;LanguageModel&lt;/code&gt; API into the new tab script itself. Open &lt;code&gt;js/index.js&lt;/code&gt; and update it as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// js/index.js&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;CACHE_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;IMAGE_KEY&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;./constants.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DOMContentLoaded&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="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nf"&gt;displayQuote&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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;runPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prompt&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="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;session&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;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;session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;session&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;LanguageModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&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="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prompt&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;e&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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Prompt failed&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;e&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="s1"&gt;Prompt:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy&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;displayQuote&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;showQuote&lt;/span&gt; &lt;span class="p"&gt;}&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;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;local&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="s1"&gt;showQuote&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;showQuote&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;LanguageModel&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;self&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Write a one-sentence motivational message about success, perseverance or discipline&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;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;expectedInputs&lt;/span&gt;&lt;span class="p"&gt;:&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;languages&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;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="na"&gt;expectedOutputs&lt;/span&gt;&lt;span class="p"&gt;:&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;languages&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;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;topK&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="p"&gt;};&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;availability&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;LanguageModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;availability&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;availability&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;available&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;return&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;quoteText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;quote-text&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;quoteAuthor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;quote-author&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;quoteText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Loading quote...&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;runPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prompt&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;quoteText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;quoteAuthor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;is-hidden&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;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;quoteText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script is what brings the new AI quote feature to life on your new tab page.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;displayQuote()&lt;/code&gt; function checks &lt;code&gt;chrome.storage.local&lt;/code&gt; to see if the feature is enabled. If so, it checks that the &lt;code&gt;LanguageModel&lt;/code&gt; API exists and that the model is available with the requested parameters. It then sets a simple prompt asking for a short motivational sentence.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;expectedInputs&lt;/code&gt; and &lt;code&gt;expectedOutputs&lt;/code&gt; fields specify that you want plain text in English, while &lt;code&gt;temperature&lt;/code&gt; and &lt;code&gt;topK&lt;/code&gt; are set to high values to encourage more varied output. For more ideas and tuning tips, check out the &lt;a href="https://developer.chrome.com/docs/ai/prompt-api" rel="noopener noreferrer"&gt;Prompt API documentation&lt;/a&gt; and &lt;a href="https://ai.google.dev/gemini-api/docs/prompting-strategies" rel="noopener noreferrer"&gt;prompting strategies guide&lt;/a&gt;&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%2F1a4dgsl8948dh3jlwrr6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1a4dgsl8948dh3jlwrr6.jpg" alt="Loading page in new tab extension" width="800" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While the prompt is running, the UI shows a &lt;code&gt;Loading quote...&lt;/code&gt; message so the user knows something is happening. When a response arrives, the quote text replaces that message and the quote author element is revealed.&lt;/p&gt;

&lt;p&gt;After refreshing the new tab page, you should start seeing AI generated motivational messages from Gemini Nano running locally in your browser!&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%2Fizupct7loqcuejn2efs8.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fizupct7loqcuejn2efs8.jpg" alt="Motivational messages from Gemini Nano in a Chrome Extension" width="800" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can experiment with different prompts and parameters to change the tone, length, or style of the messages.&lt;/p&gt;

&lt;p&gt;Just keep in mind that on-device AI models are not designed to handle the same level of complexity as the massive server-side models running in the cloud.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some tips for debugging Chrome extensions
&lt;/h2&gt;

&lt;p&gt;Extensions are made up of several moving parts: a service worker, popup, options page, and any overridden pages. When something breaks you need to know which piece to inspect.&lt;/p&gt;

&lt;p&gt;Your main tool is the &lt;code&gt;chrome://extensions&lt;/code&gt; page. Just make sure &lt;strong&gt;Developer mode&lt;/strong&gt; is toggled on in the top-right corner.&lt;/p&gt;

&lt;p&gt;To debug your service worker (&lt;code&gt;background.js&lt;/code&gt;), find your extension card on the extensions page and click the &lt;strong&gt;service worker&lt;/strong&gt; link:&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%2F4fvad8a8dr163uikot2b.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%2F4fvad8a8dr163uikot2b.png" alt="Inspecting service worker in Chrome extensions UI" width="800" height="505"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Chrome opens a dedicated DevTools window where you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;View &lt;code&gt;console.log&lt;/code&gt; output from the service worker.&lt;/li&gt;
&lt;li&gt;Step through code in the Sources panel.&lt;/li&gt;
&lt;li&gt;Inspect network calls, including Unsplash API requests.&lt;/li&gt;
&lt;li&gt;Inspect extension storage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your Chrome extension encounters a serious problem, you may also see an &lt;strong&gt;Errors&lt;/strong&gt; button on the extension card:&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%2F8twnc7d2bdheuozqt9ie.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%2F8twnc7d2bdheuozqt9ie.png" alt="Errors button in Chrome extension card" width="800" height="505"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click it to open a panel that shows recent errors and stack traces:&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%2Fvhsu842pmbtplffdo1lr.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%2Fvhsu842pmbtplffdo1lr.png" alt="Viewing error details in Chrome extension" width="800" height="506"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After fixing the underlying issue, you can clear the errors and reload the extension.&lt;/p&gt;

&lt;p&gt;To debug the popup, right click the extension icon in the toolbar and choose &lt;strong&gt;Inspect popup&lt;/strong&gt;. This opens a DevTools window attached to the popup and keeps the popup from closing while you inspect it:&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%2Fbrfgvadahd7tn4nlz957.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbrfgvadahd7tn4nlz957.jpg" alt="Debugging chrome extension popup windows" width="800" height="505"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, for new tab pages (or other browser overrides) you can debug them like a regular web page by right-click anywhere on the page and selecting &lt;strong&gt;Inspect&lt;/strong&gt; or using the &lt;code&gt;F12&lt;/code&gt; shortcut key.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources and next steps
&lt;/h2&gt;

&lt;p&gt;You've now built a full Chrome extension from scratch and gained a solid understanding of how extension parts fit together, how Manifest V3 works, and how to interact with the browser through its extension APIs.&lt;/p&gt;

&lt;p&gt;You can see the full code in the &lt;a href="https://github.com/Freshman-tech/freshtab-starter-files/tree/final" rel="noopener noreferrer"&gt;final branch on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you take on more advanced ideas, here are some helpful directions to explore:&lt;/p&gt;

&lt;h3&gt;
  
  
  Using an extensions framework
&lt;/h3&gt;

&lt;p&gt;For larger or more complex projects, writing everything by hand can get cumbersome. Frameworks like &lt;strong&gt;&lt;a href="https://wxt.dev/" rel="noopener noreferrer"&gt;Wxt&lt;/a&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;a href="https://github.com/PlasmoHQ/plasmo" rel="noopener noreferrer"&gt;Plasmo&lt;/a&gt;&lt;/strong&gt; handle the boring stuff for you such as automatic reloading, streamlining builds, and making it easier to integrate modern JavaScript tooling.&lt;/p&gt;

&lt;p&gt;The official &lt;a href="https://github.com/GoogleChrome/chrome-extensions-samples" rel="noopener noreferrer"&gt;Chrome Extensions Samples&lt;/a&gt; repository is also a great place to see real, working examples of every major API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Going deeper with Chrome's AI features
&lt;/h3&gt;

&lt;p&gt;If you want to continue experimenting with the LanguageModel API, &lt;a href="https://developer.chrome.com/docs/ai" rel="noopener noreferrer"&gt;Chrome's documentation&lt;/a&gt; offers practical examples and prompt patterns.&lt;/p&gt;

&lt;p&gt;With these tools and references, you can expand this project into something more interesting or use it as a launching point for entirely new ideas.&lt;/p&gt;

&lt;p&gt;Once you're happy with your extension, you can publish it to the Chrome Web Store so others can install it. Google provides a &lt;a href="https://developer.chrome.com/webstore/publish" rel="noopener noreferrer"&gt;step-by-step guide&lt;/a&gt; for the full submission process.&lt;/p&gt;

&lt;p&gt;Happy building!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>A Complete Guide to Node.js Process Management with PM2</title>
      <dc:creator>Ayooluwa Isaiah</dc:creator>
      <pubDate>Wed, 23 Mar 2022 12:31:02 +0000</pubDate>
      <link>https://dev.to/appsignal/a-complete-guide-to-nodejs-process-management-with-pm2-100f</link>
      <guid>https://dev.to/appsignal/a-complete-guide-to-nodejs-process-management-with-pm2-100f</guid>
      <description>&lt;p&gt;Process management refers to various activities around the creation, termination, and monitoring of processes. A process manager is a program that ensures that your applications always stay online after being launched.&lt;/p&gt;

&lt;p&gt;Process managers can prevent downtime in production by automatically restarting your application after a crash or even after the host machine reboots. They are also useful in development: they auto-restart an app once its source files or dependencies are updated. Process managers also typically provide monitoring tools that access application logs and other key metrics, such as CPU and memory usage.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Unitech/pm2"&gt;PM2&lt;/a&gt; is a Node.js process manager that comes with a built-in load balancer. It helps facilitate production deployments and enables you to keep running applications alive indefinitely (even when accidents occur). It also lets you gain insights into your application's runtime performance and resource consumption and scale your application in real-time through its clustering feature.&lt;/p&gt;

&lt;p&gt;In this article, we'll examine PM2's most important features and discover how it can help you increase the resilience of your Node.js applications in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started with PM2
&lt;/h2&gt;

&lt;p&gt;PM2 is available as an &lt;a href="https://www.npmjs.com/package/pm2"&gt;NPM package&lt;/a&gt;, so you can install it through &lt;code&gt;npm&lt;/code&gt; or &lt;code&gt;yarn&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;pm2
&lt;span class="c"&gt;# or&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;yarn add pm2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After installing PM2, run &lt;code&gt;npx pm2 --version&lt;/code&gt; to see the installed version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npx pm2 &lt;span class="nt"&gt;--version&lt;/span&gt;
5.1.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you don't want to prefix the &lt;code&gt;pm2&lt;/code&gt; command with &lt;code&gt;npm&lt;/code&gt; every time, you can install it globally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; pm2
&lt;span class="c"&gt;# or&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;yarn global add pm2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aside from the main &lt;code&gt;pm2&lt;/code&gt; command, the installation provides some other executables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;pm2-dev&lt;/code&gt;: a development tool for restarting your application when file changes in the directory are detected (similar to &lt;a href="https://github.com/remy/nodemon"&gt;Nodemon&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pm2-runtime&lt;/code&gt;: designed to be a drop-in replacement for the &lt;code&gt;node&lt;/code&gt; binary in Docker containers. It helps keep the running application in the foreground (unlike &lt;code&gt;pm2&lt;/code&gt;, which sends it to the background) so that the container keeps running.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pm2-docker&lt;/code&gt;: an alias for &lt;code&gt;pm2-runtime&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Start Your Node.js App in Development Mode with PM2
&lt;/h2&gt;

&lt;p&gt;It can be quite tedious to restart your application server in development every time you change the source files. Using the &lt;code&gt;pm2-dev&lt;/code&gt; binary to start your application can take care of that concern automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pm2-dev start app.js
&lt;span class="o"&gt;===============================================================================&lt;/span&gt;
&lt;span class="nt"&gt;---&lt;/span&gt; PM2 development mode &lt;span class="nt"&gt;------------------------------------------------------&lt;/span&gt;
Apps started         : app
Processes started    : 1
Watch and Restart    : Enabled
Ignored folder       : node_modules
&lt;span class="o"&gt;===============================================================================&lt;/span&gt;
app-0  | &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"level"&lt;/span&gt;:30,&lt;span class="s2"&gt;"time"&lt;/span&gt;:1638512528047,&lt;span class="s2"&gt;"pid"&lt;/span&gt;:4575,&lt;span class="s2"&gt;"hostname"&lt;/span&gt;:&lt;span class="s2"&gt;"Kreig"&lt;/span&gt;,&lt;span class="s2"&gt;"msg"&lt;/span&gt;:&lt;span class="s2"&gt;"Server listening at http://127.0.0.1:3000"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;rundev] App app restarted
app-0  | &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"level"&lt;/span&gt;:30,&lt;span class="s2"&gt;"time"&lt;/span&gt;:1638512535737,&lt;span class="s2"&gt;"pid"&lt;/span&gt;:4631,&lt;span class="s2"&gt;"hostname"&lt;/span&gt;:&lt;span class="s2"&gt;"Kreig"&lt;/span&gt;,&lt;span class="s2"&gt;"msg"&lt;/span&gt;:&lt;span class="s2"&gt;"Server listening at http://127.0.0.1:3000"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, your server will auto-restart each time you create, modify or delete a source file in your project. It also works when you add or remove a dependency with &lt;code&gt;npm&lt;/code&gt; or &lt;code&gt;yarn&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start Your Node.js App in Production Mode with PM2
&lt;/h2&gt;

&lt;p&gt;When deploying an application to production, you can use the &lt;code&gt;pm2&lt;/code&gt; binary to start it in the background. It launches a daemon that monitors your application and keeps it running indefinitely.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pm2 start app.js
&lt;span class="o"&gt;[&lt;/span&gt;PM2] Starting /home/ayo/dev/demo/covid-node/app.js &lt;span class="k"&gt;in &lt;/span&gt;fork_mode &lt;span class="o"&gt;(&lt;/span&gt;1 instance&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;PM2] Done.
┌─────┬────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ &lt;span class="nb"&gt;id&lt;/span&gt;  │ name   │ namespace   │ version │ mode    │ pid      │ &lt;span class="nb"&gt;uptime&lt;/span&gt; │ ↺    │ status    │ cpu      │ mem      │ user     │ watching │
├─────┼────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0   │ app    │ default     │ 1.0.0   │ fork    │ 16573    │ 0s     │ 0    │ online    │ 0%       │ 19.1mb   │ ayo      │ disabled │
└─────┴────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PM2 defaults to the name of the entry file as the app's &lt;code&gt;name&lt;/code&gt;, but you can use a more recognizable name through the &lt;code&gt;--name&lt;/code&gt; option. This name is what you'll use to reference the application in many &lt;code&gt;pm2&lt;/code&gt; subcommands.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pm2 start app.js &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"my app"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Suppose you need to ensure that your application has established connections with other services (such as the database or cache) before being considered "online" by PM2. In that case, you can use the &lt;code&gt;--wait-ready&lt;/code&gt; option when starting your application. This causes PM2 to wait for 3 seconds (by default) or for a ready event (&lt;code&gt;process.send('ready')&lt;/code&gt;) before the application is considered ready. You can use the &lt;code&gt;--listen-timeout&lt;/code&gt; option to change the length of the delay.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pm2 start app.js &lt;span class="nt"&gt;--wait-ready&lt;/span&gt; &lt;span class="nt"&gt;--listen-timeout&lt;/span&gt; 5000 &lt;span class="c"&gt;# wait for 5 seconds&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Monitoring Your Running Applications in PM2
&lt;/h2&gt;

&lt;p&gt;To list your running applications, use the &lt;code&gt;pm2 list&lt;/code&gt; command. This prints a table describing the state of all running applications with columns for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the app name and id&lt;/li&gt;
&lt;li&gt;CPU and memory usage&lt;/li&gt;
&lt;li&gt;number of restarts (&lt;code&gt;↺&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;uptime&lt;/li&gt;
&lt;li&gt;process id&lt;/li&gt;
&lt;li&gt;the mode (&lt;code&gt;fork&lt;/code&gt; or &lt;code&gt;cluster&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and others.&lt;/p&gt;

&lt;p&gt;You can use this table alongside a &lt;a href="https://www.appsignal.com/tour/hosts"&gt;host monitoring service like AppSignal&lt;/a&gt; to give you a complete picture of your application and its host environment:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qtftl_mv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2022-03/latest-host-metrics.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qtftl_mv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2022-03/latest-host-metrics.png" alt="Host Monitoring in AppSignal" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pm2 list
┌─────┬───────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ &lt;span class="nb"&gt;id&lt;/span&gt;  │ name      │ namespace   │ version │ mode    │ pid      │ &lt;span class="nb"&gt;uptime&lt;/span&gt; │ ↺    │ status    │ cpu      │ mem      │ user     │ watching │
├─────┼───────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0   │ app       │ default     │ 1.0.0   │ fork    │ 16573    │ 9m     │ 0    │ online    │ 0%       │ 57.3mb   │ ayo      │ disabled │
│ 2   │ index     │ default     │ 1.0.0   │ fork    │ 0        │ 0      │ 16   │ errored   │ 0%       │ 0b       │ ayo      │ disabled │
│ 1   │ server    │ default     │ 0.1.0   │ fork    │ 17471    │ 71s    │ 0    │ online    │ 0%       │ 77.5mb   │ ayo      │ disabled │
└─────┴───────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see only a subset of this information, try enlarging your terminal window. The &lt;code&gt;list&lt;/code&gt; subcommand will not display all the columns if your terminal window is too small. You can also sort the output table according to a metric of your choice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pm2 list &lt;span class="nt"&gt;--sort&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;name|id|pid|memory|cpu|status|uptime][:asc|desc]
&lt;span class="c"&gt;# such as&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;pm2 list &lt;span class="nt"&gt;--sort&lt;/span&gt; &lt;span class="nb"&gt;uptime&lt;/span&gt;:desc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you require more information about a particular app beyond what &lt;code&gt;list&lt;/code&gt; provides, use the &lt;code&gt;show&lt;/code&gt; subcommand and pass the app name to view more detailed application process metadata. Some of the metrics and data presented in the output include the app's:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;output and error log files&lt;/li&gt;
&lt;li&gt;heap size and usage&lt;/li&gt;
&lt;li&gt;event loop latency&lt;/li&gt;
&lt;li&gt;uptime&lt;/li&gt;
&lt;li&gt;number of restarts&lt;/li&gt;
&lt;li&gt;source control metadata&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and more.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pm2 show server
Describing process with &lt;span class="nb"&gt;id &lt;/span&gt;1 - name server
┌───────────────────┬──────────────────────────────────────────────────┐
│ status            │ online                                           │
│ name              │ server                                           │
│ namespace         │ default                                          │
│ version           │ 0.1.0                                            │
│ restarts          │ 0                                                │
│ &lt;span class="nb"&gt;uptime&lt;/span&gt;            │ 60m                                              │
│ script path       │ /home/ayo/dev/demo/analytics-dashboard/server.js │
│ script args       │ N/A                                              │
│ error log path    │ /home/ayo/.pm2/logs/server-error.log             │
│ out log path      │ /home/ayo/.pm2/logs/server-out.log               │
│ pid path          │ /home/ayo/.pm2/pids/server-1.pid                 │
│ interpreter       │ node                                             │
│ interpreter args  │ N/A                                              │
│ script &lt;span class="nb"&gt;id&lt;/span&gt;         │ 1                                                │
│ &lt;span class="nb"&gt;exec &lt;/span&gt;cwd          │ /home/ayo/dev/demo/analytics-dashboard           │
│ &lt;span class="nb"&gt;exec &lt;/span&gt;mode         │ fork_mode                                        │
│ node.js version   │ 17.0.0                                           │
│ node &lt;span class="nb"&gt;env&lt;/span&gt;          │ N/A                                              │
│ watch &amp;amp; reload    │ ✘                                                │
│ unstable restarts │ 0                                                │
│ created at        │ 2021-12-03T08:33:01.489Z                         │
└───────────────────┴──────────────────────────────────────────────────┘

&lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another way to keep tabs on your running applications is through the built-in terminal dashboard (accessed through the &lt;code&gt;monit&lt;/code&gt; subcommand). This allows you to view live data on resource usage and logs for each of your applications.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pm2 monit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nFW3P5gH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2022-03/pm2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nFW3P5gH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2022-03/pm2.png" alt="Monitoring running apps with PM2" width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Restarting Your Node.js Application with PM2
&lt;/h2&gt;

&lt;p&gt;PM2 allows you to configure several different strategies for how your Node.js application should restart. By default, it restarts your application if it exits or crashes to minimize the impact to your customers in production while the source of the crash is investigated. The &lt;code&gt;restart&lt;/code&gt; subcommand is also available for manually restarting your application at any time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pm2 restart app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To ensure a graceful shutdown, make sure you intercept the &lt;code&gt;SIGINT&lt;/code&gt; signal to stop all new requests and finish up existing ones before allowing your program to exit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;process.on&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SIGINT'&lt;/span&gt;, &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
   gracefulShutdown&lt;span class="o"&gt;((&lt;/span&gt;err&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
     process.exit&lt;span class="o"&gt;(&lt;/span&gt;err ? 1 : 0&lt;span class="o"&gt;)&lt;/span&gt;
   &lt;span class="o"&gt;})&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use the &lt;code&gt;--kill-timeout&lt;/code&gt; option to ensure that a graceful shutdown does not take too long:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pm2 restart app &lt;span class="nt"&gt;--kill-timeout&lt;/span&gt; 5000 &lt;span class="c"&gt;# set a 5 second limit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Auto Restart Based on Memory Usage
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;--max-memory-restart&lt;/code&gt; option is available to restart an app when it reaches a certain memory threshold. This can help prevent a &lt;a href="https://stackoverflow.com/questions/38558989/node-js-heap-out-of-memory"&gt;Node.js heap out of memory error&lt;/a&gt;. You can specify the memory limit in kilobytes&lt;br&gt;
(&lt;code&gt;K&lt;/code&gt;), Megabytes (&lt;code&gt;M&lt;/code&gt;), or Gigabytes (&lt;code&gt;G&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pm2 start app.js &lt;span class="nt"&gt;--max-memory-restart&lt;/span&gt; 1G
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Auto Restart Based on Cron Schedule
&lt;/h3&gt;

&lt;p&gt;PM2 also offers a restart strategy based on the &lt;a href="https://www.netiq.com/documentation/cloud-manager-2-5/ncm-reference/data/bexyssf.html"&gt;Cron&lt;br&gt;
syntax&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This allows you to schedule a restart at a specific time each day / on certain days of the week / a set time interval (such as every 48 hours).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Restart at 12:00 pm every day&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;pm2 start app.js &lt;span class="nt"&gt;--cron-restart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"0 12 * * *"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Auto Restart on File Change
&lt;/h3&gt;

&lt;p&gt;Remember how &lt;code&gt;pm2-dev&lt;/code&gt; auto-restarts your application when you make changes to a file? You can configure the &lt;code&gt;pm2&lt;/code&gt; command to act in a similar manner through the &lt;code&gt;--watch&lt;/code&gt; subcommand. In the table outputted by &lt;code&gt;pm2 list&lt;/code&gt;, look at the &lt;code&gt;watching&lt;/code&gt; column to observe the &lt;code&gt;watch&lt;/code&gt; status of an application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pm2 start app.js &lt;span class="nt"&gt;--watch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Auto Restart after a Delay
&lt;/h3&gt;

&lt;p&gt;You can configure the &lt;code&gt;--restart-delay&lt;/code&gt; option to set a delay for automatic restarts. The delay should be supplied in milliseconds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pm2 start app.js &lt;span class="nt"&gt;--restart-delay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5000 &lt;span class="c"&gt;# 5s delay&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Ignore Some Exit Codes When Auto Restarting
&lt;/h3&gt;

&lt;p&gt;PM2 auto-restarts your app when the process exits, but it does not take the &lt;a href="https://shapeshed.com/unix-exit-codes/"&gt;exit code&lt;/a&gt; into account by default, so it restarts regardless of whether the app exits cleanly or crashes. If this behavior is not desired, you can use the &lt;code&gt;--stop-exit-codes&lt;/code&gt; option to set exit codes that should not prompt PM2 to auto-restart. For example, you can ensure PM2 does not auto-restart on a clean exit with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pm2 start app.js &lt;span class="nt"&gt;--stop-exit-codes&lt;/span&gt; 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Restarting Processes after a System Reboot
&lt;/h2&gt;

&lt;p&gt;The previous section covered a variety of ways to restart your application after it is launched. However, none of the strategies there will keep your application up if your server reboots. Notably, &lt;a href="https://pm2.keymetrics.io/docs/usage/startup/"&gt;PM2 ships with a startup feature&lt;/a&gt; that can help solve this problem. You can combine this with a good &lt;a href="https://www.appsignal.com/tour/uptime-monitoring"&gt;uptime monitoring service like AppSignal's&lt;/a&gt; to guarantee that your application comes back online quickly, even if an accident happens.&lt;/p&gt;

&lt;p&gt;You'll need to generate a startup script for your server's init system to execute on system boot and launch the PM2 process, which will subsequently start the configured application processes immediately. You can allow PM2 to autodetect your startup script or pass the init system used by your operating system, which could be &lt;code&gt;systemd&lt;/code&gt;, &lt;code&gt;upstart&lt;/code&gt;, &lt;code&gt;launchd&lt;/code&gt;, &lt;code&gt;rcd&lt;/code&gt;, or &lt;code&gt;systemv&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pm2 startup &lt;span class="c"&gt;# autodetect init system&lt;/span&gt;
&lt;span class="c"&gt;# or&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;pm2 startup systemd &lt;span class="c"&gt;# generate script for systemd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should receive the following output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;PM2] Init System found: systemd
&lt;span class="nt"&gt;-----------------------------------------------------------&lt;/span&gt;
 PM2 detected systemd but you precised systemd
 Please verify that your choice is indeed your init system
 If you arent sure, just run : pm2 startup
&lt;span class="nt"&gt;-----------------------------------------------------------&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;PM2] To setup the Startup Script, copy/paste the following &lt;span class="nb"&gt;command&lt;/span&gt;:
&lt;span class="nb"&gt;sudo env &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;:/usr/bin /usr/local/lib/node_modules/pm2/bin/pm2 startup systemd &lt;span class="nt"&gt;-u&lt;/span&gt; ayo &lt;span class="nt"&gt;--hp&lt;/span&gt; /home/ayo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll need to copy and paste the generated command into the terminal, and then run it as the root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo env &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;:/usr/bin /usr/local/lib/node_modules/pm2/bin/pm2 startup &amp;lt;distribution&amp;gt; &lt;span class="nt"&gt;-u&lt;/span&gt; &amp;lt;user&amp;gt; &lt;span class="nt"&gt;--hp&lt;/span&gt; &amp;lt;home-path&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything goes well, you'll see the following output, indicating that PM2 is configured to start at boot.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;PM2] Init System found: systemd

&lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;PM2] &lt;span class="o"&gt;[&lt;/span&gt;v] Command successfully executed.
+---------------------------------------+
&lt;span class="o"&gt;[&lt;/span&gt;PM2] Freeze a process list on reboot via:
&lt;span class="nv"&gt;$ &lt;/span&gt;pm2 save

&lt;span class="o"&gt;[&lt;/span&gt;PM2] Remove init script via:
&lt;span class="nv"&gt;$ &lt;/span&gt;pm2 unstartup systemd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, you can run &lt;code&gt;pm2 save&lt;/code&gt; to save your process list. This saves the processes currently managed by PM2 to disk so they're accessible to the daemon on system boot.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pm2 save
&lt;span class="o"&gt;[&lt;/span&gt;PM2] Saving current process list...
&lt;span class="o"&gt;[&lt;/span&gt;PM2] Successfully saved &lt;span class="k"&gt;in&lt;/span&gt; /home/&amp;lt;user&amp;gt;/.pm2/dump.pm2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go ahead and restart your computer or server. Once it boots back up, run &lt;code&gt;pm2 list&lt;/code&gt; to see if all the processes are restored. If PM2 doesn't restore them automatically, you can manually relaunch them with the &lt;code&gt;resurrect&lt;/code&gt; subcommand. You then won't need to start each process individually.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pm2 resurrect
&lt;span class="o"&gt;[&lt;/span&gt;PM2] Resurrecting
&lt;span class="o"&gt;[&lt;/span&gt;PM2] Restoring processes located &lt;span class="k"&gt;in&lt;/span&gt; /home/&amp;lt;user&amp;gt;/.pm2/dump.pm2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At any point in the future, you can run &lt;code&gt;pm2 save&lt;/code&gt; again to update the list of processes that should be restored on boot or when using the &lt;code&gt;resurrect&lt;/code&gt; subcommand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clustering with PM2
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://blog.appsignal.com/2021/02/03/improving-node-application-performance-with-clustering.html"&gt;Clustering in Node.js&lt;/a&gt; refers to creating child processes that run simultaneously and share the same port in an application. This technique makes it possible to horizontally scale a Node.js application on a single machine, taking advantage of the processing capabilities offered by multi-core systems (since an instance of a Node.js app only runs on a single thread).&lt;/p&gt;

&lt;p&gt;The standard Node.js library provides a &lt;a href="https://nodejs.org/api/cluster.html"&gt;cluster module&lt;/a&gt; to set up clustering in Node.js applications. In a nutshell, it creates child processes (workers) and distributes incoming connections across the simultaneously running worker processes. You'll need to modify your source code to spawn and manage the workers and set up how you'd like to distribute incoming connections amongst them.&lt;/p&gt;

&lt;p&gt;PM2 also provides a cluster mode that uses the native cluster module under the hood. However, it does not require any modifications to the application's source code. Instead, all you need to do to start a Node.js program in cluster mode is to supply the &lt;code&gt;-i&lt;/code&gt; option to the &lt;code&gt;start&lt;/code&gt; subcommand, as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pm2 start app.js &lt;span class="nt"&gt;-i&lt;/span&gt; 0
┌────┬────────────────────┬──────────┬──────┬───────────┬──────────┬──────────┐
│ &lt;span class="nb"&gt;id&lt;/span&gt; │ name               │ mode     │ ↺    │ status    │ cpu      │ memory   │
├────┼────────────────────┼──────────┼──────┼───────────┼──────────┼──────────┤
│ 0  │ app                │ cluster  │ 0    │ online    │ 0%       │ 49.0mb   │
│ 1  │ app                │ cluster  │ 0    │ online    │ 0%       │ 46.8mb   │
│ 2  │ app                │ cluster  │ 0    │ online    │ 0%       │ 44.8mb   │
│ 3  │ app                │ cluster  │ 0    │ online    │ 0%       │ 42.2mb   │
└────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;-i&lt;/code&gt; or &lt;em&gt;instances&lt;/em&gt; option above allows you to specify the number of workers (child processes) that PM2 should launch. You can set &lt;code&gt;0&lt;/code&gt; or &lt;code&gt;max&lt;/code&gt; to specify that PM2 should spawn as many workers as the number of available CPU cores (as above). Alternatively, you can set the exact number of workers to be greater than the number of available CPU cores, if desired. If you want to add additional worker processes on the fly, use the &lt;code&gt;scale&lt;/code&gt; subcommand as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pm2 scale &amp;lt;app_name&amp;gt; +4 &lt;span class="c"&gt;# add 4 additional workers in realtime&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once your application launches in cluster mode, incoming requests to the server will be automatically load-balanced across all the worker processes, which can significantly improve throughput. This feature also enables you to restart your app in production (using &lt;code&gt;pm2 restart&lt;/code&gt;) without suffering any downtime since PM2 waits for the new workers to become operational before it kills the old ones.&lt;/p&gt;

&lt;p&gt;PM2's clustering feature works best when your application is completely &lt;a href="https://12factor.net/processes"&gt;stateless&lt;/a&gt;. You won't need any code modifications to scale on the same server or even across multiple servers if your app doesn't maintain any state in individual processes. If your application isn't stateless, you'll likely get better results directly using the native cluster module.&lt;/p&gt;

&lt;h2&gt;
  
  
  Log Management in PM2
&lt;/h2&gt;

&lt;p&gt;Log management is quite straightforward in PM2. The logs for all your running applications are placed in the &lt;code&gt;~/.pm2/logs&lt;/code&gt; directory, and they can be displayed with the &lt;code&gt;logs&lt;/code&gt; subcommand. All log entries are prefixed with the application's name to ensure easy identification.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pm2 logs &lt;span class="c"&gt;# display all logs in realtime&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;pm2 logs &amp;lt;app_name&amp;gt; &lt;span class="c"&gt;# display only a specific app's logs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also clear log data with the &lt;code&gt;flush&lt;/code&gt; subcommand:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pm2 flush &lt;span class="c"&gt;# clear all log data&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;pm2 flush &amp;lt;app_name&amp;gt; &lt;span class="c"&gt;# flush log data for a specific app&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To enable log rotation, install the following module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pm2 &lt;span class="nb"&gt;install &lt;/span&gt;pm2-logrotate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Wrap Up and Next Steps: Dive Further into PM2
&lt;/h2&gt;

&lt;p&gt;I hope this article has helped to crystallize the importance of process management in Node.js applications and how to leverage PM2's robust feature set to manage your application efficiently.&lt;/p&gt;

&lt;p&gt;PM2 offers other capabilities that were not covered in this article such as Docker integration, a JavaScript API, and a daemon-less mode, so ensure you check out &lt;a href="https://pm2.keymetrics.io/docs/usage/quick-start/"&gt;PM2's documentation&lt;/a&gt; to learn more about these advanced features.&lt;/p&gt;

&lt;p&gt;Thanks for reading, and happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you liked this post, &lt;a href="https://blog.appsignal.com/javascript-sorcery"&gt;subscribe to our JavaScript Sorcery list&lt;/a&gt; for a monthly deep dive into more magical JavaScript tips and tricks.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.P.S. If you need an APM for your Node.js app, go and &lt;a href="https://www.appsignal.com/nodejs"&gt;check out the AppSignal APM for Node.js&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>node</category>
    </item>
    <item>
      <title>An Introduction to Deno: Is It Better than Node.js?</title>
      <dc:creator>Ayooluwa Isaiah</dc:creator>
      <pubDate>Wed, 16 Feb 2022 12:40:58 +0000</pubDate>
      <link>https://dev.to/appsignal/an-introduction-to-deno-is-it-better-than-nodejs-3ac3</link>
      <guid>https://dev.to/appsignal/an-introduction-to-deno-is-it-better-than-nodejs-3ac3</guid>
      <description>&lt;p&gt;Deno is a JavaScript and TypeScript runtime similar to Node.js, built on Rust and the V8 JavaScript engine. It was created by Ryan Dahl, the original inventor of Node.js, to counter mistakes he made when he originally designed and released Node.js back in 2009.&lt;/p&gt;

&lt;p&gt;Ryan's regrets about Node.js are well documented in &lt;a href="https://www.youtube.com/watch?v=M3BM9TB-8yA"&gt;his famous '10 Things I Regret About Node.js' talk&lt;/a&gt; at JSConf EU in 2018. To summarize, he bemoaned the lack of attention to security, module resolution through &lt;code&gt;node_modules&lt;/code&gt;, various deviations from how browser's worked, amongst other things, and he set out to fix all these mistakes in Deno.&lt;/p&gt;

&lt;p&gt;In this article, we'll discuss why Deno was created and its advantages and disadvantages compared to Node.js. It'll also give a practical overview of Deno's quirks and features so that you can decide if it's a good fit for your next project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing Deno
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://deno.land/"&gt;Deno&lt;/a&gt; is distributed as a single, self-contained binary without any dependencies.&lt;/p&gt;

&lt;p&gt;You can &lt;a href="https://deno.land/manual@v1.14.2/getting_started/installation"&gt;install Deno&lt;/a&gt; in various ways, depending on your operating system. The simplest method involves downloading and executing a shell script as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Linux and macOS&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://deno.land/x/install/install.sh | sh

&lt;span class="c"&gt;# Windows PowerShell&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;iwr https://deno.land/x/install/install.ps1 &lt;span class="nt"&gt;-useb&lt;/span&gt; | iex
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you've executed the appropriate command for your operating system, the Deno CLI binary will be downloaded to your computer. You may be required to add the binary location to your &lt;code&gt;PATH&lt;/code&gt;, depending on the installation method you chose.&lt;/p&gt;

&lt;p&gt;You can do this in Bash by adding the lines below to your&lt;br&gt;
&lt;code&gt;$HOME/bash_profile&lt;/code&gt; file. You may need to start a new shell session for the changes to take effect.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DENO_INSTALL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.deno"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DENO_INSTALL&lt;/span&gt;&lt;span class="s2"&gt;/bin:&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To verify the installed version of Deno, run the command below. It should print the Deno version to the console if the CLI was downloaded successfully and added to your &lt;code&gt;PATH&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;deno &lt;span class="nt"&gt;--version&lt;/span&gt;
deno 1.14.2 &lt;span class="o"&gt;(&lt;/span&gt;release, x86_64-unknown-linux-gnu&lt;span class="o"&gt;)&lt;/span&gt;
v8 9.4.146.16
typescript 4.4.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have an outdated version of Deno, upgrading to the latest release can be done through the &lt;code&gt;upgrade&lt;/code&gt; subcommand:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;deno upgrade
Looking up latest version
Found latest version 1.14.2
Checking https://github.com/denoland/deno/releases/download/v1.14.2/deno-x86_64-unknown-linux-gnu.zip
31.5 MiB / 31.5 MiB &lt;span class="o"&gt;(&lt;/span&gt;100.0%&lt;span class="o"&gt;)&lt;/span&gt;
Deno is upgrading to version 1.14.2
Archive:  /tmp/.tmpfdtMXE/deno.zip
  inflating: deno
Upgraded successfully
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go ahead and write a customary hello world program to verify that everything works correctly. You can create a directory for your Deno programs and place the following code in an &lt;code&gt;index.ts&lt;/code&gt; file at the directory's root.&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;function&lt;/span&gt; &lt;span class="nx"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str&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="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`Hello &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;str&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="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="nx"&gt;log&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Deno&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save and execute the file by providing the filename as an argument to the &lt;code&gt;run&lt;/code&gt; subcommand. If the text "Hello Deno!" outputs, it means that you have installed and set up Deno correctly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;deno run index.ts
Check file:///home/ayo/dev/deno/index.ts
Hello Deno!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To find out about other features and options provided by the Deno CLI, use the &lt;code&gt;--help&lt;/code&gt; flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;deno &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deno's First-class TypeScript Support
&lt;/h2&gt;

&lt;p&gt;One of the big selling points of Deno over Node.js is its first-class support for TypeScript.&lt;/p&gt;

&lt;p&gt;As you've already seen, you don't need to do anything besides install the Deno CLI for it to work. Like its predecessor, Deno uses the V8 runtime engine under the hood to parse and execute JavaScript code, but it also includes the &lt;a href="https://github.com/microsoft/TypeScript"&gt;TypeScript compiler&lt;/a&gt; in its executable to achieve TypeScript support.&lt;/p&gt;

&lt;p&gt;Under the hood, TypeScript code is checked and compiled. The resulting JavaScript code is cached in a directory on your filesystem, ready to be executed again without being compiled from scratch. You can use &lt;code&gt;deno info&lt;/code&gt; to inspect the location of the cache directory and other directories containing Deno-managed files.&lt;/p&gt;

&lt;p&gt;Deno does not require any configuration to work with TypeScript, but you can provide one if you want to tweak how the TypeScript compiler parses the code. You can provide a JSON file to specify the TypeScript compiler options. Although &lt;code&gt;tsconfig.json&lt;/code&gt; is the convention when using the standalone &lt;code&gt;tsc&lt;/code&gt; compiler, the Deno team recommends using &lt;code&gt;deno.json&lt;/code&gt; because other Deno-specific configuration options can be placed there.&lt;/p&gt;

&lt;p&gt;Note that Deno doesn't support all TypeScript compiler options. &lt;a href="https://deno.land/manual@v1.14.2/typescript/configuration#how-deno-uses-a-configuration-file"&gt;A full list&lt;/a&gt; of the available options, along with their default values, are presented in the Deno documentation. Here's a sample configuration file for Deno:&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;"compilerOptions"&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;"checkJs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"noImplicitReturns"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"noUnusedLocals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"noUnusedParameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"noUncheckedIndexedAccess"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&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;p&gt;At the time of writing, Deno does not automatically detect a &lt;code&gt;deno.json&lt;/code&gt; file so it must be specified via the &lt;code&gt;--config&lt;/code&gt; flag. However, this feature is planned for a future release.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;deno run &lt;span class="nt"&gt;--config&lt;/span&gt; deno.json index.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the Deno CLI encounters a type error, it halts the compilation of the script and terminates with a non-zero exit code. You can bypass the error by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;using &lt;code&gt;//@ts-ignore&lt;/code&gt; or &lt;code&gt;//@ts-expect-error&lt;/code&gt; at the point where the error occurred or&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;// @ts-nocheck&lt;/code&gt; at the beginning of the file to ignore all errors in a file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Deno also provides a &lt;code&gt;--no-check&lt;/code&gt; flag to disable type checking altogether. This helps prevent the TypeScript compiler from slowing you down when iterating quickly on a problem.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;deno run &lt;span class="nt"&gt;--no-check&lt;/span&gt; index.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Permissions in Deno
&lt;/h2&gt;

&lt;p&gt;Deno prides itself on being a secure runtime for JavaScript and TypeScript. Part of the way it maintains security is through its &lt;a href="https://deno.land/manual@v1.14.2/getting_started/permissions"&gt;permissions system&lt;/a&gt;. To demonstrate how permissions work in Deno, add the below script to your &lt;code&gt;index.ts&lt;/code&gt; file. It's a script that fetches the &lt;a href="https://disease.sh/docs/"&gt;latest global Covid-19 statistics from disease.sh&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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getCovidStats&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&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;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://disease.sh/v3/covid-19/all&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;data&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;response&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;getCovidStats&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you attempt to execute the script, it should display a &lt;code&gt;PermissionDenied&lt;/code&gt; error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;deno run index.ts
PermissionDenied: Requires net access to &lt;span class="s2"&gt;"disease.sh"&lt;/span&gt;, run again with the &lt;span class="nt"&gt;--allow-net&lt;/span&gt; flag
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The error message above indicates that the script hasn't been granted network access. It suggests including the &lt;code&gt;--allow-net&lt;/code&gt; flag in the command to grant access.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;deno run &lt;span class="nt"&gt;--allow-net&lt;/span&gt; index.ts
┌────────────────────────┬───────────────┐
│ &lt;span class="o"&gt;(&lt;/span&gt;idx&lt;span class="o"&gt;)&lt;/span&gt;                  │ Values        │
├────────────────────────┼───────────────┤
│ updated                │ 1633335683059 │
│ cases                  │     235736138 │
│ todayCases             │         32766 │
│ deaths                 │       4816283 │
│ todayDeaths            │           670 │
│ recovered              │     212616434 │
│ todayRecovered         │         51546 │
│ active                 │      18303421 │
│ critical               │         86856 │
│ casesPerOneMillion     │         30243 │
│ deathsPerOneMillion    │         617.9 │
│ tests                  │    3716763329 │
│ testsPerOneMillion     │     473234.63 │
│ population             │    7853954694 │
│ oneCasePerPeople       │             0 │
│ oneDeathPerPeople      │             0 │
│ oneTestPerPeople       │             0 │
│ activePerOneMillion    │       2330.47 │
│ recoveredPerOneMillion │      27071.26 │
│ criticalPerOneMillion  │         11.06 │
│ affectedCountries      │           223 │
└────────────────────────┴───────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of granting blanket approval for the script to access all websites (as shown above), you can provide an allowlist of comma-separated hostnames or IP addresses as an argument to &lt;code&gt;--allow-net&lt;/code&gt; so that only the specified websites are accessible by the script. If the script tries to connect to a domain that is not in the allowlist, Deno will prevent it from connecting, and the script execution will fail.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;deno run &lt;span class="nt"&gt;--allow-net&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'disease.sh'&lt;/span&gt; index.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This feature is one of Deno's improvements over Node.js where&lt;br&gt;
any script can access any resource over the network. Similar permissions also exist for reading from and writing to the filesystem. If a script needs to perform either task, you need to specify the &lt;code&gt;--allow-read&lt;/code&gt; and &lt;code&gt;--allow-write&lt;/code&gt; permissions, respectively. Both flags allow you to set the specific directories accessible to a script so that other parts of the filesystem are safe from tampering. Deno also provides an &lt;code&gt;--allow-all&lt;/code&gt; flag that enables all security-sensitive functions for a script, if so desired.&lt;/p&gt;
&lt;h2&gt;
  
  
  Deno's Compatibility With Browser APIs
&lt;/h2&gt;

&lt;p&gt;One of Deno's main &lt;a href="https://deno.land/manual@v1.14.2/introduction#goals"&gt;goals&lt;/a&gt; is to be compatible with web browsers, where possible. This is reflected in its use of web platform APIs instead of creating a Deno-specific API for certain operations. For example, we saw the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API"&gt;Fetch API&lt;/a&gt; in action in the previous section. This is the exact Fetch API used in browsers, with a few deviations where necessary to account for the unique security model in Deno (and these changes are mostly inconsequential).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://deno.land/manual@v1.14.2/runtime/web_platform_apis"&gt;There's a list of all implemented browser APIs in Deno's online documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Dependency Management in Deno
&lt;/h2&gt;

&lt;p&gt;The way Deno manages dependencies is probably the most obvious way it diverges significantly from Node.js.&lt;/p&gt;

&lt;p&gt;Node.js uses a package manager like &lt;code&gt;npm&lt;/code&gt; or &lt;code&gt;yarn&lt;/code&gt; to download third-party packages from the &lt;a href="https://npmjs.com"&gt;npm registry&lt;/a&gt; into a &lt;code&gt;node_modules&lt;/code&gt; directory and a &lt;code&gt;package.json&lt;/code&gt; file to keep track of a project's dependencies. Deno does away with those mechanisms in favor of a more browser-centric way of using third-party packages: URLs.&lt;/p&gt;

&lt;p&gt;Here's an example that uses &lt;a href="https://github.com/oakserver/oak"&gt;Oak&lt;/a&gt;, a web application framework for Deno, to create a basic web server:&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;Application&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;https://deno.land/x/oak/mod.ts&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;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;ctx&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;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello Deno!&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;listen&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;hostname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;secure&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="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Listening on: http://localhost:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;port&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="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8000&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deno uses ES modules, the same module system used in web browsers. A module can be imported from an absolute or relative path, as long as the referenced script exports methods or other values. It's worth noting that the file extension must always be present, regardless of whether you import from an absolute or relative path.&lt;/p&gt;

&lt;p&gt;While you can import modules from any URL, many third-party modules specifically built for Deno are cached through &lt;a href="https://deno.land/x"&gt;deno.land/x&lt;/a&gt;. Each time a new version of a module is released, it is automatically cached at that location and made immutable so that the contents of a specific version of a module remain unchangeable.&lt;/p&gt;

&lt;p&gt;Suppose you run the code in the previous snippet. In that case, it will download the module and all its dependencies and cache them locally in the directory specified by the &lt;code&gt;DENO_DIR&lt;/code&gt; environmental variable (the default should be &lt;code&gt;$HOME/.cache/deno&lt;/code&gt;). The next time the program runs, there will be no downloads since all the dependencies have been cached locally. This is similar to how the &lt;a href="https://go.dev/blog/using-go-modules"&gt;Go module&lt;/a&gt; system works.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;deno run &lt;span class="nt"&gt;--allow-net&lt;/span&gt; index.ts
Download https://deno.land/x/oak/mod.ts
Warning Implicitly using latest version &lt;span class="o"&gt;(&lt;/span&gt;v9.0.1&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;https://deno.land/x/oak/mod.ts
Download https://deno.land/x/oak@v9.0.1/mod.ts
&lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For production applications, the creators of Deno &lt;a href="https://deno.land/manual@v1.14.2/linking_to_external_code#but-what-if-the-host-of-the-url-goes-down-the-source-won#39t-be-available"&gt;recommend&lt;/a&gt; vendoring your dependencies by checking them into source control to ensure continued availability (even if the source of the module is unavailable, for whatever reason). Point the &lt;code&gt;DENO_DIR&lt;/code&gt; environmental variable to a local directory in your project (such as &lt;code&gt;vendor&lt;/code&gt;), which you can commit to Git.&lt;/p&gt;

&lt;p&gt;For example, the command below will download all your script's dependencies into a &lt;code&gt;vendor&lt;/code&gt; directory in your project. You can subsequently commit the folder to pull it down all at once in your production server. You'll also need to set the &lt;code&gt;DENO_DIR&lt;/code&gt; variable to read from the &lt;code&gt;vendor&lt;/code&gt; directory on the server, instead of downloading them all over again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ DENO_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;/vendor deno cache index.ts &lt;span class="c"&gt;# Linux and macOS&lt;/span&gt;
&lt;span class="nv"&gt;$ $env&lt;/span&gt;:DENO_DIR&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;get-location&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="se"&gt;\v&lt;/span&gt;&lt;span class="s2"&gt;endor"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; deno cache index.ts &lt;span class="c"&gt;# Windows PowerShell&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deno also supports the concept of versioning your dependencies to ensure reproducible builds. At the moment, we've imported Oak from &lt;code&gt;https://deno.land/x/oak/mod.ts&lt;/code&gt;. This always downloads the latest version, which could become incompatible with your program in the future. It also causes Deno to produce a warning when you download the module for the first time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Warning Implicitly using latest version &lt;span class="o"&gt;(&lt;/span&gt;v9.0.1&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;https://deno.land/x/oak/mod.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is considered best practice to reference a specific release as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;import &lt;span class="o"&gt;{&lt;/span&gt; Application &lt;span class="o"&gt;}&lt;/span&gt; from &lt;span class="s1"&gt;'https://deno.land/x/oak@v9.0.1/mod.ts'&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're referencing a module in many files in your codebase, upgrading it may become tedious since you have to update the URL in many places. To circumvent this issue, the Deno team recommends &lt;a href="https://deno.land/manual@v1.14.2/linking_to_external_code#it-seems-unwieldy-to-import-urls-everywhere"&gt;importing your external dependencies in a centralized &lt;code&gt;deps.ts&lt;/code&gt; file and then re-exporting them&lt;/a&gt;. Here's a sample &lt;code&gt;deps.ts&lt;/code&gt; file that exports what we need from the Oak library.&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Router&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;https://deno.land/x/oak@v9.0.1/mod.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in your application code, you can import them as follows:&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;Application&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Router&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;./deps.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, updating a module becomes a simple matter of changing the URL in the &lt;code&gt;deps.ts&lt;/code&gt; file to point to the new version.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Deno Standard Library
&lt;/h2&gt;

&lt;p&gt;Deno provides a &lt;a href="https://deno.land/std"&gt;standard libary&lt;/a&gt; (stdlib) that aims to be a loose port of &lt;a href="https://golang.org/pkg/"&gt;Go's standard library&lt;/a&gt;. The modules contained in the standard library are audited by the Deno team and updated with each release of Deno. The intention behind providing a stdlib is to allow you to create useful web applications right away, without resorting to any third-party packages (as is the norm in the Node.js ecosystem).&lt;/p&gt;

&lt;p&gt;Some examples of standard library modules you might find helpful include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://deno.land/std@0.109.0/http"&gt;HTTP&lt;/a&gt;: An HTTP client and server implementation for Deno.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://deno.land/std@0.109.0/fmt"&gt;Fmt&lt;/a&gt;: Includes helpers for printing formatted output.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://deno.land/std@0.109.0/testing"&gt;Testing&lt;/a&gt;: Provides basic utilities for testing and benchmarking your code.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://deno.land/std@0.109.0/fs"&gt;FS&lt;/a&gt;: Has helpers for manipulating the filesystem.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://deno.land/std@0.109.0/encoding"&gt;Encoding&lt;/a&gt;: Provides helpers to deal with various file formats, such as XML, CSV, base64, YAML, binary, and more.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://deno.land/std@0.110.0/node"&gt;Node&lt;/a&gt;: Has a compatibility layer for the Node.js standard library.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's an example (taken from &lt;a href="https://deno.land/manual@v1.14.2/examples/http_server#using-the-codestdhttpcode-library"&gt;Deno's official docs&lt;/a&gt;) that utilizes the &lt;code&gt;http&lt;/code&gt; module in Deno's stdlib to create a basic web server:&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;listenAndServe&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;https://deno.land/std@0.109.0/http/server.ts&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;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;:8080&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;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Response&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;let&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Your user-agent is:&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&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;user-agent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unknown&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&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;200&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="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`HTTP webserver running. Access it at: http://localhost:8080/`&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;listenAndServe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start the server through the command below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;deno run &lt;span class="nt"&gt;--allow-net&lt;/span&gt; index.ts
HTTP webserver running. Access it at: http://localhost:8080/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a different terminal, access the running server through the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://localhost:8080
Your user-agent is:

curl/7.68.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the modules in the standard library are currently tagged as unstable (as reflected in the version number). This means you should not rely on them just yet for a serious production application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using NPM Packages in Deno
&lt;/h2&gt;

&lt;p&gt;It cannot be denied that one of the major reasons why Node.js has been so successful is the large number of packages that can be downloaded and utilized in a project. If you're considering switching to Deno, you may be wondering if you'd have to give up all the NPM packages you know and love.&lt;/p&gt;

&lt;p&gt;The short answer is: no. While you may not be able to utilize some NPM packages in Deno if they rely on Node.js APIs (especially if the specific APIs are not supported in &lt;a href="https://deno.land/std/node"&gt;Deno's Node.js compatibility layer&lt;/a&gt;), many NPM packages can be utilized in Deno through CDNs like&lt;br&gt;
&lt;a href="https://esm.sh/"&gt;esm.sh&lt;/a&gt; and &lt;a href="https://www.skypack.dev/"&gt;skypack.dev&lt;/a&gt;. Both these CDNs provide NPM packages as ES Modules that can be subsequently consumed in a Deno script even if the author of the package did not design it to target Deno specifically.&lt;/p&gt;

&lt;p&gt;Here's an example that imports the &lt;a href="https://www.npmjs.com/package/dayjs"&gt;dayjs NPM package&lt;/a&gt; from Skypack in a Deno script:&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="nx"&gt;dayjs&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;https://cdn.skypack.dev/dayjs@1.10.7&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Today is: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;dayjs&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MMMM DD, YYYY&lt;/span&gt;&lt;span class="dl"&gt;"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;deno run index.ts
Today is: October 05, 2021
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To ensure that Deno can discover the types associated with a package, ensure you add the &lt;code&gt;?dts&lt;/code&gt; suffix at the end of the package URL when using Skypack's CDN. This causes Skypack to set a &lt;code&gt;X-TypeScript-Types&lt;/code&gt; header so that Deno can automatically discover the types associated with a package. Esm.sh includes this header by default, but you can opt-out by adding the &lt;code&gt;?no-check&lt;/code&gt; suffix at the end of the package URL.&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="nx"&gt;dayjs&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;https://cdn.skypack.dev/dayjs@1.10.7?dts&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;h2&gt;
  
  
  Deno Tooling
&lt;/h2&gt;

&lt;p&gt;The Deno CLI comes with several valuable tools that make the developer experience much more pleasant. Like Node.js, it comes with a REPL (Read Evaluate Print Loop), which you can access with &lt;code&gt;deno repl&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;deno repl
Deno 1.14.2
&lt;span class="nb"&gt;exit &lt;/span&gt;using ctrl+d or close&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 2+2
4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It also has a built-in &lt;a href="https://deno.land/manual@v1.14.2/getting_started/command_line_interface#watch-mode"&gt;file watcher&lt;/a&gt; that can be used with several of its subcommands. For example, you can configure &lt;code&gt;deno run&lt;/code&gt; to automatically rebuild and restart a program once a file is changed by using the &lt;code&gt;--watch&lt;/code&gt; flag. In Node.js, this functionality is generally achieved through some third-party package such as &lt;a href="https://www.npmjs.com/package/nodemon"&gt;nodemon&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;deno run &lt;span class="nt"&gt;--allow-net&lt;/span&gt; &lt;span class="nt"&gt;--watch&lt;/span&gt; index.ts
HTTP webserver running. Access it at: http://localhost:8080/
Watcher File change detected! Restarting!
HTTP webserver running. Access it at: http://localhost:8080/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;a href="https://deno.com/blog/v1.6"&gt;Deno 1.6&lt;/a&gt;, you can compile scripts into self-contained executables that do not require Deno to be installed through the &lt;code&gt;compile&lt;/code&gt; subcommand (you can use &lt;a href="https://github.com/vercel/pkg"&gt;pkg&lt;/a&gt; to do the same in Node.js). You can also generate executables for other platforms (&lt;a href="https://deno.land/manual@v1.14.2/tools/compiler#cross-compilation"&gt;cross compilation&lt;/a&gt;) through the &lt;code&gt;--target&lt;/code&gt; flag. When compiling a script, you must specify the permissions needed for it to run.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;deno compile &lt;span class="nt"&gt;--allow-net&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; server index.ts
&lt;span class="nv"&gt;$ &lt;/span&gt;./server
HTTP webserver running. Access it at: http://localhost:8080/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the binaries produced through this process are quite huge. In my testing, &lt;code&gt;deno compile&lt;/code&gt; produced an 83MB binary for a simple "Hello world" program. However, the Deno team is currently working on a way to reduce the file sizes to be a lot more manageable.&lt;/p&gt;

&lt;p&gt;Another way to distribute a Deno program is to package it into a single JavaScript file through the &lt;code&gt;bundle&lt;/code&gt; subcommand. This file contains the source code of the program and all its dependencies, and it can be executed through &lt;code&gt;deno run&lt;/code&gt; as shown below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;deno bundle index.ts index.bundle.js
Check file:///home/ayo/dev/demo/deno/index.js
Bundle file:///home/ayo/dev/demo/deno/index.js
Emit &lt;span class="s2"&gt;"index.bundle.js"&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;7.39KB&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;deno run &lt;span class="nt"&gt;--allow-net&lt;/span&gt; index.bundle.js
HTTP webserver running. Access it at: http://localhost:8080/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two additional great tools that Deno ships with are the built-in &lt;a href="https://deno.land/manual@v1.14.2/tools/linter"&gt;linter&lt;/a&gt; (&lt;code&gt;deno lint&lt;/code&gt;) and &lt;a href="https://deno.land/manual@v1.14.2/tools/formatter"&gt;formatter&lt;/a&gt; (&lt;code&gt;deno fmt&lt;/code&gt;). In the Node.js ecosystem, linting and formatting code are typically handled with &lt;a href="https://eslint.org"&gt;ESLint&lt;/a&gt; and &lt;a href="https://prettier.io/"&gt;Prettier&lt;/a&gt;, respectively.&lt;/p&gt;

&lt;p&gt;When using Deno, you no longer need to install anything or write configuration files to get linting and formatting for JavaScript, TypeScript, and other &lt;a href="https://deno.land/manual@v1.14.2/tools/formatter#code-formatter"&gt;supported file formats&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Unit Testing in Deno
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://deno.land/manual@v1.14.2/testing"&gt;Support for unit testing&lt;/a&gt; is built into Deno for both JavaScript and TypeScript code. When you run &lt;code&gt;deno test&lt;/code&gt;, it automatically detects any files that end with &lt;code&gt;_test.ts&lt;/code&gt; or &lt;code&gt;.test.ts&lt;/code&gt; (also supports other file extensions) and executes any defined tests therein.&lt;/p&gt;

&lt;p&gt;To write your first test, create an &lt;code&gt;index_test.ts&lt;/code&gt; file and populate it with the following 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;assertEquals&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;https://deno.land/std@0.109.0/testing/asserts.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;Deno&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Multiply two numbers&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ans&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ans&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&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;Deno provides the &lt;code&gt;Deno.test&lt;/code&gt; method for creating a unit test. It takes the name of the test as its first argument. Its second argument is the function executed when the test runs.&lt;/p&gt;

&lt;p&gt;There is a second style that takes in an object instead of two arguments. It supports other &lt;a href="https://deno.land/manual@v1.14.2/testing#test-definition-filtering"&gt;properties&lt;/a&gt; aside from the test name and function to configure if or how the test should run.&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;Deno&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Multiply two numbers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;fn&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;ans&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ans&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&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;The &lt;code&gt;assertEquals()&lt;/code&gt; method comes from the &lt;code&gt;testing&lt;/code&gt; module in the standard library, and it provides a way to easily check the equality of two values.&lt;/p&gt;

&lt;p&gt;Go ahead and run the test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;deno &lt;span class="nb"&gt;test
test &lt;/span&gt;Multiply two numbers ... ok &lt;span class="o"&gt;(&lt;/span&gt;8ms&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;test &lt;/span&gt;result: ok. 1 passed&lt;span class="p"&gt;;&lt;/span&gt; 0 failed&lt;span class="p"&gt;;&lt;/span&gt; 0 ignored&lt;span class="p"&gt;;&lt;/span&gt; 0 measured&lt;span class="p"&gt;;&lt;/span&gt; 0 filtered out &lt;span class="o"&gt;(&lt;/span&gt;37ms&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Deno Language Server
&lt;/h2&gt;

&lt;p&gt;One of the major considerations for choosing a programming language or environment is its integration with editors and IDEs. In Deno 1.6, a built-in language server (&lt;code&gt;deno lsp&lt;/code&gt;) was added to the runtime to provide features such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;autocompletion&lt;/li&gt;
&lt;li&gt;go-to-definition&lt;/li&gt;
&lt;li&gt;linting and formatting
integration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As well as other language smarts for any editor that supports the &lt;a href="https://microsoft.github.io/language-server-protocol/"&gt;Language Server Protocol&lt;/a&gt; (LSP). You can learn more about setting up Deno support in your editor in &lt;a href="https://deno.land/manual@v1.11.5/getting_started/setup_your_environment#lsp-clients"&gt;Deno's online docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up: Should I Choose Deno over Node.js?
&lt;/h2&gt;

&lt;p&gt;In this article, we've considered many aspects of the Deno runtime, and ways in which it's an upgrade over Node.js.&lt;/p&gt;

&lt;p&gt;There’s a lot more to say about Deno and its ecosystem, but this should hopefully be a helpful introduction for Node.js developers considering Deno for a new project. The lesser availability of third-party packages for Deno is an obvious aspect where it falls short, as is the fact that it's not as battle-tested as Node.js in the real world due to its young age (Deno 1.0 was released in May 2020).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://web.archive.org/web/20210421005320/https://mayankchoubey.github.io/Deno-vs-Node-Performance/"&gt;Comparing the performance between Node.js and Deno&lt;/a&gt; shows that they're within the same ballpark in most cases, although there are a few scenarios where Node.js exhibits far superior performance. The measured disparities are bound to improve as Deno becomes more mature.&lt;/p&gt;

&lt;p&gt;When deciding between Node.js and Deno, it's also important to keep in mind that some of the benefits that Deno provides can also be brought to Node.js with the help of third-party packages. So if there are only one or two things that you admire about Deno, chances are you'll be able to achieve a similar result in Node.js, though not as seamlessly.&lt;/p&gt;

&lt;p&gt;Thanks for reading, and happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you liked this post, &lt;a href="https://blog.appsignal.com/javascript-sorcery"&gt;subscribe to our JavaScript Sorcery list&lt;/a&gt; for a monthly deep dive into more magical JavaScript tips and tricks.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.P.S. If you need an APM for your Node.js app, go and &lt;a href="https://www.appsignal.com/nodejs"&gt;check out the AppSignal APM for Node.js&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>deno</category>
      <category>node</category>
    </item>
    <item>
      <title>How to Set Up a Node.js Project with TypeScript</title>
      <dc:creator>Ayooluwa Isaiah</dc:creator>
      <pubDate>Wed, 26 Jan 2022 13:18:00 +0000</pubDate>
      <link>https://dev.to/appsignal/how-to-set-up-a-nodejs-project-with-typescript-10a9</link>
      <guid>https://dev.to/appsignal/how-to-set-up-a-nodejs-project-with-typescript-10a9</guid>
      <description>&lt;p&gt;In this tutorial, you will learn how to add TypeScript support to Node.js projects. We will address common needs, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;compiling and executing the code&lt;/li&gt;
&lt;li&gt;debugging the source files&lt;/li&gt;
&lt;li&gt;configuring third-party packages so that the TypeScript compiler also validates them&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's get going!&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Use TypeScript?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.typescriptlang.org/"&gt;TypeScript&lt;/a&gt; brings optional static typing to JavaScript projects. The primary benefit of static typing is that type errors are detected and corrected at build time, so code will more likely run correctly once deployed to production. &lt;a href="https://2020.stateofjs.com/en-US/technologies/javascript-flavors/"&gt;A growing number of JavaScript developers&lt;/a&gt; are seeing the value of writing more strongly typed code, which has led to an increase in the adoption of TypeScript for all kinds of JavaScript-based projects.&lt;/p&gt;

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

&lt;p&gt;This article assumes that you have a basic knowledge of &lt;a href="https://nodejs.org/"&gt;Node.js&lt;/a&gt; and TypeScript. You also need to have a recent version of Node.js and npm installed on your computer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing and Configuring TypeScript on Node.js
&lt;/h2&gt;

&lt;p&gt;Besides the web browser, Node.js is the most popular platform on which JavaScript code is executed. However, just like the browser, it lacks native support for TypeScript code (unlike &lt;a href="https://deno.land/"&gt;Deno&lt;/a&gt;, for example).&lt;/p&gt;

&lt;p&gt;Before installing TypeScript, make sure that you've created and initialized a Node.js project with a &lt;code&gt;package.json&lt;/code&gt; file. TypeScript is available as a package on the &lt;a href="https://www.npmjs.com/package/typescript"&gt;npm registry&lt;/a&gt;, and it can be downloaded into your project through a package manager like &lt;a href="https://www.npmjs.com/"&gt;npm&lt;/a&gt; or &lt;a href="https://yarnpkg.com/"&gt;yarn&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;typescript &lt;span class="nt"&gt;--save-dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the above command succeeds, you can check the current version through the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npx tsc &lt;span class="nt"&gt;--version&lt;/span&gt;
Version 4.4.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's advisable to install TypeScript as a project-specific dependency. The same version of the language will then be used, regardless of the machine running the code. You can also install the TypeScript CLI globally by using the &lt;code&gt;--global&lt;/code&gt; switch. A global installation can come in handy for running one-off scripts for testing purposes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;typescript &lt;span class="nt"&gt;--global&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;tsc &lt;span class="nt"&gt;--version&lt;/span&gt;
Version 4.4.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you use a Node.js environment manager like &lt;a href="https://volta.sh/"&gt;Volta&lt;/a&gt;, you'll be able to switch between globally installed TypeScript versions and project-specific ones seamlessly.&lt;/p&gt;

&lt;p&gt;Now that TypeScript is installed in your project, create a &lt;a href="https://www.typescriptlang.org/tsconfig/"&gt;configuration file&lt;/a&gt; that specifies which files should be compiled and the compiler options for the project. This file is called &lt;code&gt;tsconfig.json&lt;/code&gt;, and you should place it at the root of your project directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;tsconfig.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's a basic configuration that you can start with:&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;"extends"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@tsconfig/node16/tsconfig.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&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;"include"&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;"src"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"exclude"&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;"node_modules"&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;p&gt;The above configuration file extends the &lt;a href="https://github.com/tsconfig/bases/"&gt;base configuration&lt;/a&gt; provided by the TypeScript team for Node.js v16. &lt;a href="https://www.typescriptlang.org/tsconfig#compilerOptions"&gt;Additional options&lt;/a&gt; or overrides may be included through the &lt;code&gt;compilerOptions&lt;/code&gt; property. It also specifies that all the files in the &lt;code&gt;src&lt;/code&gt; directory should be included in the program, but everything in the &lt;code&gt;node_modules&lt;/code&gt; directory is skipped entirely. Both the &lt;code&gt;include&lt;/code&gt; and &lt;code&gt;exclude&lt;/code&gt; properties support &lt;a href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#glob-support-in-tsconfigjson"&gt;glob patterns&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Before you proceed, ensure you add the base configuration package for Node.js v16 to your project's &lt;code&gt;devDependencies&lt;/code&gt; through the command below. Base &lt;code&gt;tsconfig.json&lt;/code&gt; packages also exist for &lt;a href="https://www.npmjs.com/package/@tsconfig/node10"&gt;Node 10&lt;/a&gt;, &lt;a href="https://www.npmjs.com/package/@tsconfig/node12"&gt;Node 12&lt;/a&gt;, and &lt;a href="https://www.npmjs.com/package/@tsconfig/node14"&gt;Node 14&lt;/a&gt; at the time of writing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @tsconfig/node16 &lt;span class="nt"&gt;--save-dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Compiling TypeScript Files for Node.js
&lt;/h2&gt;

&lt;p&gt;Go ahead and create the aforementioned &lt;code&gt;src&lt;/code&gt; directory in your project root, and place a &lt;code&gt;main.ts&lt;/code&gt; file inside it. This file should contain the following 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="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;sayMyName&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&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;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Heisenberg&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;You're right 👍&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;else&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="nx"&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;You're wrong 👎&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="nx"&gt;sayMyName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Heisenberg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save the file, then attempt to compile the TypeScript code to JavaScript&lt;br&gt;
through the command below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npx tsc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will get an error indicating that the compiler does not understand the &lt;code&gt;console&lt;/code&gt; object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/main.ts:3:5 - error TS2584: Cannot find name 'console'. Do you need to change your target library? Try changing the 'lib' compiler option to include 'dom'.
src/main.ts:5:1 - error TS2584: Cannot find name 'console'. Do you need to change your target library? Try changing the 'lib' compiler option to include 'dom'.

5 console.log(product(10, 5));
  ~~~~~~~

Found 1 error.

3     console.log("You're right 👍");
      `~~~~~~~`

src/main.ts:5:5 - error TS2584: Cannot find name 'console'. Do you need to change your target library? Try changing the 'lib' compiler option to include 'dom'.

5     console.log("You're wrong 👎");
      `~~~~~~~`


Found 2 errors.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This error occurs because the &lt;code&gt;lib&lt;/code&gt; compiler option set in the &lt;a href="https://www.npmjs.com/package/@tsconfig/node16"&gt;base configuration&lt;/a&gt; for Node.js v16 does not include the &lt;code&gt;dom&lt;/code&gt; option, which contains type definitions for the &lt;code&gt;console&lt;/code&gt; object and other browser-specific APIs. The error message above suggests adding the &lt;code&gt;dom&lt;/code&gt; option to the &lt;code&gt;lib&lt;/code&gt; property to fix the problem, but this is not the correct solution for a Node.js project. The correct fix involves installing the &lt;a href="https://www.npmjs.com/package/@types/node"&gt;type definitions for Node.js APIs&lt;/a&gt; so that the TypeScript compiler can understand and validate all the built-in Node.js APIs. &lt;/p&gt;

&lt;p&gt;Here's how:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @types/node &lt;span class="nt"&gt;--save-dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once installed, the error will vanish the next time &lt;code&gt;npx tsc&lt;/code&gt; is run and a &lt;code&gt;main.js&lt;/code&gt; file will be produced in the &lt;code&gt;src&lt;/code&gt; folder with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use strict&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;sayMyName&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="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;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Heisenberg&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;You're right 👍&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;else&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="nx"&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;You're wrong 👎&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="nx"&gt;sayMyName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Heisenberg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can subsequently execute this file through the &lt;code&gt;node&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;node src/main.js
You&lt;span class="s1"&gt;'re right 👍
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to change the folder where the JavaScript files are placed, you can use the &lt;a href="https://www.typescriptlang.org/tsconfig#Output_Formatting_6256"&gt;outDir&lt;/a&gt; compiler option in your &lt;code&gt;tsconfig.json&lt;/code&gt; file:&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;"compilerOptions"&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;"outDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist"&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;p&gt;Subsequent compilations will emit the JavaScript output into a &lt;code&gt;dist&lt;/code&gt; folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
├── dist
│   └── main.js
├── package.json
├── package-lock.json
├── src
│   └── main.ts
└── tsconfig.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Execute TypeScript Source Files Directly With ts-node
&lt;/h2&gt;

&lt;p&gt;The process of compiling TypeScript source files into JavaScript code before executing them with Node.js can get a little tedious after a while, especially during development. You can eliminate the intermediate steps before running the program through the &lt;a href="https://github.com/TypeStrong/ts-node"&gt;ts-node&lt;/a&gt; CLI to execute &lt;code&gt;.ts&lt;/code&gt; files directly. Go ahead and install the &lt;code&gt;ts-node&lt;/code&gt; package using the command below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;ts-node &lt;span class="nt"&gt;--save-dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Afterwards, execute the &lt;code&gt;main.ts&lt;/code&gt; file with the &lt;code&gt;ts-node&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npx ts-node src/main.ts
You&lt;span class="s1"&gt;'re right 👍
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using &lt;code&gt;ts-node&lt;/code&gt; in this way places the TypeScript compiler as a middleman between the source files and the Node.js runtime. It transpiles the source code before executing the resulting JavaScript code with &lt;code&gt;node&lt;/code&gt; (performed under the hood). This makes the script execution a bit slower than JavaScript output directly with &lt;code&gt;node&lt;/code&gt;. You can opt out of type checking through the &lt;code&gt;--transpile-only&lt;/code&gt; or &lt;code&gt;-T&lt;/code&gt; flag to make the script execute faster in scenarios where type validation isn't essential.&lt;/p&gt;

&lt;p&gt;Another feature that &lt;code&gt;ts-node&lt;/code&gt; enables is the transformation of modern &lt;code&gt;import&lt;/code&gt; syntax to CommonJS syntax. This means that when using &lt;code&gt;ts-node&lt;/code&gt;, you can use &lt;code&gt;import&lt;/code&gt; instead of &lt;code&gt;require&lt;/code&gt; to utilize Node.js modules in your code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/TypeStrong/ts-node#commonjs-vs-native-ecmascript-modules"&gt;Learn more about this feature in the &lt;code&gt;ts-node&lt;/code&gt; project's README document&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  TypeScript Integration with Third-party Node.js NPM Packages
&lt;/h2&gt;

&lt;p&gt;When utilizing Node.js packages from the npm registry, additional setup may be required to compile the project successfully.&lt;/p&gt;

&lt;p&gt;The main problem is that most of the packages you're likely to encounter are written in vanilla JavaScript, so TypeScript cannot determine the valid types for exposed methods. Everything from the library is implicitly typed as &lt;code&gt;any&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here's an example that utilizes the popular &lt;a href="https://www.npmjs.com/package/express"&gt;Express package&lt;/a&gt; to create a web server:&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="nx"&gt;express&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;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;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&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;function&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="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;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello World&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Assuming you've installed the express module with &lt;code&gt;npm install express&lt;/code&gt;, execute the script with &lt;code&gt;ts-node&lt;/code&gt;. It should yield the following errors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npx ts-node src/main.ts
TSError: ⨯ Unable to compile TypeScript:
src/main.ts:4:24 - error TS7006: Parameter &lt;span class="s1"&gt;'req'&lt;/span&gt; implicitly has an &lt;span class="s1"&gt;'any'&lt;/span&gt; type.

4 app.get&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/'&lt;/span&gt;, &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;req, res&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                         ~~~

src/main.ts:4:29 - error TS7006: Parameter &lt;span class="s1"&gt;'res'&lt;/span&gt; implicitly has an &lt;span class="s1"&gt;'any'&lt;/span&gt; type.

4 app.get&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/'&lt;/span&gt;, &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;req, res&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                              ~~~


Found 2 errors.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The TypeScript compiler is responsible for the errors shown above. It cannot determine the type of the &lt;code&gt;req&lt;/code&gt; and &lt;code&gt;res&lt;/code&gt; parameters in the callback function, so they are both implicitly typed as &lt;code&gt;any&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Since the &lt;code&gt;strict&lt;/code&gt; compiler option is set to &lt;code&gt;true&lt;/code&gt; in the &lt;a href="https://www.npmjs.com/package/@tsconfig/node16"&gt;base tsconfig.json&lt;/a&gt; file, the &lt;a href="https://www.typescriptlang.org/tsconfig#noImplicitAny"&gt;noImplicitAny&lt;/a&gt; compiler option is also enabled. This ensures that TypeScript will emit an error instead of inferring type &lt;code&gt;any&lt;/code&gt; when it is unable to determine the type of a value.&lt;/p&gt;

&lt;p&gt;You can fix this error by providing a type declaration file for the &lt;code&gt;express&lt;/code&gt; module so that the TypeScript compiler can accurately determine the valid types for its exported methods. You can find up-to-date type definitions for many popular npm packages in the &lt;a href="https://github.com/DefinitelyTyped/DefinitelyTyped"&gt;DefinitelyTyped GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The type definition for a package can be downloaded through the &lt;code&gt;@types&lt;/code&gt; scope. For example, use the command below to install the &lt;a href="https://www.npmjs.com/package/@types/express"&gt;type definitions for Express&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @types/express &lt;span class="nt"&gt;--save-dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After installing the type definitions for Express, compiling and executing the script with &lt;code&gt;ts-node&lt;/code&gt; should complete successfully without errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Linting TypeScript with ESLint
&lt;/h2&gt;

&lt;p&gt;An important step towards adding comprehensive support for TypeScript in a Node.js application is setting an adequate linting workflow. You can make use of the popular &lt;a href="https://eslint.org/"&gt;ESLint&lt;/a&gt; package to lint TypeScript code. Although it was originally written for JavaScript code, it also supports TypeScript with the help of a few plugins. Go ahead and install the &lt;a href="https://www.npmjs.com/package/eslint"&gt;eslint package&lt;/a&gt; in your project with the command below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;eslint &lt;span class="nt"&gt;--save-dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now create a new &lt;code&gt;.eslintrc.js&lt;/code&gt; file in the root of your project directory. Here is where you'll place the &lt;a href="https://eslint.org/docs/user-guide/configuring/configuration-files"&gt;configuration settings&lt;/a&gt; for ESLint. To add TypeScript support to ESLint, install the &lt;a href="https://www.npmjs.com/package/@typescript-eslint/parser"&gt;@typescript-eslint/parser&lt;/a&gt; and &lt;a href="https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin"&gt;@typescript-eslint/eslint-plugin&lt;/a&gt; packages in your project. The former is used to parse TypeScript code to a format that is understandable by ESLint, while the latter provides TypeScript-specific linting rules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @typescript-eslint/parser @typescript-eslint/eslint-plugin &lt;span class="nt"&gt;--save-dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once both packages are installed, open up your &lt;code&gt;.eslintrc.js&lt;/code&gt; file in your editor, and enter the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@typescript-eslint/parser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;parserOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;ecmaVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;latest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Allows the use of modern ECMAScript features&lt;/span&gt;
    &lt;span class="na"&gt;sourceType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Allows for the use of imports&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;extends&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;plugin:@typescript-eslint/recommended&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// Uses the linting rules from @typescript-eslint/eslint-plugin&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;node&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="c1"&gt;// Enable Node.js global variables&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to override any of the &lt;a href="https://eslint.org/docs/rules/"&gt;linting rules&lt;/a&gt; or configure other rules, use the &lt;code&gt;rules&lt;/code&gt; property in the &lt;code&gt;.eslintrc.js&lt;/code&gt; file, as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&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="na"&gt;rules&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;no-console&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;off&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;import/prefer-default-export&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;off&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;@typescript-eslint/no-unused-vars&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;warn&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After saving the file, run the ESLint CLI on your TypeScript code through this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npx eslint &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--fix&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also create a &lt;code&gt;lint&lt;/code&gt; script in your &lt;code&gt;package.json&lt;/code&gt; file as follows:&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;"scripts"&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="err"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eslint . --fix"&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;p&gt;Then run ESLint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm run lint
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To prevent ESLint from linting certain files or directories, create a &lt;code&gt;.eslintignore&lt;/code&gt; file in your project root, and place the patterns for files to ignore therein. Here's an example in which all generated files in the &lt;code&gt;dist&lt;/code&gt; folder are ignored:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also decide to omit every single JavaScript file in your project directory with the following pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;**/*.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that everything in the &lt;code&gt;node_modules&lt;/code&gt; folder, and files or folders that begin with a dot character (except eslint config files), are ignored automatically, so there's no need to place patterns matching such files in your &lt;code&gt;.eslintignore&lt;/code&gt; file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debug Your TypeScript Code with Visual Studio Code
&lt;/h2&gt;

&lt;p&gt;Debugging TypeScript source files is easy and straightforward with the help of &lt;code&gt;ts-node&lt;/code&gt; and Visual Studio Code. All you need to do is create a launch configuration file (&lt;code&gt;launch.json&lt;/code&gt;) within the &lt;code&gt;.vscode&lt;/code&gt; directory in the project root and populate the file with the following code:&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;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"configurations"&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="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Debug main.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"launch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"cwd"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${workspaceRoot}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"runtimeArgs"&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;"-r"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ts-node/register"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&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;"${workspaceRoot}/src/main.ts"&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;p&gt;The &lt;code&gt;ts-node/register&lt;/code&gt; method is preloaded in the above file to handle TypeScript source files correctly. Secondly, the name of the TypeScript file to run when starting a debugging session is provided as the first value in the &lt;code&gt;args&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;Go ahead and start debugging your Node.js project by pressing F5 on your keyboard. Try to set a breakpoint, then inspect the values in the current scope once the breakpoint is hit. It should work as expected, without issues!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--peV0f6jr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2022-01/debugging-typescript-vscode.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--peV0f6jr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2022-01/debugging-typescript-vscode.png" alt="Debugging TypeScript in VSCode" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying TypeScript to Production
&lt;/h2&gt;

&lt;p&gt;According to its author, &lt;a href="https://github.com/TypeStrong/ts-node/issues/104#issuecomment-250252708"&gt;ts-node is safe to use in production&lt;/a&gt;. Nonetheless, to reduce the start-up time of the server and prevent additional memory usage from keeping the compiler in memory, it's better to compile the source files beforehand. Execute the resulting JavaScript code with the &lt;code&gt;node&lt;/code&gt; command when deploying to production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Node.js and TypeScript: Summing Up
&lt;/h2&gt;

&lt;p&gt;In this article, you learned how to configure a Node.js project with TypeScript support and run TypeScript source files directly, without an intermediate compilation step.&lt;/p&gt;

&lt;p&gt;We also covered how type definition files work, and how to take advantage of predefined type definition files for popular npm packages so that the TypeScript compiler fully validates all third-party dependencies.&lt;/p&gt;

&lt;p&gt;Finally, we discussed how to debug TypeScript files in VSCode, and what to do when deploying your TypeScript project to production.&lt;/p&gt;

&lt;p&gt;Thanks for reading, and happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you liked this post, &lt;a href="https://blog.appsignal.com/javascript-sorcery"&gt;subscribe to our JavaScript Sorcery list&lt;/a&gt; for a monthly deep dive into more magical JavaScript tips and tricks.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.P.S. If you need an APM for your Node.js app, go and &lt;a href="https://www.appsignal.com/nodejs"&gt;check out the AppSignal APM for Node.js&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>typescript</category>
    </item>
    <item>
      <title>7 Ways to Improve Node.js Performance at Scale</title>
      <dc:creator>Ayooluwa Isaiah</dc:creator>
      <pubDate>Wed, 08 Dec 2021 13:00:03 +0000</pubDate>
      <link>https://dev.to/appsignal/7-ways-to-improve-nodejs-performance-at-scale-56md</link>
      <guid>https://dev.to/appsignal/7-ways-to-improve-nodejs-performance-at-scale-56md</guid>
      <description>&lt;p&gt;Performance is one of the most important aspects of web application development.&lt;/p&gt;

&lt;p&gt;A fast application will make its users, developers, and business stakeholders happy, while a slow one is sure to frustrate all three parties.&lt;/p&gt;

&lt;p&gt;In this article, we will consider some practices that you should adopt to scale your Node.js servers. Your servers will then be able to handle high traffic workloads without a degraded user experience.&lt;/p&gt;

&lt;p&gt;By following all of the proven and tested performance tips in this post, you will be able improve the speed and performance of your product, giving it the edge it needs to succeed in the market.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Profile and Monitor Your Application
&lt;/h2&gt;

&lt;p&gt;Before you attempt to improve the performance of a system, it's necessary to measure the current level of performance. This way, you'll know the inefficiencies and the right strategy to adopt to get the desired results.&lt;/p&gt;

&lt;p&gt;Gauging the current level of performance of an application may require running different kinds of tests, such as the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Load testing&lt;/strong&gt;: refers to the practice of simulating the expected usage of a system and measuring its response as the workload increases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stress testing&lt;/strong&gt;: designed to measure how a system performs beyond the limits of normal working conditions. Its goal is to determine how much the system can handle before it fails and how it attempts to recover from a failure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spike testing&lt;/strong&gt;: helps to test the behavior of an application when it receives a drastic increase or decrease in load.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability testing&lt;/strong&gt;: used to find the point at which the
application stops scaling and identify the reasons behind it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Volume testing&lt;/strong&gt;: determines if a system can cope with large amounts of data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Endurance testing&lt;/strong&gt;: helps evaluate the behavior of a software application under sustained load for a long period, to catch problems such as memory leaks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Performing some or all of the above tests will provide you with several important metrics, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;response times&lt;/li&gt;
&lt;li&gt;average latency&lt;/li&gt;
&lt;li&gt;error rates&lt;/li&gt;
&lt;li&gt;requests per second&lt;/li&gt;
&lt;li&gt;throughput&lt;/li&gt;
&lt;li&gt;CPU and memory usage&lt;/li&gt;
&lt;li&gt;concurrent users&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and more.&lt;/p&gt;

&lt;p&gt;After implementing a specific optimization, don't forget to rerun the tests to verify that your changes had the desired effect on system performance.&lt;/p&gt;

&lt;p&gt;It's also important to utilize an Application Performance Monitoring (APM) tool to keep tabs on the long-term performance of a system. Different monitoring solutions can take care of this this for you. We like &lt;a href="https://appsignal.com/nodejs"&gt;AppSignal&lt;/a&gt; :).&lt;/p&gt;

&lt;p&gt;It's easy to integrate it into your application (just run &lt;code&gt;npx @appsignal/cli install&lt;/code&gt;), and it will automatically track several performance metrics such as response times and throughput alongside error logs, system availability, host metrics, and more. You can use the insights gained from the data to take proactive steps to increase system performance or to quickly identify the root cause of a specific issue so that you can address it promptly before it is noticed by your users.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hAPLkj5Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2021-12/appsignal-perf-dashboard.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hAPLkj5Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2021-12/appsignal-perf-dashboard.png" alt="AppSignal performance dashboard" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Reduce Latency Through Caching
&lt;/h2&gt;

&lt;p&gt;Server-side caching is one of the most common strategies for improving the performance of a web application. Its primary aim is to increase the speed of data retrieval, either by spending less time computing such data or doing I/O (such as retrieving such data over the network or from a database).&lt;/p&gt;

&lt;p&gt;A cache is a high-speed storage layer used as a temporary store for frequently accessed data. You don't have to retrieve data from the (usually much slower) primary source of the data every time it is requested.&lt;/p&gt;

&lt;p&gt;Caching is most effective for data that does not change very often. If your application is receiving lots of requests for the same unchanged data, storing it in a cache is sure to significantly improve the responsiveness of such requests. You can also store the results of computationally intensive tasks in the cache, as long as it can be reused for other requests. This prevents server resources from being bogged down unnecessarily by repeating the work to compute such data.&lt;/p&gt;

&lt;p&gt;Another common candidate for caching is API requests that go to an external system. Suppose the responses can be reliably reused for subsequent requests. In that case, it makes sense to store API requests in the cache layer to avoid the additional network request and any other costs associated with the API in question.&lt;/p&gt;

&lt;p&gt;A relatively straightforward way to implement caching in a Node.js application is through an in-process caching solution such as &lt;a href="https://www.npmjs.com/package/node-cache"&gt;node-cache&lt;/a&gt;. It involves placing actively used data in memory, where it can be retrieved more quickly. The major problem with an in-process cache is that it's tied to an application process, so it's rarely suitable for distributed workflows (especially when caching mutable objects). In such settings, you may use a distributed caching solution such as &lt;a href="https://redis.io/"&gt;Redis&lt;/a&gt; or &lt;a href="https://memcached.org/"&gt;Memcached&lt;/a&gt;. These run independently of an application and are more practical when scaling the application onto multiple servers.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Use Timeouts When Dealing with I/O Operations
&lt;/h2&gt;

&lt;p&gt;When building Node.js applications, timeouts are amongst the easiest things to get wrong. Your server is probably talking to other external services that themselves might also be calling other services. If one service in the chain is slow or unresponsive, it will result in a slow experience for your end-users. Even if you don't run into this issue during development, you can't guarantee that your dependencies will always respond as fast as they usually do, which is why the concept of timeouts is important.&lt;/p&gt;

&lt;p&gt;A timeout is the maximum wait time set on a request. It represents how long a client is prepared to wait for a response from an external service. If the response is not received within the specified limits, the connection will abort so that the application does not hang indefinitely. Many popular libraries for making HTTP requests in Node.js (such as &lt;a href="https://github.com/axios/axios"&gt;axios&lt;/a&gt;) do not set a default timeout, meaning that any remote API can keep your application waiting for the requested resource indefinitely. You should set a request timeout to prevent this from happening:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;axios&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timeout&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// global timeout of 1s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above snippet, a timeout of 1000ms (1s) is set as the default for all HTTP requests made through axios. This guarantees that any request will not take longer than that time, even if the API is unresponsive. You can also set a timeout value on individual requests when the global default is not appropriate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;axios&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://example.com/api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="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="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="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="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="nx"&gt;log&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the axios &lt;code&gt;timeout&lt;/code&gt; value is a &lt;strong&gt;read&lt;/strong&gt; timeout, which is different from a &lt;strong&gt;connection&lt;/strong&gt; timeout. The latter is the time within which a TCP connection must be established, while the former determines how long the client will wait for a response after the connection is established.&lt;/p&gt;

&lt;p&gt;Usually, the connection timeout is much lower than the read timeout. The client is able to try a different server or alternative API if one service is taking too long to accept a connection. This still gives enough time for the server to generate a response once the connection is accepted.&lt;/p&gt;

&lt;p&gt;At the moment, axios does not support setting a connection timeout separately from a read timeout, which could be limiting in some scenarios. If you need this functionality, you can try the &lt;a href="https://github.com/sindresorhus/got"&gt;got&lt;/a&gt; library - it allows for &lt;a href="https://github.com/sindresorhus/got/blob/main/documentation/6-timeout.md"&gt;separate read and connection timeout&lt;br&gt;
specifications&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Before choosing a timeout value, you can monitor the response times for APIs you connect to using specialized tools or track your API calls by logging them. This will allow you to make an informed decision for all the external services that your program interacts with. You should also have a &lt;a href="https://cloud.google.com/iot/docs/how-tos/exponential-backoff"&gt;retry strategy&lt;/a&gt; in place for important services to account for temporary slowdowns. The graph below shows how average the response times for an endpoint can be monitored in &lt;a href="https://www.appsignal.com/tour/performance"&gt;AppSignal&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ynhMgiBG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2021-12/appsignal-perf-chart.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ynhMgiBG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2021-12/appsignal-perf-chart.png" alt="Monitoring response times and throughput in AppSignal" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  4. Don't Serve Static Assets with Node.js
&lt;/h2&gt;

&lt;p&gt;To ensure the best possible performance for your Node.js servers, refrain from using them to serve static assets like JavaScript, CSS, or image files from your application. Node.js wasn't designed with this use case in mind, so serving assets from the main application consumes valuable resources and holds up important business computations. Offload the task of serving static files to a web server like &lt;a href="https://www.nginx.com/"&gt;Nginx&lt;/a&gt;, which can perform optimizations that does not make sense for Node.js to do. &lt;a href="https://web.archive.org/web/20160929154731/http://blog.modulus.io/supercharge-your-nodejs-applications-with-nginx"&gt;This test&lt;/a&gt; shows that Nginx is about twice as fast at delivering static assets as Node.js (using Express static middleware).&lt;/p&gt;

&lt;p&gt;Another option to serve static files is to set up a CDN proxy like &lt;a href="https://aws.amazon.com/cloudfront/"&gt;Amazon CloudFront&lt;/a&gt; to cache your static content and serve it as close as possible to end-users. This frees up the Node.js servers to handle dynamic requests only.&lt;/p&gt;
&lt;h2&gt;
  
  
  5. Use Clustering to Improve Throughput
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XK3_Q7Mr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2021-12/throughput.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XK3_Q7Mr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2021-12/throughput.png" alt="You can monitor your Throughput in AppSignal" width="800" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.appsignal.com/2021/02/03/improving-node-application-performance-with-clustering.html"&gt;Clustering&lt;/a&gt; is a technique used to horizontally scale a Node.js server on a single machine by spawning child processes (workers) that run concurrently and share a single port. It is a common tactic to reduce downtime, slowdowns and outages by distributing the incoming connections across all the available worker processes so that available CPU cores are utilized to their full potential. Since a Node.js instance runs on a single thread, it cannot take advantage of multi-core systems properly - hence the need for clustering.&lt;/p&gt;

&lt;p&gt;You can cluster your Node.js server through the &lt;a href="https://nodejs.org/api/cluster.html"&gt;cluster module&lt;/a&gt; in the standard library. Here's an example taken from the official documentation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cluster&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cluster&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;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http&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;process&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;process&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;os&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;os&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;cpus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cpus&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;numCPUs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cpus&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;length&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;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isPrimary&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="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Primary &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; is running`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Fork workers.&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;numCPUs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&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;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fork&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;exit&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;worker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signal&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="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`worker &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; died`&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;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Workers can share any TCP connection&lt;/span&gt;
  &lt;span class="c1"&gt;// In this case it is an HTTP server&lt;/span&gt;
  &lt;span class="nx"&gt;http&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createServer&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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;writeHead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&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;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hello world&lt;/span&gt;&lt;span class="se"&gt;\n&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="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8000&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="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Worker &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; started`&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;Once you run this program, connections sent to port 8000 will be shared between the worker processes. This will lead to more efficient requests management in the application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ node server.js
Primary 15990 is running
Worker 15997 started
Worker 15998 started
Worker 16010 started
Worker 16004 started
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A downside to using the native cluster module in Node.js is the amount of code you need to write to spawn and manage the workers, and it is not possible to modify the number of processes on the fly.&lt;/p&gt;

&lt;p&gt;For more robust management of Node.js clusters, use the &lt;a href="https://pm2.keymetrics.io/docs/usage/cluster-mode/"&gt;PM2&lt;/a&gt; process manager for Node.js. It uses the cluster module under the hood and takes care of spawning workers, stopping or restarting workers, and distributing the incoming load between the workers. It also provides some tools to help you monitor and tweak the performance of worker processes:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9QrAZeJA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2021-12/pm2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9QrAZeJA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2021-12/pm2.png" alt="PM2 in action" width="745" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Scale across Multiple Machines with a Load Balancer
&lt;/h2&gt;

&lt;p&gt;Scaling your Node.js application horizontally across multiple machines is similar to scaling across multiple cores on a single machine. As long as your application can run as an independent process, it can be distributed to run across several machines. The major requirement is using a load balancer to distribute incoming traffic to the servers (similar to how the cluster module is used to direct traffic to the child worker process). You can even have multiple load balancers that point to the same set of servers to avoid a single point of failure.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Utilize Worker Threads for CPU-intensive Tasks
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--37yLDDmd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2021-12/host-metrics.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--37yLDDmd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2021-12/host-metrics.png" alt="Monitor CPU usage and other host metrics with AppSignal" width="800" height="753"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Worker threads provide a mechanism to run CPU-intensive tasks in a Node.js application without blocking the main event loop. They were introduced in Node.js v10.5.0, and only became stable in the v12.0.0 release.&lt;/p&gt;

&lt;p&gt;A worker thread is spawned by the main or parent thread, and its responsibility is to perform a task in isolation from other workers. Unlike child processes or clusters, worker threads can share memory by transferring &lt;code&gt;ArrayBuffer&lt;/code&gt; instances or sharing &lt;code&gt;SharedArrayBuffer&lt;/code&gt; instances. A worker and their parent can also communicate in both directions using a message channel.&lt;/p&gt;

&lt;p&gt;Here's how to create a worker thread using the &lt;code&gt;worker_threads&lt;/code&gt; module from the standard library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// main.js&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;Worker&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;worker_threads&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Create a new worker&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./worker.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Listen for messages from worker&lt;/span&gt;
&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&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;result&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="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`The prime numbers between 2 and &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; are: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;primes&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="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;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;error&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="nx"&gt;log&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="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;exit&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;exitCode&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="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;exitCode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Send messages to the worker&lt;/span&gt;
&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When &lt;code&gt;main.js&lt;/code&gt; is executed, it spawns a new worker thread derived from the &lt;code&gt;worker.js&lt;/code&gt; file. The &lt;code&gt;postMessage()&lt;/code&gt; method sends messages to the worker, and a listener is used to detect responses from the worker. The &lt;code&gt;worker.js&lt;/code&gt; file is shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;worker_threads&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&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;data&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;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;primes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;getPrimes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;input&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;function&lt;/span&gt; &lt;span class="nx"&gt;getPrimes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;max&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;sieve&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="nx"&gt;primes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;sieve&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;primes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;sieve&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&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="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="nx"&gt;primes&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 above snippet, the &lt;code&gt;getPrimes()&lt;/code&gt; function is used to find all the prime numbers between 2 and the specified argument which is received from the parent through the &lt;code&gt;message&lt;/code&gt; listener. The result is also sent back to the parent by using the &lt;code&gt;postMessage()&lt;/code&gt; method as before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The prime numbers between 2 and 100 are: 2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97
The prime numbers between 2 and 50 are: 2,3,5,7,11,13,17,19,23,29,31,37,41,43,47
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read the &lt;a href="https://nodejs.org/api/worker_threads.html"&gt;official documentation of the &lt;code&gt;worker_threads&lt;/code&gt;&lt;br&gt;
module&lt;/a&gt; to learn more about using Node.js workers to your advantage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional Tips to Improve Node.js Performance
&lt;/h2&gt;

&lt;p&gt;Here are some micro-optimizations you can make in your Node.js application to reliably get better results:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Always use the latest release of Node.js to get the best possible performance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pay attention to your dependencies, and choose the most performant libraries where possible. Sometimes, it is better to forgo adding a dependency and instead write the code to perform a task yourself.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ensure that all independent I/O operations use asynchronous primitives like callbacks, promises, and async/await to ensure a non-blocking operation flow and improve downstream latency.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You don't have to optimize everything. Once the hotspots of your application are well optimized, &lt;em&gt;stop&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Your hot spots may change over time, so make sure to use some form of &lt;a href="https://appsignal.com/nodejs"&gt;observability or monitoring solution&lt;/a&gt; to track these changes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When working with large pieces of data, use &lt;a href="https://nodejs.org/api/stream.html"&gt;Node.js streams&lt;/a&gt; for optimal&lt;br&gt;
memory efficiency and reduced latency.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;To reduce the load on the garbage collector (thus reducing latency), avoid &lt;a href="https://blog.appsignal.com/2020/05/06/avoiding-memory-leaks-in-nodejs-best-practices-for-performance.html"&gt;memory allocations&lt;/a&gt; in hotspots.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Optimize your database queries, and scale them appropriately to ensure that they do not become a bottleneck.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Don't trade performance for reliability. Try to strike a balance between tuning your code for performance, the cost of development, and continued maintenance.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrap-up: Scale Your Node.js Application by Improving Performance
&lt;/h2&gt;

&lt;p&gt;In this article, we've covered some practical tips to help you scale your Node.js application to handle more traffic. Before implementing a specific optimization, ensure you run comprehensive performance tests on your system and use the insights you gain to determine what course of action to take. Also, use observability/monitoring tools so that you can see the impact of your changes and spot regressions quickly and reliably.&lt;/p&gt;

&lt;p&gt;If you have any additional tips regarding performance optimization in Node.js that were not discussed in this post, feel free to share them with me on &lt;a href="https://twitter.com/ayisaiah"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks for reading, and happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you liked this post, &lt;a href="https://blog.appsignal.com/javascript-sorcery"&gt;subscribe to our JavaScript Sorcery list&lt;/a&gt; for a monthly deep dive into more magical JavaScript tips and tricks.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.P.S. If you need an APM for your Node.js app, go and &lt;a href="https://www.appsignal.com/nodejs"&gt;check out the AppSignal APM for Node.js&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Our guest author Ayo is a Software Developer by trade. He enjoys writing about diverse technologies in web development, mainly in Go and JavaScript/TypeScript. You can learn more about him &lt;a href="https://freshman.tech/"&gt;through his blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>node</category>
    </item>
    <item>
      <title>A Guide to Load Testing Node.js APIs with Artillery</title>
      <dc:creator>Ayooluwa Isaiah</dc:creator>
      <pubDate>Wed, 24 Nov 2021 12:43:18 +0000</pubDate>
      <link>https://dev.to/appsignal/a-guide-to-load-testing-nodejs-apis-with-artillery-4he3</link>
      <guid>https://dev.to/appsignal/a-guide-to-load-testing-nodejs-apis-with-artillery-4he3</guid>
      <description>&lt;p&gt;&lt;a href="https://artillery.io/"&gt;Artillery&lt;/a&gt; is an open-source command-line tool purpose-built for load testing and smoke testing web applications. It is written in JavaScript and it supports testing HTTP, Socket.io, and WebSockets APIs.&lt;/p&gt;

&lt;p&gt;This article will get you started with load testing your Node.js APIs using Artillery. You'll be able to detect and fix critical performance issues before you deploy code to production.&lt;/p&gt;

&lt;p&gt;Before we dive in and set up Artillery for a Node.js app, though, let's first answer the question: what is load testing and why is it important?&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Should You Do Load Tests in Node.js?
&lt;/h2&gt;

&lt;p&gt;Load testing is essential to quantify system performance and identify breaking points at which an application starts to fail. A load test generally involves simulating user queries to a remote server.&lt;/p&gt;

&lt;p&gt;Load tests reproduce real-world workloads to measure how a system responds to a specified load volume over time. You can determine if a system behaves correctly under loads it is designed to handle and how adaptable it is to spikes in traffic. It is closely related to &lt;a href="https://www.blazemeter.com/blog/performance-testing-vs-load-testing-vs-stress-testing"&gt;stress testing&lt;/a&gt;, which assesses how a system behaves under extreme loads and if it can recover once traffic returns to normal levels.&lt;/p&gt;

&lt;p&gt;Load testing can help validate if an application can withstand realistic load scenarios without a degradation in performance. It can also help uncover issues like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Increased response times&lt;/li&gt;
&lt;li&gt;Memory leaks&lt;/li&gt;
&lt;li&gt;Poor performance of various system components under load&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As well as other design issues that contribute to a suboptimal user experience.&lt;/p&gt;

&lt;p&gt;In this article, we'll focus on the free and open-source version of Artillery to explore load testing. However, bear in mind that a &lt;a href="https://artillery.io/pro/"&gt;pro version of Artillery&lt;/a&gt; is also available for those whose needs exceed what can be achieved through the free version. It provides added features for testing at scale and is designed to be usable even if you don't have prior DevOps experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing Artillery for Node.js
&lt;/h2&gt;

&lt;p&gt;Artillery is an &lt;a href="https://www.npmjs.com/package/artillery"&gt;npm package&lt;/a&gt; so you can install it through &lt;code&gt;npm&lt;/code&gt; or &lt;code&gt;yarn&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;yarn global add artillery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this is successful, the &lt;code&gt;artillery&lt;/code&gt; program should be accessible from the command line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;artillery &lt;span class="nt"&gt;-V&lt;/span&gt;
        ___         __  _ ____                  _
  _____/   |  _____/ /_&lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; / /__  _______  __  &lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt;___  _____
 /____/ /| | / ___/ __/ / / / _ &lt;span class="se"&gt;\/&lt;/span&gt; ___/ / / / / / __ &lt;span class="se"&gt;\/&lt;/span&gt;____/
/____/ ___ |/ /  / /_/ / / /  __/ /  / /_/ / / / /_/ /____/
    /_/  |_/_/   &lt;span class="se"&gt;\_&lt;/span&gt;_/_/_/_/&lt;span class="se"&gt;\_&lt;/span&gt;__/_/   &lt;span class="se"&gt;\_&lt;/span&gt;_, &lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt;_/&lt;span class="se"&gt;\_&lt;/span&gt;___/
                                    /____/

&lt;span class="nt"&gt;------------&lt;/span&gt; Version Info &lt;span class="nt"&gt;------------&lt;/span&gt;
Artillery: 1.7.7
Artillery Pro: not installed &lt;span class="o"&gt;(&lt;/span&gt;https://artillery.io/pro&lt;span class="o"&gt;)&lt;/span&gt;
Node.js: v16.7.0
OS: linux/x64
&lt;span class="nt"&gt;--------------------------------------&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Basic Artillery Usage
&lt;/h2&gt;

&lt;p&gt;Once you've installed the Artillery CLI, you can start using it to send traffic to a web server. It provides a &lt;code&gt;quick&lt;/code&gt; subcommand that lets you run a test without writing a test script first.&lt;/p&gt;

&lt;p&gt;You'll need to specify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an endpoint&lt;/li&gt;
&lt;li&gt;the rate of virtual users per second or a fixed amount of virtual users&lt;/li&gt;
&lt;li&gt;how many requests should be made per user
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;artillery quick &lt;span class="nt"&gt;--count&lt;/span&gt; 20 &lt;span class="nt"&gt;--num&lt;/span&gt; 10 http://localhost:4000/example
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--count&lt;/code&gt; parameter above specifies the total number of virtual users, while &lt;code&gt;--num&lt;/code&gt; indicates the number of requests that should be made per user. Therefore, 200 (20*10) GET requests are sent to the specified endpoint. On successful completion of the test, a report is printed out to the console.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;All virtual &lt;span class="nb"&gt;users &lt;/span&gt;finished
Summary report @ 14:46:26&lt;span class="o"&gt;(&lt;/span&gt;+0100&lt;span class="o"&gt;)&lt;/span&gt; 2021-08-29
  Scenarios launched:  20
  Scenarios completed: 20
  Requests completed:  200
  Mean response/sec: 136.99
  Response &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;msec&lt;span class="o"&gt;)&lt;/span&gt;:
    min: 0
    max: 2
    median: 1
    p95: 1
    p99: 2
  Scenario counts:
    0: 20 &lt;span class="o"&gt;(&lt;/span&gt;100%&lt;span class="o"&gt;)&lt;/span&gt;
  Codes:
    200: 200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This shows several details about the test run, such as the requests completed, response times, time taken for the test, and more. It also displays the response codes received on each request so that you can determine if your API handles failures gracefully in cases of overload.&lt;/p&gt;

&lt;p&gt;While the &lt;code&gt;quick&lt;/code&gt; subcommand is handy for performing one-off tests from the command line, it's quite limited in what it can achieve. That's why Artillery provides a way to configure different load testing scenarios through test definition files in YAML or JSON formats. This allows great flexibility to simulate the expected flows at one or more of your application's endpoints.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing Your First Artillery Test Script
&lt;/h2&gt;

&lt;p&gt;In this section, I'll demonstrate a basic test configuration that you can apply to any application. If you want to follow along, you can set up a test environment for your project, or run the tests locally so that your production environment is not affected. Ensure you install Artillery as a development dependency so that the version you use is consistent across all deployments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;yarn add &lt;span class="nt"&gt;-D&lt;/span&gt; artillery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An Artillery test script consists of two main sections: &lt;code&gt;config&lt;/code&gt; and &lt;code&gt;scenarios&lt;/code&gt;. &lt;code&gt;config&lt;/code&gt; includes the general configuration settings for the test such as the target, response timeouts, default HTTP headers, etc. &lt;code&gt;scenarios&lt;/code&gt; consist of the various requests that virtual users should make during a test. Here's a script that tests an endpoint by sending 10 virtual users every second for 30 seconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:4000"&lt;/span&gt;
  &lt;span class="na"&gt;phases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
      &lt;span class="na"&gt;arrivalRate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;

&lt;span class="na"&gt;scenarios&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Retrieve&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;data"&lt;/span&gt;
    &lt;span class="na"&gt;flow&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/example"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above script, the &lt;code&gt;config&lt;/code&gt; section defines the base URL for the application that's being tested in the &lt;code&gt;target&lt;/code&gt; property. All the endpoints defined later in the script will run against this base URL.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;phases&lt;/code&gt; property is then used to set up the number of virtual users generated in a period of time and how frequently these users are sent to specified endpoints.&lt;/p&gt;

&lt;p&gt;In this test, &lt;code&gt;duration&lt;/code&gt; determines that virtual users will be generated for 30 seconds and &lt;code&gt;arrivalRate&lt;/code&gt; determines the number of virtual users sent to the endpoints per second (10 users).&lt;/p&gt;

&lt;p&gt;On the other hand, the &lt;code&gt;scenarios&lt;/code&gt; section defines the various operations that a virtual user should perform. This is controlled through the &lt;code&gt;flow&lt;/code&gt; property, which specifies the exact steps that should be executed in order. In this case, we have a single step: a GET request to the &lt;code&gt;/example&lt;/code&gt; endpoint on the base URL. Every virtual user that Artillery generates will make this request.&lt;/p&gt;

&lt;p&gt;Now that we've written our first script, let's dive into how to run a load test.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running a Load Test in Artillery
&lt;/h2&gt;

&lt;p&gt;Save your test script to a file (such as &lt;code&gt;load-test.yml&lt;/code&gt;) and execute it through the command below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;artillery run path/to/script.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will start sending virtual users to the specified endpoints at a rate of 10 requests per second. A report will be printed to the console every 10 seconds, informing you of the number of test scenarios launched and completed within the time period, and other statistics such as mean response time, HTTP response codes, and errors (if any).&lt;/p&gt;

&lt;p&gt;Once the test concludes, a summary report (identical to the one we examined earlier) is printed out before the command exits.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;All virtual &lt;span class="nb"&gt;users &lt;/span&gt;finished
Summary report @ 15:38:48&lt;span class="o"&gt;(&lt;/span&gt;+0100&lt;span class="o"&gt;)&lt;/span&gt; 2021-09-02
  Scenarios launched:  300
  Scenarios completed: 300
  Requests completed:  300
  Mean response/sec: 9.87
  Response &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;msec&lt;span class="o"&gt;)&lt;/span&gt;:
    min: 0
    max: 1459
    median: 1
    p95: 549.5
    p99: 1370
  Scenario counts:
    Retrieve data: 300 &lt;span class="o"&gt;(&lt;/span&gt;100%&lt;span class="o"&gt;)&lt;/span&gt;
  Codes:
    200: 300
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to Create Realistic User Flows
&lt;/h2&gt;

&lt;p&gt;The test script we executed in the previous section is not very different from the &lt;code&gt;quick&lt;/code&gt; example in that it makes requests to only a single endpoint. However, you can use Artillery to test more complex user flows in an application.&lt;/p&gt;

&lt;p&gt;In a SaaS product, for example, a user flow could be: someone lands on your homepage, checks out the pricing page, and then signs up for a free trial. You'll definitely want to find out how this flow will perform under stress if hundreds or thousands of users are trying to perform these actions at the same time.&lt;/p&gt;

&lt;p&gt;Here's how you can define such a user flow in an Artillery test script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:4000"&lt;/span&gt;
  &lt;span class="na"&gt;phases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;
      &lt;span class="na"&gt;arrivalRate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Warming&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;up"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;240&lt;/span&gt;
      &lt;span class="na"&gt;arrivalRate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;
      &lt;span class="na"&gt;rampTo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Ramping&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;up"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;500&lt;/span&gt;
      &lt;span class="na"&gt;arrivalRate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sustained&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;load"&lt;/span&gt;
  &lt;span class="na"&gt;processor&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./processor.js"&lt;/span&gt;

&lt;span class="na"&gt;scenarios&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sign&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;up&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;flow"&lt;/span&gt;
    &lt;span class="na"&gt;flow&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;think&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/pricing"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;think&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/signup"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;think&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;post&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/signup"&lt;/span&gt;
          &lt;span class="na"&gt;beforeRequest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;generateSignupData&lt;/span&gt;
          &lt;span class="na"&gt;json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
            &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above script, we define three test phases in &lt;code&gt;config.phases&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The first phase sends 20 virtual users per second to the application for 60 seconds.&lt;/li&gt;
&lt;li&gt;In the second phase, the load will start at 20 users per second and gradually increase to 100 users per second over 240 seconds.&lt;/li&gt;
&lt;li&gt;The third and final phase simulates a sustained load of 100 users per second for 500 seconds.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By providing several phases, you can accurately simulate real-world traffic patterns and test how adaptable your system is to a sudden barrage of requests.&lt;/p&gt;

&lt;p&gt;The steps that each virtual user takes in the application are under &lt;code&gt;scenarios.flow&lt;/code&gt;. The first request is &lt;code&gt;GET /&lt;/code&gt; which leads to the homepage. Afterward, there is a pause for 1 second (configured with &lt;code&gt;think&lt;/code&gt;) to simulate user scrolling or reading before making the next GET request to &lt;code&gt;/pricing&lt;/code&gt;. After a further delay of 2 seconds, the virtual user makes a GET request to &lt;code&gt;/signup&lt;/code&gt;. The last request is &lt;code&gt;POST /signup&lt;/code&gt;, which sends a JSON payload in the request body.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;{{ email }}&lt;/code&gt; and &lt;code&gt;{{ password }}&lt;/code&gt; placeholders are populated through the &lt;code&gt;generateSignupData&lt;/code&gt; function, which executes before the request is made. This function is defined in the &lt;code&gt;processor.js&lt;/code&gt; file referenced in &lt;code&gt;config.processor&lt;/code&gt;. In this way, Artillery lets you specify custom hooks to execute at specific points during a test run. Here are the contents of &lt;code&gt;processor.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Faker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;faker&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;generateSignupData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requestParams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ee&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="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Faker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;internet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exampleEmail&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Faker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;internet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&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="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;generateSignupData&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;The &lt;code&gt;generateSignupData&lt;/code&gt; function uses methods provided by &lt;a href="https://github.com/marak/Faker.js/"&gt;Faker.js&lt;/a&gt; to generate a random email address and password each time it is called. The results are then set on the virtual user's context, and &lt;code&gt;next()&lt;/code&gt; is called so that the scenario can continue to execute. You can use this approach to inject dynamic random content into your tests so they're as close as possible to real-world requests.&lt;/p&gt;

&lt;p&gt;Note that other &lt;a href="https://artillery.io/docs/guides/guides/http-reference.html#Function-signatures"&gt;hooks&lt;/a&gt; are available aside from &lt;code&gt;beforeRequest&lt;/code&gt;, including the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;afterResponse&lt;/code&gt; - Executes one or more functions after a response has been received from the
endpoint:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;post&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/login"&lt;/span&gt;
    &lt;span class="na"&gt;afterResponse&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;logHeaders"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;logBody"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;beforeScenario&lt;/code&gt; and &lt;code&gt;afterScenario&lt;/code&gt; - Used to execute one or more functions before or after each request in a scenario:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;scenarios&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;beforeScenario&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;setData"&lt;/span&gt;
    &lt;span class="na"&gt;afterScenario&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;logResults"&lt;/span&gt;
    &lt;span class="na"&gt;flow&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/auth"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;function&lt;/code&gt; - Can execute functions at any point in a scenario:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;post&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/login"&lt;/span&gt;
    &lt;span class="na"&gt;function&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;doSomething"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Injecting Data from a Payload File
&lt;/h2&gt;

&lt;p&gt;Artillery also lets you inject custom data through a payload file in CSV format. For example, instead of generating fake email addresses and passwords on the fly as we did in the previous section, you can have a predefined list of such data in a CSV file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dovie32@example.net,rwkWspKUKy
Allen.Fay@example.org,7BaFHbaWga
Jany30@example.org,CWvc6Bznnh
Dorris47@example.com,1vlT_02i6h
Imani.Spencer21@example.net,1N0PRraQU7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To access the data in this file, you need to reference it in the test script through the &lt;code&gt;config.payload.path&lt;/code&gt; property. Secondly, you need to specify the names of the fields you'd like to access through &lt;code&gt;config.payload.fields&lt;/code&gt;. The &lt;a href="https://artillery.io/docs/guides/guides/test-script-reference.html#Payload-file-options"&gt;&lt;code&gt;config.payload&lt;/code&gt; property provides several other options&lt;/a&gt; to configure its behavior, and it's also possible to specify multiple payload files in a single script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:4000"&lt;/span&gt;
  &lt;span class="na"&gt;phases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;
      &lt;span class="na"&gt;arrivalRate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;
  &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./auth.csv"&lt;/span&gt;
    &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password"&lt;/span&gt;

&lt;span class="na"&gt;scenarios&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authenticating&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;users"&lt;/span&gt;
    &lt;span class="na"&gt;flow&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;post&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/login"&lt;/span&gt;
          &lt;span class="na"&gt;json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
            &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Capturing Response Data From an Endpoint
&lt;/h2&gt;

&lt;p&gt;Artillery makes it easy to capture the response of a request and reuse certain fields in a subsequent request. This is helpful if you're simulating flows with requests that depend on an earlier action's execution.&lt;/p&gt;

&lt;p&gt;Let's assume you're providing a geocoding API that accepts the name of a place and returns its longitude and latitude in the following format:&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;"longitude"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;-73.935242&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"latitude"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;40.730610&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;p&gt;You can populate a CSV file with a list of cities:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Seattle
London
Paris
Monaco
Milan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's how you can configure Artillery to use each city's longitude and latitude values in another request. For example, you can use the values to retrieve the current weather through another endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:4000"&lt;/span&gt;
  &lt;span class="na"&gt;phases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;
      &lt;span class="na"&gt;arrivalRate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;
  &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./cities.csv"&lt;/span&gt;
    &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;city"&lt;/span&gt;

&lt;span class="na"&gt;scenarios&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;flow&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/geocode?city={{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;city&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;capture&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$.longitude"&lt;/span&gt;
              &lt;span class="na"&gt;as&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lon"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$.latitude"&lt;/span&gt;
              &lt;span class="na"&gt;as&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lat"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/weather?lon={{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;lon&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}&amp;amp;lat={{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;lat&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;capture&lt;/code&gt; property above is where all the magic happens. It's where you can access the JSON response of a request and store it in a variable to reuse in subsequent requests. The &lt;code&gt;longitude&lt;/code&gt; and &lt;code&gt;latitude&lt;/code&gt; properties from the &lt;code&gt;/geocode&lt;/code&gt; response body (with the aliases &lt;code&gt;lon&lt;/code&gt; and &lt;code&gt;lat&lt;/code&gt;, respectively) are then passed on as query parameters to the &lt;code&gt;/weather&lt;/code&gt; endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Artillery in a CI/CD Environment
&lt;/h2&gt;

&lt;p&gt;An obvious place to run your load testing scripts is in a CI/CD pipeline so that your application is put through its paces before being deployed to production.&lt;/p&gt;

&lt;p&gt;When using Artillery in such environments, it's necessary to set failure conditions that cause the program to exit with a non-zero code. Your deployment should abort if performance objectives are not met. Artillery provides support for this use case through its &lt;code&gt;config.ensure&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;Here's an example that uses the &lt;code&gt;ensure&lt;/code&gt; setting to assert that 99% of all requests have an aggregate response time of 150 milliseconds or less and that 1% or less of all requests are allowed to fail:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://example.com"&lt;/span&gt;
  &lt;span class="na"&gt;phases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;
      &lt;span class="na"&gt;arrivalRate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;
  &lt;span class="na"&gt;ensure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;p99&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;150&lt;/span&gt;
    &lt;span class="na"&gt;maxErrorRate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you run the test, it will continue as before, except that assertions are verified at the end of the test and cause the program to exit with a non-zero exit code if requirements are not met. The reason for a test failure is printed at the bottom of the summary report.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;All virtual users finished
Summary report @ 07:45:48(+0100) 2021-09-03
  Scenarios launched:  10
  Scenarios completed: 10
  Requests completed:  20
  Mean response/sec: 4
  Response time (msec):
    min: 1
    max: 487
    median: 2
    p95: 443.5
    p99: 487
  Scenario counts:
    0: 10 (100%)
  Codes:
    200: 20

ensure condition failed: ensure.p99 &amp;lt; 200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aside from checking the aggregate latency, you can also run assertions on &lt;code&gt;min&lt;/code&gt;, &lt;code&gt;max&lt;/code&gt;, and &lt;code&gt;median&lt;/code&gt; — the minimum, maximum, and median response times, respectively. Here's how to assert that requests never take more than 500 milliseconds to complete during a test run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ensure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;500&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The report for a failed test will indicate the reason for failure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;All virtual users finished&lt;/span&gt;
&lt;span class="s"&gt;Summary report @ 08:29:59(+0100) 2021-09-03&lt;/span&gt;
  &lt;span class="s"&gt;Scenarios launched&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;  &lt;span class="m"&gt;10&lt;/span&gt;
  &lt;span class="na"&gt;Scenarios completed&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
  &lt;span class="na"&gt;Requests completed&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  &lt;span class="m"&gt;20&lt;/span&gt;
  &lt;span class="na"&gt;Mean response/sec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3.64&lt;/span&gt;
  &lt;span class="na"&gt;Response time (msec)&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;min&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
    &lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;603&lt;/span&gt;
    &lt;span class="na"&gt;median&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;305.5&lt;/span&gt;
    &lt;span class="na"&gt;p95&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;602.5&lt;/span&gt;
    &lt;span class="na"&gt;p99&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;603&lt;/span&gt;
  &lt;span class="na"&gt;Scenario counts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;0&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10 (100%)&lt;/span&gt;
  &lt;span class="na"&gt;Codes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;200&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;

&lt;span class="na"&gt;ensure condition failed&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ensure.max &amp;lt; &lt;/span&gt;&lt;span class="m"&gt;500&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Generating Status Reports in Artillery
&lt;/h2&gt;

&lt;p&gt;Artillery prints a summary report for each test run to the standard output, but it's also possible to output detailed statistics for a test run into a JSON file by utilizing the &lt;code&gt;--output&lt;/code&gt; flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;artillery run config.yml &lt;span class="nt"&gt;--output&lt;/span&gt; test.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the test completes, its report is placed in a &lt;code&gt;test.json&lt;/code&gt; file in the current working directory. This JSON file can be visualized through &lt;a href="https://reportviewer.artillery.io/"&gt;Artillery's online report viewer&lt;/a&gt; or converted into an HTML report through the &lt;code&gt;report&lt;/code&gt; subcommand:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;artillery report &lt;span class="nt"&gt;--output&lt;/span&gt; report.html test.json
Report generated: report.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can open the &lt;code&gt;report.html&lt;/code&gt; file in your browser to view a full report of the test run. It includes tables and several charts that should give you a good idea of how your application performed under load:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ayhjpknv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2021-11/artillery-report.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ayhjpknv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2021-11/artillery-report.png" alt="Artillery HTML report" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Extending Artillery With Plugins
&lt;/h2&gt;

&lt;p&gt;Artillery's built-in tools for testing HTTP, Socket.io, and Websocket APIs can take you quite far in your load testing process. However, if you have additional requirements, you can &lt;a href="https://www.npmjs.com/search?q=artillery-plugin-"&gt;search for plugins on NPM&lt;/a&gt; to extend Artillery's functionality.&lt;/p&gt;

&lt;p&gt;Here are some official Artillery plugins that you might want to check out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://artillery.io/docs/guides/plugins/plugin-expectations-assertions.html"&gt;artillery-plugin-expect&lt;/a&gt;: Helps with adding expectations to HTTP requests for functional or acceptance testing.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://artillery.io/docs/guides/plugins/plugin-expectations-assertions.html#Metrics"&gt;artillery-plugin-publish-metrics&lt;/a&gt;: Used to send statistics from test runs to some external monitoring and observability systems.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://artillery.io/docs/guides/plugins/plugin-fuzzer.html"&gt;artillery-plugin-fuzzer&lt;/a&gt;: Helps you &lt;a href="https://www.contrastsecurity.com/knowledge-hub/glossary/fuzz-testing"&gt;fuzz test&lt;/a&gt; your APIs with random and unexpected payloads to your API endpoints so you can catch errors. It is based on the &lt;a href="https://github.com/minimaxir/big-list-of-naughty-strings/"&gt;Big List Of Naughty Strings&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://artillery.io/docs/guides/plugins/plugin-metrics-by-endpoint.html"&gt;artillery-plugin-metrics-by-endpoint&lt;/a&gt;: Breaks down response time metrics by endpoint rather than displaying aggregate values across all endpoints.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also &lt;a href="https://artillery.io/blog/extend-artillery-by-creating-your-own-plugins/"&gt;extend Artillery by creating your own plugins&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Artillery for Node.js Apps to Avoid Downtime
&lt;/h2&gt;

&lt;p&gt;In this article, we've described how you can set up a load testing workflow for your Node.js applications with Artillery. This setup will ensure that your application performance stays predictable under various traffic conditions. You'll be able to account well for traffic-heavy periods and avoid downtime, even when faced with a sudden influx of users.&lt;/p&gt;

&lt;p&gt;We've covered a sizeable chunk of what Artillery can do for you, but there's still lots more to discover. &lt;/p&gt;

&lt;p&gt;Ensure you read the &lt;a href="https://artillery.io/docs/guides/overview/welcome.html"&gt;Artillery official documentation&lt;/a&gt; to learn about the other features on offer.&lt;/p&gt;

&lt;p&gt;Thanks for reading, and happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you liked this post, &lt;a href="https://blog.appsignal.com/javascript-sorcery"&gt;subscribe to our JavaScript Sorcery list&lt;/a&gt; for a monthly deep dive into more magical JavaScript tips and tricks.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.P.S. If you need an APM for your Node.js app, go and &lt;a href="https://www.appsignal.com/nodejs"&gt;check out the AppSignal APM for Node.js&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Ayo is a Software Developer by trade. He enjoys writing about diverse technologies in web development, mainly in Go and JavaScript/TypeScript. You can learn more about him &lt;a href="https://freshman.tech/"&gt;through his blog&lt;/a&gt;.&lt;/em&gt; &lt;/p&gt;

</description>
      <category>node</category>
    </item>
    <item>
      <title>What's New in Node.js 17</title>
      <dc:creator>Ayooluwa Isaiah</dc:creator>
      <pubDate>Wed, 17 Nov 2021 13:00:17 +0000</pubDate>
      <link>https://dev.to/appsignal/whats-new-in-nodejs-17-40ih</link>
      <guid>https://dev.to/appsignal/whats-new-in-nodejs-17-40ih</guid>
      <description>&lt;p&gt;&lt;a href="https://nodejs.org/en/blog/release/v17.0.0/"&gt;Node.js v17.0.0&lt;/a&gt;, the latest major version of the popular JavaScript runtime, has just been released. It supersedes v16 in the &lt;a href="https://nodejs.org/en/download/current/"&gt;Current release line&lt;/a&gt; of the runtime. V16 is now in line to be promoted to the &lt;a href="https://nodejs.org/en/about/releases/"&gt;long-term support&lt;/a&gt; (LTS) channel on October 26, 2021, as it's an even-numbered release.&lt;/p&gt;

&lt;p&gt;Despite being a relatively minor update, this release brings several refinements to the runtime, including more promisified APIs, JavaScript engine upgrades, and OpenSSL 3.0 support.&lt;/p&gt;

&lt;p&gt;In this article, we'll take a look at some of the major highlights from this release and the implications for Node.js developers.&lt;/p&gt;

&lt;h2&gt;
  
  
  New Promise-based APIs
&lt;/h2&gt;

&lt;p&gt;Node.js continues to promisify its core APIs as part of its &lt;a href="https://github.com/nodejs/node/blob/master/doc/guides/strategic-initiatives.md"&gt;strategic initiative&lt;/a&gt; plan. In the last few major Node.js releases, Promise-based APIs were added for the &lt;code&gt;dns&lt;/code&gt;, &lt;code&gt;fs&lt;/code&gt;, &lt;code&gt;stream&lt;/code&gt;, and &lt;code&gt;timers&lt;/code&gt; modules.&lt;/p&gt;

&lt;p&gt;In Node.js 17, this ongoing promisification work has been extended to the &lt;code&gt;readline&lt;/code&gt; module, primarily used for accepting input from the command line. The new APIs are accessible via the &lt;code&gt;readline/promises&lt;/code&gt; module.&lt;/p&gt;

&lt;p&gt;The old way of utilizing the &lt;code&gt;readline&lt;/code&gt; module in Node.js v16 and earlier involved using callback functions, as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// main.mjs&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;readline&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;readline&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="nx"&gt;process&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;process&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;rl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;readline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createInterface&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;rl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;question&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`What's your name?`&lt;/span&gt;&lt;span class="p"&gt;,&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="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="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Hi &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;!`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;rl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;close&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;As of Node.js 17, you can now use &lt;code&gt;await&lt;/code&gt; when importing from&lt;br&gt;
&lt;code&gt;readline/promises&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// main.mjs&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;readline&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;readline/promises&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="nx"&gt;process&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;process&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;rl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;readline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createInterface&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;rl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;question&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`What's your name?`&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="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Hi &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;!`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;rl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Stack Traces Now Include the Version of Node.js
&lt;/h2&gt;

&lt;p&gt;When diagnosing a reported issue, a common question is: what version of Node.js executed the program?&lt;/p&gt;

&lt;p&gt;Node.js 17 makes it easier to provide this information by including the version number at the end of a stack trace whenever an uncaught exception causes the process to exit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;file:///home/ayo/dev/demo/main.mjs:1
throw new Error&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Uncaught exception"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      ^

Error: Uncaught exception
    at file:///home/ayo/dev/demo/main.mjs:1:7
    at ModuleJob.run &lt;span class="o"&gt;(&lt;/span&gt;node:internal/modules/esm/module_job:185:25&lt;span class="o"&gt;)&lt;/span&gt;
    at async Promise.all &lt;span class="o"&gt;(&lt;/span&gt;index 0&lt;span class="o"&gt;)&lt;/span&gt;
    at async ESMLoader.import &lt;span class="o"&gt;(&lt;/span&gt;node:internal/modules/esm/loader:281:24&lt;span class="o"&gt;)&lt;/span&gt;
    at async loadESM &lt;span class="o"&gt;(&lt;/span&gt;node:internal/process/esm_loader:88:5&lt;span class="o"&gt;)&lt;/span&gt;
    at async handleMainPromise &lt;span class="o"&gt;(&lt;/span&gt;node:internal/modules/run_main:65:12&lt;span class="o"&gt;)&lt;/span&gt;

Node.js v17.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you'd rather omit this information in your program's stack trace, you can use the &lt;code&gt;--no-extra-info-on-fatal-exception&lt;/code&gt; command-line flag when starting your Node.js scripts.&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenSSL 3.0 Support
&lt;/h2&gt;

&lt;p&gt;Node.js 17 now supports the newly unveiled &lt;a href="https://www.openssl.org/blog/blog/2021/09/07/OpenSSL3.Final/"&gt;OpenSSL 3.0&lt;/a&gt; release.&lt;/p&gt;

&lt;p&gt;The aim is for the APIs in OpenSSL 3.0 to be compatible with those provided in previous OpenSSL versions. However, stricter restrictions on the allowable key sizes and algorithms mean there might be some ecosystem impact — especially for users still using small keys or older algorithms.&lt;/p&gt;

&lt;p&gt;This impact is reflected in the error message &lt;code&gt;ERR_OSSL_EVP_UNSUPPORTED&lt;/code&gt; in Node.js 17 when your application or its dependencies uses an algorithm or key size that is not allowed in OpenSSL 3.0.&lt;/p&gt;

&lt;p&gt;You can use the &lt;code&gt;--openssl-legacy-provider&lt;/code&gt; command-line flag to enable the OpenSSL 3.0 legacy provider as a temporary way to ease these restrictions.&lt;/p&gt;

&lt;h2&gt;
  
  
  V8 Is Upgraded to v9.5
&lt;/h2&gt;

&lt;p&gt;As of Node.js 17, the v8 JavaScript engine has been updated to &lt;a href="https://v8.dev/blog/v8-release-95"&gt;v9.5&lt;/a&gt;. The changes in this release are primarily aimed at expanding internationalization for dates and calendars as well as for the output of time zones. It also implements the &lt;a href="https://github.com/WebAssembly/exception-handling/blob/master/proposals/exception-handling/Exceptions.md"&gt;WebAssembly Exception Handling proposal&lt;/a&gt;, designed to reduce overhead compared to current JavaScript-based workarounds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deprecations and Removals
&lt;/h2&gt;

&lt;p&gt;As a major release, Node.js 17 also comes with a few &lt;a href="https://nodejs.org/en/blog/release/v17.0.0/#deprecations-and-removals"&gt;deprecations and removals&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A notable one is the deprecation of trailing slash pattern mappings which is unsupported in the import maps specification.&lt;/p&gt;

&lt;h2&gt;
  
  
  Upgrading to Node.js 17
&lt;/h2&gt;

&lt;p&gt;You can download Node.js 17 to your computer using the appropriate link for your operating system and architecture provided on the &lt;a href="https://nodejs.org/en/download/current/"&gt;Node.js download page&lt;/a&gt;. A better way to manage Node.js releases on your machine is to use a Node.js environment management tool like &lt;a href="https://volta.sh/"&gt;Volta&lt;/a&gt;, which allows you to install and switch between multiple Node.js versions seamlessly.&lt;/p&gt;

&lt;p&gt;After &lt;a href="https://docs.volta.sh/guide/getting-started"&gt;installing the Volta CLI&lt;/a&gt;, run the command below to install the latest version of Node.js:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;volta &lt;span class="nb"&gt;install &lt;/span&gt;node@latest
success: installed and &lt;span class="nb"&gt;set &lt;/span&gt;node@17.0.1 &lt;span class="o"&gt;(&lt;/span&gt;with npm@8.1.0&lt;span class="o"&gt;)&lt;/span&gt; as default
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also install specific versions using the syntax below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;volta &lt;span class="nb"&gt;install &lt;/span&gt;node@lts &lt;span class="c"&gt;# install latest lts version&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;volta &lt;span class="nb"&gt;install &lt;/span&gt;node@16.9.0 &lt;span class="c"&gt;# install specific version 16.9.0&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;volta &lt;span class="nb"&gt;install &lt;/span&gt;node@12 &lt;span class="c"&gt;# install the latest v12 release&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you install a Node.js release with Volta, it will co-exist with any other versions that you have already installed.&lt;/p&gt;

&lt;p&gt;You can list the Node runtime versions in your toolchain using the command below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;volta list node
⚡️ Node runtimes &lt;span class="k"&gt;in &lt;/span&gt;your toolchain:

  v14.8.0
  v14.17.5
  v16.7.0
  v16.8.0
  v16.9.0
  v17.0.1 &lt;span class="o"&gt;(&lt;/span&gt;default&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please see &lt;a href="https://docs.volta.sh/guide/"&gt;Volta's online documentation&lt;/a&gt; for more details on how it works and what it can do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;You can examine the complete list of bug fixes, new features, and other changes included in this release in the &lt;a href="https://nodejs.org/en/blog/release/v17.0.0/"&gt;official Node.js v17 release&lt;br&gt;
notes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To find out more about the Node.js project and how you can contribute, you can check out the list of &lt;a href="https://github.com/nodejs/node/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc"&gt;Node.js open issues&lt;/a&gt; and &lt;a href="https://github.com/nodejs/node/blob/master/CONTRIBUTING.md"&gt;Node.js contribution guidelines&lt;/a&gt; on the project's GitHub repository.&lt;/p&gt;

&lt;p&gt;Thanks for reading, and happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you liked this post, &lt;a href="https://blog.appsignal.com/javascript-sorcery"&gt;subscribe to our JavaScript Sorcery list&lt;/a&gt; for a monthly deep dive into more magical JavaScript tips and tricks.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.P.S. If you need an APM for your Node.js app, go and &lt;a href="https://www.appsignal.com/nodejs"&gt;check out the AppSignal APM for Node.js&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Ayo is a Software Developer by trade. He enjoys writing about diverse technologies in web development, mainly in Go and JavaScript/TypeScript. You can learn more about him &lt;a href="https://freshman.tech/"&gt;through his blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>node</category>
    </item>
    <item>
      <title>How to Dockerize an Existing Node.js Application</title>
      <dc:creator>Ayooluwa Isaiah</dc:creator>
      <pubDate>Tue, 26 Oct 2021 12:42:45 +0000</pubDate>
      <link>https://dev.to/appsignal/how-to-dockerize-an-existing-nodejs-application-1ng2</link>
      <guid>https://dev.to/appsignal/how-to-dockerize-an-existing-nodejs-application-1ng2</guid>
      <description>&lt;p&gt;&lt;a href="https://docs.docker.com/get-started/overview/"&gt;Docker&lt;/a&gt; is a software platform that enables packaging an application into containers. These containers represent isolated environments that provide everything necessary to run the application. Dockerizing an application refers to packaging it in a Docker image to run in one or more containers.&lt;/p&gt;

&lt;p&gt;Dockerizing an application involves specifying everything needed to run the application in a Dockerfile and then using the file to build a specialized Docker image that can be shared to multiple machines. A Docker image is a reproducible environment for the application that guarantees portability across machines.&lt;/p&gt;

&lt;p&gt;In this tutorial, you'll learn the process of Dockerizing an existing Node.js application from scratch. We'll cover topics such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What the Dockerfile represents&lt;/li&gt;
&lt;li&gt;Sharing Docker images to multiple machines&lt;/li&gt;
&lt;li&gt;The basics of Docker Compose for orchestrating multi-container applications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After reading this article, you should be armed with enough knowledge to Dockerize your own applications, even if they're built with some other technology.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up a Demo Node.js Application
&lt;/h2&gt;

&lt;p&gt;To demonstrate the concepts discussed in this article, we'll use a &lt;a href="https://github.com/finallyayo/covid-node"&gt;demo Node.js application&lt;/a&gt; that provides an endpoint for the retrieval of Covid-19 statistics. It uses the free API provided by &lt;a href="https://disease.sh/docs/"&gt;disease.sh&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;You can clone its GitHub repository to your computer using the command below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git clone https://github.com/finallyayo/covid-node
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once downloaded, &lt;code&gt;cd&lt;/code&gt; into the project folder and run &lt;code&gt;yarn&lt;/code&gt; to install its dependencies. Afterward, open up the &lt;code&gt;app.js&lt;/code&gt; file in your text editor. You should see the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fastify&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fastify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)({&lt;/span&gt;
  &lt;span class="na"&gt;logger&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;got&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;got&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;NodeCache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node-cache&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;appCache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;NodeCache&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;fastify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/covid&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="kd"&gt;function&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="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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;covidAllStats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;appCache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;covidAllStats&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;covidAllStats&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&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;got&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://disease.sh/v3/covid-19/all&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;covidAllStats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="nx"&gt;appCache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;covidAllStats&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;covidAllStats&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&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="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&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;application/json; charset=utf-8&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;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;covidAllStats&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;fastify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&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;fastify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0.0.0.0&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;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;address&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;fastify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;fastify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`server listening on &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;address&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This application provides a single endpoint (&lt;code&gt;/covid&lt;/code&gt;) that returns the aggregated global Covid-19 totals to date. Once retrieved from the API, the data is subsequently cached in-memory for 10 minutes.&lt;/p&gt;

&lt;p&gt;Specifying &lt;code&gt;'0.0.0.0'&lt;/code&gt; as the address is essential when deploying to Docker because Docker containers do not default to exposing mapped ports to &lt;code&gt;localhost&lt;/code&gt;. If this address is missing, your application might be inaccessible despite starting successfully in the container.&lt;/p&gt;

&lt;p&gt;Go ahead and start the server with &lt;code&gt;yarn dev&lt;/code&gt;, then make a GET request to the &lt;code&gt;/covid&lt;/code&gt; endpoint with &lt;code&gt;curl&lt;/code&gt; or some other tool. You should see a JSON response similar to the output shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://localhost:4000/covid
&lt;span class="s2"&gt;"updated"&lt;/span&gt;:1629986413872,&lt;span class="s2"&gt;"cases"&lt;/span&gt;:214977601,&lt;span class="s2"&gt;"todayCases"&lt;/span&gt;:270792,&lt;span class="s2"&gt;"deaths"&lt;/span&gt;:4481152,&lt;span class="s2"&gt;"todayDeaths"&lt;/span&gt;:5588,&lt;span class="s2"&gt;"recovered"&lt;/span&gt;:192301169,&lt;span class="s2"&gt;"todayRecovered"&lt;/span&gt;:273952,&lt;span class="s2"&gt;"active"&lt;/span&gt;:18195280,&lt;span class="s2"&gt;"critical"&lt;/span&gt;:112761,&lt;span class="s2"&gt;"casesPerOneMillion"&lt;/span&gt;:27580,&lt;span class="s2"&gt;"deathsPerOneMillion"&lt;/span&gt;:574.9,&lt;span class="s2"&gt;"tests"&lt;/span&gt;:3264569720,&lt;span class="s2"&gt;"testsPerOneMillion"&lt;/span&gt;:416082.42,&lt;span class="s2"&gt;"population"&lt;/span&gt;:7845968850,&lt;span class="s2"&gt;"oneCasePerPeople"&lt;/span&gt;:0,&lt;span class="s2"&gt;"oneDeathPerPeople"&lt;/span&gt;:0,&lt;span class="s2"&gt;"oneTestPerPeople"&lt;/span&gt;:0,&lt;span class="s2"&gt;"activePerOneMillion"&lt;/span&gt;:2319.06,&lt;span class="s2"&gt;"recoveredPerOneMillion"&lt;/span&gt;:24509.55,&lt;span class="s2"&gt;"criticalPerOneMillion"&lt;/span&gt;:14.37,&lt;span class="s2"&gt;"affectedCountries"&lt;/span&gt;:223&lt;span class="o"&gt;}&lt;/span&gt;⏎
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lN4FiaU1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2021-10/covid.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lN4FiaU1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2021-10/covid.png" alt="Screenshot of API response from disease.sh" width="800" height="445"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Although this is a very simple application, it will suffice to demonstrate the concepts of Docker covered in this tutorial.&lt;/p&gt;

&lt;p&gt;In the next section, we'll take a look at how to set up the &lt;a href="https://docs.docker.com/engine/"&gt;Docker Engine&lt;/a&gt; locally on your machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing Docker
&lt;/h2&gt;

&lt;p&gt;Before you can Dockerize an application, you need to install the Docker Engine. The official Docker manual provides a guide for installing the software on a variety of operating systems, most notably on &lt;a href="https://docs.docker.com/desktop/mac/install/"&gt;macOS&lt;/a&gt;, &lt;a href="https://docs.docker.com/desktop/windows/install/"&gt;Windows&lt;/a&gt;, and a variety of &lt;a href="https://docs.docker.com/engine/install/#server"&gt;Linux distributions&lt;/a&gt;. Ensure you install the latest stable release — v20.10.x at the time of writing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker &lt;span class="nt"&gt;-v&lt;/span&gt;
Docker version 20.10.5, build 55c4c88
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setting Up a Dockerfile
&lt;/h2&gt;

&lt;p&gt;Once the Docker Engine has been installed, the next step is to set up a &lt;a href="https://docs.docker.com/engine/reference/builder/"&gt;Dockerfile&lt;/a&gt; to build a &lt;a href="https://docs.docker.com/storage/storagedriver/#images-and-layers"&gt;Docker image&lt;/a&gt; for your application. An image represents an immutable snapshot of an environment that contains all the source code, dependencies, and other files needed for an application to run. Once a Docker image is created, it can be transported to another machine and executed there without compatibility issues.&lt;/p&gt;

&lt;p&gt;Docker images are assembled through a Dockerfile. It is a text file that contains a set of instructions executed in succession. These instructions are executed on a parent image, and each step in the file contributes to creating an entirely custom image for your application.&lt;/p&gt;

&lt;p&gt;Let's go ahead and create a &lt;code&gt;Dockerfile&lt;/code&gt; for our demo application at the root of the project directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open up the &lt;code&gt;Dockerfile&lt;/code&gt; in your text editor and add the following line to the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM node:16-alpine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above specifies the base image to be the &lt;a href="https://hub.docker.com/%5F/node"&gt;official Node.js Alpine Linux&lt;/a&gt; image. Alpine Linux is used here due to its small size, which helps a lot when transporting images from one machine to the other.&lt;/p&gt;

&lt;p&gt;The next line in the &lt;code&gt;Dockerfile&lt;/code&gt; is shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WORKDIR /app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;WORKDIR&lt;/code&gt; instruction sets the working directory to &lt;code&gt;/app&lt;/code&gt;. This directory will be created if it doesn't exist.&lt;/p&gt;

&lt;p&gt;Use the following lines to install your application's dependencies: a crucial step for building your Docker image. Note that the lines that start with &lt;code&gt;#&lt;/code&gt; denote a comment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Copy and download dependencies
COPY package.json yarn.lock ./
RUN yarn --frozen-lockfile

# Copy the source files into the image
COPY . .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we need to expose the port that the application will run on through the &lt;code&gt;EXPOSE&lt;/code&gt; instruction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;EXPOSE 4000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, specify the command for starting the application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CMD yarn start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see the entire &lt;code&gt;Dockerfile&lt;/code&gt; below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM node:16-alpine
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn --frozen-lockfile
COPY . .
EXPOSE 4000
CMD yarn start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Build the Docker Image
&lt;/h2&gt;

&lt;p&gt;Now that the &lt;code&gt;Dockerfile&lt;/code&gt; is complete, it's time to build the Docker image according to the instructions in the file. This is achieved through the &lt;code&gt;docker build&lt;/code&gt; command. You need to pass in the directory where the &lt;code&gt;Dockerfile&lt;/code&gt; exists and your preferred name for the image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker build &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; covid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If all goes well and the build succeeds, you will see the messages below at the end of the command's output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Successfully built 973edfcb25d2
Successfully tagged covid:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can run &lt;code&gt;docker images&lt;/code&gt; to view some basic info about the created image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker images
REPOSITORY      TAG         IMAGE ID       CREATED         SIZE
covid           latest      973edfcb25d2   2 minutes ago   137MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Run the Docker Image in a Container
&lt;/h2&gt;

&lt;p&gt;Use the &lt;code&gt;docker run&lt;/code&gt; command to run your newly minted Docker image inside of a &lt;a href="https://www.docker.com/resources/what-container"&gt;container&lt;/a&gt;. Since the application has been built into the image, it has everything it needs to work. It can be launched directly in an isolated process. Before you can access your running image inside the container, you must expose its port to the outside world through the &lt;code&gt;--publish&lt;/code&gt; or &lt;code&gt;-p&lt;/code&gt; flag.&lt;/p&gt;

&lt;p&gt;This lets you bind the port in the container to a port outside the container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 4000:4000 covid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command above starts the &lt;code&gt;covid&lt;/code&gt; image inside of a container and exposes port 4000 inside the container to port 4000 outside the container. You can subsequently access the routes on your server through &lt;code&gt;http://localhost:4000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ojc209Cq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2021-10/docker-run.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ojc209Cq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2021-10/docker-run.png" alt="Screenshot of docker run command" width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sharing Docker Images
&lt;/h2&gt;

&lt;p&gt;You can transfer Docker images from one machine to the other in a variety of ways. The most popular method involves using the &lt;code&gt;docker push&lt;/code&gt; command to push the image to the &lt;a href="https://hub.docker.com/"&gt;official Docker registry&lt;/a&gt; and retrieving it through the &lt;code&gt;docker pull&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;You need to sign up for a free account at &lt;a href="https://hub.docker.com/"&gt;Docker Hub&lt;/a&gt; first. After the signup process is complete, head over to the &lt;a href="https://hub.docker.com/repositories"&gt;Repositories&lt;/a&gt; page, and create a new repository. Give it a name and set its visibility to "Public" or "Private".&lt;/p&gt;

&lt;p&gt;Note that free accounts have access to a limited number of private repos.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7rJznguF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2021-10/docker-hub-create-repo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7rJznguF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2021-10/docker-hub-create-repo.png" alt="Screenshot of Docker hub create repo page" width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you've created a repository, enter the &lt;code&gt;docker login&lt;/code&gt; command on your terminal to log in to Docker Hub on your machine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FJ7EJVyu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2021-10/docker-login.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FJ7EJVyu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2021-10/docker-login.png" alt="Screenshot of Docker login command" width="800" height="196"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before you push the image to Docker Hub, you need to update the image tag to match your repository namespace: &lt;code&gt;&amp;lt;your docker username&amp;gt;/&amp;lt;repo name&amp;gt;&lt;/code&gt;. This is because the &lt;code&gt;docker push&lt;/code&gt; command expects an argument in this format.&lt;/p&gt;

&lt;p&gt;Enter the command below to tag your &lt;code&gt;covid&lt;/code&gt; image with a new name. Ensure you replace &lt;code&gt;&amp;lt;your docker username&amp;gt;&lt;/code&gt; with your actual docker username.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker tag covid &amp;lt;your docker username&amp;gt;/covid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, push the image to Docker Hub using the &lt;code&gt;docker push&lt;/code&gt; command, as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker push &amp;lt;your docker username&amp;gt;/covid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the image is pushed successfully to the registry, it will be reflected in your repository dashboard:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Wn3nhdJ3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2021-10/docker-push.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Wn3nhdJ3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.appsignal.com/images/blog/2021-10/docker-push.png" alt="Screenshot of Docker hub repository" width="800" height="547"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can pull the image on any machine with &lt;code&gt;docker&lt;/code&gt; installed through the command below. If the repository is private, you'll need to log in first through the &lt;code&gt;docker login&lt;/code&gt; command. Keep in mind that the speed of downloading an image from the registry depends on the image size and the speed of your internet connection. This is one of the reasons why smaller Docker images are preferred in general.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker pull &amp;lt;your docker username&amp;gt;/covid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that you can also choose to share Docker images through registries provided by other cloud services such as &lt;a href="https://docs.gitlab.com/ee/user/packages/container_registry/"&gt;GitLab&lt;/a&gt;, &lt;a href="https://cloud.google.com/container-registry"&gt;Google Cloud&lt;/a&gt;, &lt;a href="https://quay.io/"&gt;RedHat&lt;/a&gt;, and others. You can even &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-a-private-docker-registry-on-ubuntu-20-04"&gt;set up your own private registry&lt;/a&gt; on a dedicated server for use within an organization.&lt;/p&gt;

&lt;h3&gt;
  
  
  Share Docker Images without Using a Registry
&lt;/h3&gt;

&lt;p&gt;An alternative way to share a Docker image with others is to export it as a &lt;code&gt;.tar&lt;/code&gt; file and transfer it to a different machine through any preferred transfer method. This helps you transfer the Docker images between machines in cases when using a Docker registry is not desirable or possible, for whatever reason. The &lt;code&gt;docker save&lt;/code&gt; command is what you need to use for exporting a Docker image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker save covid &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; covid.tar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above command will export the &lt;code&gt;covid&lt;/code&gt; image to a &lt;code&gt;covid.tar&lt;/code&gt; file in the current directory. This file may then be transferred to a remote machine and loaded into the machine's local registry through the &lt;code&gt;docker load&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker load &amp;lt; covid.tar
Loaded image: covid:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deploy Your Dockerized Node.js Application to Production
&lt;/h2&gt;

&lt;p&gt;The easiest way to deploy a Dockerized application on a remote server is to transfer the application's image with &lt;code&gt;docker pull&lt;/code&gt; and then use &lt;code&gt;docker run&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This runs the application in a container similar to how you'd do it in your development environment. However, such a strategy is suboptimal for a truly production-ready application.&lt;/p&gt;

&lt;p&gt;Unlike our demo application, a real-world product will likely be composed of several different services that depend on each other for the application as a whole to properly work. Deploying to production usually means starting all the component services in the right order to ensure a smooth operation. You also need a strategy for other tasks, such as restarting a service in case of failures, aggregating logs, and performing health checks. All these concerns — and more — can be handled through &lt;a href="https://docs.docker.com/compose"&gt;Docker Compose&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Docker Compose coordinates multi-container Docker applications through a single command. It relies on a Compose file that provides a set of instructions to configure all the containers that should be spawned. Here's what the Compose file (&lt;code&gt;docker-compose.yml&lt;/code&gt;) for our demo application looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;covid&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;4000:4000"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;NODE_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above Compose file uses version 3 of the &lt;a href="https://docs.docker.com/compose/compose-file/"&gt;Compose file format&lt;/a&gt; and defines a single service called &lt;code&gt;web&lt;/code&gt; that uses the &lt;code&gt;covid&lt;/code&gt; image we previously set up. If you leave out the &lt;code&gt;image&lt;/code&gt; property, a Docker image from the &lt;code&gt;Dockerfile&lt;/code&gt; will be built in the current directory and used for the service. The &lt;code&gt;ports&lt;/code&gt; property defines the exposed ports for the container and host machine, and the &lt;code&gt;environment&lt;/code&gt; property sets up any necessary environmental variables.&lt;/p&gt;

&lt;p&gt;Once you have a &lt;code&gt;docker-compose.yml&lt;/code&gt; file, you can start the defined services with the &lt;code&gt;docker-compose up&lt;/code&gt; command. Make sure you have &lt;code&gt;docker-compose&lt;/code&gt; installed before running the command, otherwise, find out &lt;a href="https://docs.docker.com/compose/install/"&gt;how to install Docker Compose&lt;/a&gt; on your operating system.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose up
Recreating covid-node_web_1 ... &lt;span class="k"&gt;done
&lt;/span&gt;Attaching to covid-node_web_1
web_1  | yarn run v1.22.5
web_1  | &lt;span class="nv"&gt;$ &lt;/span&gt;node app.js
web_1  | &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"level"&lt;/span&gt;:30,&lt;span class="s2"&gt;"time"&lt;/span&gt;:1630001521702,&lt;span class="s2"&gt;"pid"&lt;/span&gt;:28,&lt;span class="s2"&gt;"hostname"&lt;/span&gt;:&lt;span class="s2"&gt;"204c8ce51d52"&lt;/span&gt;,&lt;span class="s2"&gt;"msg"&lt;/span&gt;:&lt;span class="s2"&gt;"Server listening at http://0.0.0.0:4000"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will launch the containers for the defined services, and they will be accessible on the specified ports. Note that if you exit this command (such as by pressing &lt;code&gt;Ctrl-C&lt;/code&gt;), every spawned container will stop immediately. To prevent this from happening, append the &lt;code&gt;--detach&lt;/code&gt; flag so that the containers start in the background and keep running.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose up &lt;span class="nt"&gt;--detach&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We've only scratched the surface of the workflows a Compose file can achieve. Ensure to check out the &lt;a href="https://docs.docker.com/compose/compose-file/compose-file-v3/"&gt;full documentation&lt;/a&gt; to learn more about all the available options. The &lt;code&gt;docker-compose&lt;/code&gt; CLI also provides several other important commands you should know about to get the most out of it. You can examine each of them through the &lt;code&gt;--help&lt;/code&gt; flag or the &lt;a href="https://docs.docker.com/compose/reference/"&gt;CLI reference page&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap-up and Further Docker Reading
&lt;/h2&gt;

&lt;p&gt;In this article, we covered the process of Dockerizing an existing Node.js application, building containers, and deploying to production through Docker Compose.&lt;/p&gt;

&lt;p&gt;Keep in mind that there's a lot more to Docker than can be covered in one article. Refer to the official documentation to learn more about &lt;a href="https://docs.docker.com/develop/develop-images/dockerfile_best-practices/"&gt;best practices for writing a Dockerfile&lt;/a&gt;, &lt;a href="https://docs.docker.com/engine/security/"&gt;securing a Docker container&lt;/a&gt;, &lt;a href="https://docs.docker.com/config/containers/logging/"&gt;logging&lt;/a&gt;, and other important topics to use Docker effectively in your application workflow.&lt;/p&gt;

&lt;p&gt;Thanks for reading, and happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you liked this post, &lt;a href="https://blog.appsignal.com/javascript-sorcery"&gt;subscribe to our JavaScript Sorcery list&lt;/a&gt; for a monthly deep dive into more magical JavaScript tips and tricks.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.P.S. If you need an APM for your Node.js app, go and &lt;a href="https://www.appsignal.com/nodejs"&gt;check out the AppSignal APM for Node.js&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Ayo is a Software Developer by trade. He enjoys writing about diverse technologies in web development, mainly in Go and JavaScript/TypeScript. You can learn more about him &lt;a href="https://freshman.tech/"&gt;through his blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>node</category>
    </item>
    <item>
      <title>Best Practices for Logging in Node.js</title>
      <dc:creator>Ayooluwa Isaiah</dc:creator>
      <pubDate>Wed, 08 Sep 2021 12:08:33 +0000</pubDate>
      <link>https://dev.to/appsignal/best-practices-for-logging-in-node-js-1p4h</link>
      <guid>https://dev.to/appsignal/best-practices-for-logging-in-node-js-1p4h</guid>
      <description>&lt;p&gt;&lt;strong&gt;This post was updated on 27 April 2023 to include information about &lt;a href="https://www.appsignal.com/tour/log-management" rel="noopener noreferrer"&gt;logging with Winston and AppSignal&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Good logging practices are crucial for monitoring and troubleshooting your Node.js servers. They help you track errors in the application, discover performance optimization opportunities, and carry out different kinds of analysis on the system (such as in the case of outages or security issues) to make critical product decisions.&lt;/p&gt;

&lt;p&gt;Even though logging is an essential aspect of building robust web applications, it's often ignored or glossed over in discussions about development best practices. Knowing what and how to log is tricky because it's often difficult to understand what information you’ll need during troubleshooting.&lt;/p&gt;

&lt;p&gt;Since we understand how essential logging is, we need to practice good logging habits. We should accumulate logs that make it easier to diagnose the root cause of problems and solve potential issues before they impact end-users.&lt;/p&gt;

&lt;p&gt;This article will outline some best practices to follow when writing logs in a Node.js application.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Use a Node.js Logging Library
&lt;/h2&gt;

&lt;p&gt;Node.js developers tend to rely on the runtime's console methods (such as &lt;code&gt;console.log()&lt;/code&gt;) to log events since it is built into the runtime, and offers a familiar API similar to the JavaScript console mechanism provided by web browsers.&lt;/p&gt;

&lt;p&gt;While &lt;code&gt;console.log()&lt;/code&gt; has its uses, it is not an adequate solution for implementing logging in a production application. It lacks support for features and configuration options that are considered essential to a good logging setup. For example, console methods do not support log levels like &lt;code&gt;warn&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt; or &lt;code&gt;debug&lt;/code&gt;, despite the provision of methods like &lt;code&gt;console.warn()&lt;/code&gt;, &lt;code&gt;console.error()&lt;/code&gt;, and &lt;code&gt;console.debug()&lt;/code&gt;, respectively. Those are simply functions that print to the standard output or standard error without indicating log severity.&lt;/p&gt;

&lt;p&gt;A good logging library provides a robust feature set that makes it easier to centralize, format, and distribute the logs to fit your needs. For example, a typical logging framework will provide various options for where to output log data (such as the terminal, filesystem, or a database), while also supporting the ability to send the logs over HTTP if you want to transport your log entries to a log management service.&lt;/p&gt;

&lt;p&gt;There are three major concerns for choosing a suitable logging library: recording, formatting, and storing messages. You need to make sure that your library of choice addresses all three concerns in a satisfactory manner. Another critical consideration for selecting a logging library is performance. Since the logger will be used a lot throughout the codebase, it can harm your application's runtime performance. Therefore, you should also investigate the performance characteristics of a library, and see how it compares to alternatives.&lt;/p&gt;

&lt;p&gt;In the Node.js ecosystem, there are several popular options to consider. Most of them offer similar features, but they also have their differences — so it's vital that you try them out yourself and see which one caters to your use case the best:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/winston" rel="noopener noreferrer"&gt;Winston&lt;/a&gt; — The most popular logging library, with support for multiple transports. This allows you to easily configure your preferred storage location for your logs.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/pino" rel="noopener noreferrer"&gt;Pino&lt;/a&gt; — Pino's biggest attraction is its speed. It claims to be up to &lt;a href="https://getpino.io/#/?id=low-overhead" rel="noopener noreferrer"&gt;five times
faster&lt;/a&gt; than alternatives, in many cases.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/bunyan" rel="noopener noreferrer"&gt;Bunyan&lt;/a&gt; — Another feature-rich logging framework that outputs in JSON by default and provides a CLI tool for viewing your logs.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/roarr" rel="noopener noreferrer"&gt;Roarr&lt;/a&gt; — Roarr is a different kind of logger that works in Node.js and the browser.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the rest of the article, we'll use &lt;code&gt;winston&lt;/code&gt; to demonstrate the different concepts. This is not necessarily an endorsement of &lt;code&gt;winston&lt;/code&gt;; it's only being used here because it's the most popular logging framework for Node.js. You can follow along by installing &lt;code&gt;winston&lt;/code&gt; in your project though &lt;code&gt;npm&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Use the Correct Log Levels
&lt;/h2&gt;

&lt;p&gt;If you've spent a reasonable amount of time in the software industry, especially supporting production applications, you may have observed the use of log levels, at least in passing. They provide a way to differentiate between the types of events in a system and add context to how important each event is. If you correctly utilize log levels in your application, it will be easy to distinguish between critical events that need to be immediately addressed versus purely informative events.&lt;/p&gt;

&lt;p&gt;Although logging systems give different names to severity levels, the concepts remain largely the same. Here are the most common log levels that you are likely to encounter, regardless of the logging framework you choose (in decreasing order of severity):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;FATAL&lt;/strong&gt;: Used to represent a catastrophic situation — your application cannot recover. Logging at this level usually signifies the end of the program.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ERROR&lt;/strong&gt;: Represents an error condition in the system that happens to halt a specific operation, but not the overall system. You can log at this level when a third-party API is returning errors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WARN&lt;/strong&gt;: Indicates runtime conditions that are undesirable or unusual, but not necessarily errors. An example could be using a backup data source when the primary source is unavailable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;INFO&lt;/strong&gt;: Info messages are purely informative. Events that are user-driven or application-specific may be logged at this level. A common use of this level is to log interesting runtime events, such as the startup or shutdown of a service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DEBUG&lt;/strong&gt;: Used to represent diagnostic information that may be needed for troubleshooting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TRACE&lt;/strong&gt;: Captures every possible detail about an application's behavior during development.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;winston&lt;/code&gt; library uses the following log levels by default — with &lt;code&gt;error&lt;/code&gt; being the most severe and &lt;code&gt;silly&lt;/code&gt; being the least:&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="err"&gt;error:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;warn:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;info:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;http:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;verbose:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;debug:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;silly:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&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;p&gt;If the defaults do not suit your needs, you can change them while initializing a custom logger. For example, you can instead use the log levels discussed above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createLogger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;transports&lt;/span&gt; &lt;span class="p"&gt;}&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="s1"&gt;winston&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;logLevels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;fatal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&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;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createLogger&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;levels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;logLevels&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;transports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;transports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Console&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;When you want to log a message, you can reference the desired level directly on the custom logger, as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;System launch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// {"message":"System launch","level":"info"}&lt;/span&gt;
&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;A critical failure!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// {"message":"A critical failure!","level":"fatal"}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Winston also allows you to define a &lt;code&gt;level&lt;/code&gt; property on the logger and on each transport which specifies the maximum level of messages that should be logged. For example, you can run your programs at &lt;code&gt;info&lt;/code&gt; level by default, then switch to &lt;code&gt;debug&lt;/code&gt; or &lt;code&gt;trace&lt;/code&gt; level when you need to troubleshoot an issue or deploy to a testing environment. You should control this setting through an environmental variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;winston&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createLogger&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;warn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;levels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;logLevels&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;transports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;transports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;info&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Use Structured Logging
&lt;/h2&gt;

&lt;p&gt;When defining how your log messages look, the priority should be to make your log entries easy to read for both humans and machines.&lt;/p&gt;

&lt;p&gt;One of the primary goals of logging is to enable post-mortem debugging, and this will involve reading log entries to reconstruct the steps that led to an event in the system. Having human-readable log entries will make this thankless task much easier for developers and sysadmins. It's also important to use a structured format that is easy to parse by machines. This allows for some automated processing on the logs (such as for alerting or auditing purposes).&lt;/p&gt;

&lt;p&gt;JSON is a universal favorite for structured log entries because it is ubiquitous and easily readable by humans. It is also highly machine-readable and easily converted to other formats, even when working with other programming languages.&lt;/p&gt;

&lt;p&gt;When logging in JSON, it's necessary to use a standard schema so that the semantics of each field is clearly defined. This also makes it easy to find what you're looking for when analyzing log entries.&lt;/p&gt;

&lt;p&gt;Winston outputs a JSON string by default with two fields: &lt;code&gt;message&lt;/code&gt; and &lt;code&gt;level&lt;/code&gt;. The former contains the text that is being logged, while the latter signifies the log level. Customizing the output is straightforward through &lt;code&gt;winston.format&lt;/code&gt;, which uses &lt;a href="https://github.com/winstonjs/logform#readme" rel="noopener noreferrer"&gt;logform&lt;/a&gt; to implement its different formats. For example, if you wanted to add a timestamp to each log entry, you can do so by combining the &lt;code&gt;timestamp&lt;/code&gt; and &lt;code&gt;json&lt;/code&gt; formats as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createLogger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;transports&lt;/span&gt; &lt;span class="p"&gt;}&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="s1"&gt;winston&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;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createLogger&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;combine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
  &lt;span class="na"&gt;transports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;transports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;({})],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will produce log entries in the following format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"message":"Connected to DB!","level":"info","timestamp":"2021-07-28T22:35:27.758Z"}
{"message":"Payment received","level":"info","timestamp":"2021-07-28T22:45:27.758Z"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure you read the &lt;a href="https://github.com/winstonjs/logform#readme" rel="noopener noreferrer"&gt;logform&lt;/a&gt; docs to learn about the predefined formats and how to create your own custom formats.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Write Descriptive Messages
&lt;/h2&gt;

&lt;p&gt;Log entries should adequately describe the events that they represent. Each message should be unique to the situation and should clearly explain the event that occurred at that point. In the event of an emergency, your log entries may be the only source of information to help you understand what happened, so it's important to get this aspect of logging right!&lt;/p&gt;

&lt;p&gt;Here's an example of an inadequate log entry being used to communicate a request failure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request failed, will retry.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The message above doesn't provide any insights into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The specific request that failed&lt;/li&gt;
&lt;li&gt;The reason for its failure&lt;/li&gt;
&lt;li&gt;The length of time before the request is retried&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can probably find out the answers to some of these questions by looking at other places such as other log entries or even the code itself. However, it is better to make the log entry more valuable on its own, through a more descriptive message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"POST" request to "https://example.com/api" failed. Response code: "429", response message: "too many requests". Retrying after "60" seconds.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This second message is much better because it provides adequate information about the failed request, including the status code and response message, and also notes that the request will be retried in 60 seconds. If all your messages are just as descriptive, you'll have a more pleasant time when&lt;br&gt;
attempting to make sense of your logs. Other examples of good log messages include the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Status of task id "1234" changed from "IN_PROGRESS" to "COMPLETED".
SomeMethod() processed "100" records in "35ms".
User registration failed: field "email" is not valid email address; field "password" is below the minimum 8 characters.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When composing log messages, include all the relevant details pertaining to the event without being unnecessarily verbose. This will prevent other log readers (which could include your future self) from being overwhelmed with too much information to sift through. Your log messages should also be able to stand on their own; don't rely on a previous message's content to provide the context for a later entry.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Add the Right Amount of Context to Your Logs
&lt;/h2&gt;

&lt;p&gt;Besides writing a descriptive log message, you also need to include the right amount of context in the log entry. Context makes it possible to quickly reconstruct the actions leading up to an event. Add basic information to the log, such as the timestamp of the event and the method where it occurred (or a stack trace, in the case of errors). You should also add data points relevant to the flow of the operation that triggered the event. These data points may be generated at different parts of the operation flow and aggregated at the point of logging.&lt;/p&gt;

&lt;p&gt;In the context of a billing service, log entries generated by the system could include several data points, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Session identifiers&lt;/li&gt;
&lt;li&gt;Usernames and IDs&lt;/li&gt;
&lt;li&gt;Product or transaction identifiers&lt;/li&gt;
&lt;li&gt;The current page that the user is on&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can use each of the above data points to track a user's flow through an entire checkout process. If an important event occurs, the available data will be automatically appended to the log output, and it will be possible to identify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the circumstances that led to the event (such as the user who experienced the event)&lt;/li&gt;
&lt;li&gt;the page where it occurred&lt;/li&gt;
&lt;li&gt;the transaction and&lt;/li&gt;
&lt;li&gt;product id that triggered the event.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These data points also let you filter log entries based on a common identifier such as the user id or product id.&lt;/p&gt;

&lt;p&gt;Winston provides the ability to add global metadata (such as the component or service where an event occurred) to every generated log entry. In a complex application, this information in your logs is helpful for troubleshooting issues because it immediately directs you to the point of failure.&lt;/p&gt;

&lt;p&gt;You can configure this when creating the logger for the component or service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createLogger&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;combine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
  &lt;span class="na"&gt;defaultMeta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;billing-service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;transports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;transports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Console&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;A &lt;code&gt;service&lt;/code&gt; field will be included in all logs created by the &lt;code&gt;logger&lt;/code&gt; object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"message":"Order \"1234\" was processed successfully","level":"info","service":"billing-service","timestamp":"2021-07-29T10:56:14.651Z"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To add metadata to individual entries, you need to create a context or metadata object that you can pass around throughout the flow of an operation so that the data is accessible at logging points. You can also utilize the concept of &lt;a href="https://github.com/winstonjs/winston#creating-child-loggers" rel="noopener noreferrer"&gt;child loggers&lt;/a&gt; to add metadata at logging points:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;090121&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;creme-de-la-creme&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;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;child&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Order "1234" was processed successfully&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// {"context":{"userId":"090121","productId":"creme-de-la-creme"},"message":"Order \"1234\" was processed successfully","level":"info","service":"billing-service","timestamp":"2021-07-29T12:20:13.249Z"}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. Avoid Logging Sensitive Information
&lt;/h2&gt;

&lt;p&gt;Regardless of whether you're in an industry with strict rules around compliance (such as healthcare or finance) or not, it's important to avoid including sensitive information in your logs.&lt;/p&gt;

&lt;p&gt;Sensitive information includes social security numbers, addresses, passwords, credit card details, access tokens, and similar data types. Since log messages are often stored in plain text, such data will be exposed if the logs fall into the wrong hands. You also have to ensure you are not falling&lt;br&gt;
afoul of regulations that apply to countries where your product is operational (such as GDPR) by recording certain pieces of information.&lt;/p&gt;

&lt;p&gt;You can avoid accidentally leaking sensitive data in your logs by minimizing which parts of the system work with that data. For instance, credit card details should only be seen by the billing component of your system, and sensitive data should be kept out of URLs — redacted where possible.&lt;/p&gt;

&lt;p&gt;Although this isn't a foolproof solution, you can also use a blocklist to prevent specific fields from ever making it into the logs.&lt;/p&gt;
&lt;h2&gt;
  
  
  7. Log for Auditing and Profiling Reasons
&lt;/h2&gt;

&lt;p&gt;We primarily use logs to diagnose issues and find the root cause of bugs. However, logs can also prove invaluable when auditing or profiling a system, or perhaps to generate interesting statistics about system behavior.&lt;/p&gt;

&lt;p&gt;For example, you can log details of what users are doing on the system (like user sign-ins, the resources they created or accessed, etc.). In the absence of specialized tools, you can also use logging to profile your code by reporting how long an operation took or how many times a function was executed. The&lt;br&gt;
insights gleaned from this data can help you improve the performance of your application's hotspots.&lt;/p&gt;

&lt;p&gt;Winston provides a simple profiling mechanism that you can take advantage of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;info&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Returns an object corresponding to a specific timing&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;profiler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startTimer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nf"&gt;setTimeout&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;// End the timer and log the duration&lt;/span&gt;
  &lt;span class="nx"&gt;profiler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;done&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="s1"&gt;Logging message&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="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;code&gt;durationMs&lt;/code&gt; field will be included in the output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"message":"Logging message","level":"info","durationMs":1001,"timestamp":"2021-07-29T14:17:55.097Z"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  8. Automatically Log Uncaught Exceptions and Unhandled Promise Rejections
&lt;/h2&gt;

&lt;p&gt;When you encounter an uncaught exception or unhandled promise rejection, it is always considered good practice to crash the program. Use a process manager like &lt;a href="https://www.npmjs.com/package/pm2" rel="noopener noreferrer"&gt;PM2&lt;/a&gt; to automatically restart the process and restore the program to a clean state.&lt;/p&gt;

&lt;p&gt;To understand why such an event has occurred, it's also necessary to log the details of the exception or promise rejection before exiting. Winston provides handlers for both situations, which may be configured on a &lt;code&gt;logger&lt;/code&gt; instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createLogger&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;transports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;transports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;File&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;file.log&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;exceptionHandlers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;transports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;File&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;exceptions.log&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;rejectionHandlers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;transports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;File&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rejections.log&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the example above, uncaught exceptions will be logged into an &lt;code&gt;exceptions.log&lt;/code&gt; file, while unhandled rejections will be logged into a &lt;code&gt;rejections.log&lt;/code&gt; file. Entries will automatically include the full stack trace as well as information about process arguments and memory usage associated with the exception, giving you all the details you need to find the root of the problem.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/winston#exceptions" rel="noopener noreferrer"&gt;Learn how to configure Winston's unhandled exception handler&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Centralize and Monitor Your Logs with Winston and AppSignal
&lt;/h2&gt;

&lt;p&gt;Managing a Node.js application often involves handling vast amounts of logs generated by the system. As your application grows, these logs can become increasingly difficult to track and analyze, making it crucial to centralize them in one unified location. By consolidating logs from various sources, you can streamline the monitoring and troubleshooting process, providing better visibility into your application's performance and ensuring a more reliable user experience.&lt;/p&gt;

&lt;p&gt;Centralizing logs not only simplifies management, but also offers several key benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Easier log analysis&lt;/strong&gt;: Combining logs from multiple instances and sources allows for more efficient and comprehensive analysis, making identify patterns, trends, and potential issues.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Faster troubleshooting&lt;/strong&gt;: When issues arise, centralized logs make it easier to pinpoint the root cause, reducing the time spent on debugging and improving the speed at which you can resolve problems.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Improved performance monitoring&lt;/strong&gt;: With all logs in one place, you can easily monitor the overall health of your application, enabling you to identify performance bottlenecks and optimize your code accordingly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alerting and notifications&lt;/strong&gt;: You can set up alerts based on specific conditions, ensuring that you're immediately notified of critical issues.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Regulatory compliance&lt;/strong&gt;: For organizations subject to regulatory requirements, centralizing logs can help meet compliance standards by providing a clear audit trail and ensuring data integrity.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Winston can be configured to integrate with a &lt;a href="https://www.appsignal.com/tour/log-management" rel="noopener noreferrer"&gt;log management platform like AppSignal&lt;/a&gt; to provide all the above benefits and more. All you need to do is set up the AppSignal transport for Winston as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createLogger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;transports&lt;/span&gt; &lt;span class="p"&gt;}&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;winston&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;WinstonTransport&lt;/span&gt; &lt;span class="p"&gt;}&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;@appsignal/nodejs&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;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createLogger&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;transports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WinstonTransport&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nodejs_app&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;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello from Winston Logger!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll need to install the &lt;a href="https://www.npmjs.com/package/@appsignal/nodejs" rel="noopener noreferrer"&gt;AppSignal SDK for Node.js&lt;/a&gt;,&lt;br&gt;
create a log source via the &lt;a href="https://appsignal.com/accounts" rel="noopener noreferrer"&gt;AppSignal dashboard&lt;/a&gt;, and initialize the AppSignal client instance with the provided API Key. Once you're all set up, your application logs will be streamed directly to AppSignal.&lt;/p&gt;

&lt;p&gt;Here's an example of how your logs might look in AppSignal:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.appsignal.com%2Fimages%2Fblog%2F2023-04%2Fwinston-logging-expanded.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.appsignal.com%2Fimages%2Fblog%2F2023-04%2Fwinston-logging-expanded.png" alt="Log details in AppSignal"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.appsignal.com/logging/platforms/integrations/nodejs.html" rel="noopener noreferrer"&gt;Learn more about AppSignal's logging integrations for Node.js&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Node.js Logging Tips: Next Steps
&lt;/h2&gt;

&lt;p&gt;In this article, we've covered some invaluable practical tips to help you write more useful log entries. Next, you should add logging to your Node.js server and configure it while keeping the above best practices in mind.&lt;/p&gt;

&lt;p&gt;If you have any additional tips regarding logging in Node.js that were not discussed in this post, feel free to share them with me on &lt;a href="https://twitter.com/ayisaiah" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks for reading, and happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you liked this post, &lt;a href="https://blog.appsignal.com/javascript-sorcery" rel="noopener noreferrer"&gt;subscribe to our JavaScript Sorcery list&lt;/a&gt; for a monthly deep dive into more magical JavaScript tips and tricks.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.P.S. If you need an APM for your Node.js app, go and &lt;a href="https://www.appsignal.com/nodejs" rel="noopener noreferrer"&gt;check out the AppSignal APM for Node.js&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Ayo is a Software Developer by trade. He enjoys writing about diverse technologies in web development, mainly in Go and JavaScript/TypeScript. You can learn more about him &lt;a href="https://freshman.tech/" rel="noopener noreferrer"&gt;through his blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>node</category>
    </item>
    <item>
      <title>Migrating away from Google Analytics</title>
      <dc:creator>Ayooluwa Isaiah</dc:creator>
      <pubDate>Mon, 13 Jul 2020 16:44:00 +0000</pubDate>
      <link>https://dev.to/ayoisaiah/migrating-away-from-google-analytics-5930</link>
      <guid>https://dev.to/ayoisaiah/migrating-away-from-google-analytics-5930</guid>
      <description>&lt;p&gt;Google analytics has been my go-to analytics tool ever since I became a web developer a few years ago. It's the de-facto standard for website usage tracking and is free to use, feature-rich and easily accessible on web and mobile. However, over the last couple of months, I contemplated moving away from the service for some reasons.&lt;/p&gt;

&lt;p&gt;My first gripe with Google Analytics is that the interface is quite slow and confusing to use. It's a busy interface and I always have to fumble around to find the relevant metric I'm looking for. The mobile app is much better in this regard at least in terms of speed.&lt;/p&gt;

&lt;p&gt;I also realised that I wasn't even using up to 10% of the features provided by the service. For the most part, all I cared about is the number of visitors, page views, and where visitors are coming from (search engines, social media e.t.c.) so all the other metrics that Google collects are not important for me.&lt;/p&gt;

&lt;p&gt;Due to this reason, I felt it wasn't an acceptable trade-off to load the 19 KB tracking script just to view basic website statistics. When decompressed, the analytics script is about 46 KB which is just extra work for the browser to parse and execute and for not a lot of gain in the context of my needs.&lt;/p&gt;

&lt;p&gt;Another major issue is privacy. Google Analytics is free to use (for up to 10 million hits per month), but it's only because the company recoups the cost of running the service by using the aggregated data to track users for the purpose of showing personalised ads.&lt;/p&gt;

&lt;p&gt;As a result, the analytics script is often blocked by ad blockers and other anti tracking extensions including those built into browsers such as Firefox, Brave, and Vivaldi meaning that the reported statistics aren't all that accurate especially for sites with tech-savvy audiences like mine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YEMgpCS9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/41fzsqxaxqva0qhv3ra0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YEMgpCS9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/41fzsqxaxqva0qhv3ra0.png" alt="Firefox's Enhanced Tracking Protection blocks Google Analytics" width="800" height="202"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Evaluating alternatives
&lt;/h2&gt;

&lt;p&gt;Quite a few Google Analytics alternatives have emerged recently with many beginning to recognise that Google has way too much influence on the web and also because of the usability issues discussed above.&lt;/p&gt;

&lt;p&gt;My main criteria for a replacement was that it should be privacy-respecting, lightweight and easy to use with a good balance of features. I found some discussions on Hacker News on alternatives to Google Analytics and proceeded to check out some of the suggestions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://goatcounter.com/"&gt;Goat Counter&lt;/a&gt; was the first option I looked into. It ticks most of the boxes and is fully open-source, easy to self-host and has a sane pricing model but I was not in love with the &lt;a href="https://stats.arp242.net/"&gt;user interface&lt;/a&gt; of the metrics dashboard. Another option was &lt;a href="https://simpleanalytics.com/"&gt;Simple Analytics&lt;/a&gt;, but it's proprietary and too expensive for me at $19/month for ~100k views monthly.&lt;/p&gt;

&lt;p&gt;Eventually, it came down to a choice between &lt;a href="https://plausible.io"&gt;Plausible&lt;/a&gt; and &lt;a href="https://usefathom.com"&gt;Fathom&lt;/a&gt;. They both have a similar feature set, a good user interface to view my stats, and lightweight tracking scripts. I decided to go with Plausible in the end.&lt;/p&gt;

&lt;p&gt;It mostly came down to pricing (Plausible starts at $12/month for ~100k views with a 33% discount if billed annually), and the fact that the &lt;a href="https://github.com/plausible/analytics"&gt;codebase&lt;/a&gt; is fully open source and supports&lt;br&gt;
&lt;a href="https://github.com/plausible/analytics/blob/master/HOSTING.md"&gt;self-hosting&lt;/a&gt; without restrictions. On the other hand, Fathom starts at $14/month for the same amount of views and the V2 of the service is no longer open source. They have a &lt;a href="https://github.com/usefathom/fathom"&gt;Lite version&lt;/a&gt; on GitHub, but it's clear from the Readme file that they don't want anyone to use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Switching to Plausible
&lt;/h2&gt;

&lt;p&gt;I decided to take advantage of Plausible's generous 30-day trial to try out the service and get a feel for how it works. Their tracking script is one of the lightest out there coming in at around 834 bytes (1.3 KB uncompressed) which is more than 20 times lighter than GA's tracking script.&lt;/p&gt;

&lt;p&gt;After adding the script to my site, the analytics began to show up almost immediately. The &lt;a href="https://plausible.io/plausible.io"&gt;dashboard&lt;/a&gt; interface is quite polished and supports all of the basic metrics such as page views and unique visitors, browser and operating system statistics, device screen sizes, locations, and referrer information.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NdplUUPC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/8zs8mnpn0g8ictncvgna.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NdplUUPC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/8zs8mnpn0g8ictncvgna.jpg" alt="Plausible user interface" width="800" height="554"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It also supports event tracking, weekly or monthly email reports, and the ability to proxy the tracking script through a custom domain which can be quite handy because some browsers and extensions block all analytics services including privacy-friendly ones.&lt;/p&gt;

&lt;p&gt;I decided to run Plausible side by side with Google Analytics for a few days just to see how the reported stats compare. Unsurprisingly, Plausible's reporting for page views was consistently about 30-40% higher presumably because GA is blocked by some clients. GA is gone for good now though.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;The experience of switching away from Google Analytics to Plausible was painless and I'm really happy with how it turned out so far. I think both Plausible and Goat Counter are excellent choices for anyone looking for a privacy friendly alternative to Google Analytics. The former just has a bit more visual polish while the latter is significantly easier to self-host but you really can't go wrong with either.&lt;/p&gt;

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

</description>
      <category>analytics</category>
      <category>google</category>
      <category>privacy</category>
      <category>performance</category>
    </item>
    <item>
      <title>A guide to setting up Vim for JavaScript development</title>
      <dc:creator>Ayooluwa Isaiah</dc:creator>
      <pubDate>Mon, 06 May 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/ayoisaiah/a-guide-to-setting-up-vim-for-javascript-development-4hbb</link>
      <guid>https://dev.to/ayoisaiah/a-guide-to-setting-up-vim-for-javascript-development-4hbb</guid>
      <description>&lt;p&gt;I’ve been using Vim for close to two years now, and do all my JavaScript development work in it. In the process, I’ve tweaked my configuration several times until I finally achieved a setup that I can say I’m really happy with. So, in this article, I want to talk about some of the plugins and tools that I’ve taken advantage of to make writing JavaScript a more pleasurable experience in Vim.&lt;/p&gt;

&lt;p&gt;All the plugins and settings were tested, and confirmed to work on Vim 8.1 and Neovim 0.4.0, the latest versions at the time of writing. As this post mainly deals with installing and configuring plugins for Vim, I recommend that you adopt &lt;a href="https://github.com/junegunn/vim-plug" rel="noopener noreferrer"&gt;vim-plug&lt;/a&gt; as your plugin manager if you don’t use one already.&lt;/p&gt;

&lt;h2&gt;
  
  
  Syntax highlighting
&lt;/h2&gt;

&lt;p&gt;Vim supports basic syntax highlighting for JavaScript but I found it suboptimal especially when it comes to modern ES2015+ syntax, and it doesn’t support JSX when working with React. I found that &lt;a href="https://github.com/pangloss/vim-javascript" rel="noopener noreferrer"&gt;vim-javascript&lt;/a&gt; and &lt;a href="https://github.com/mxw/vim-jsx" rel="noopener noreferrer"&gt;vim-jsx&lt;/a&gt; solved my problems in both instances.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl9qobjtik89dq73kxbp9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl9qobjtik89dq73kxbp9.png" alt="React code in Neovim" width="800" height="654"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, I later replaced those two with &lt;a href="https://github.com/sheerun/vim-polyglot" rel="noopener noreferrer"&gt;vim-polyglot&lt;/a&gt; which is a plugin that bundles several other syntax plugins for over 100 languages, and loads them on demand so that performance is not affected.&lt;/p&gt;

&lt;p&gt;So, instead of having to add another plugin to provide syntax highlighting for &lt;code&gt;.vue&lt;/code&gt; files for example, &lt;code&gt;vim-polyglot&lt;/code&gt; takes care of that without much fuss. This means I’m covered when switching between different JavaScript frameworks as well as other programming languages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Linting your code with ESLint
&lt;/h2&gt;

&lt;p&gt;Code linting helps you identify potential errors in your code without having to run the code. It also helps you avoid problematic patterns or code that doesn’t adhere to certain style guidelines. &lt;a href="https://eslint.org/" rel="noopener noreferrer"&gt;ESLint&lt;/a&gt; has become the dominant linting tool for JavaScript developers due to it’s comprehensive support for all the modern features of the language, and also its easy extensibility.&lt;/p&gt;

&lt;p&gt;If you’re not using ESLint already, follow &lt;a href="https://eslint.org/docs/user-guide/getting-started" rel="noopener noreferrer"&gt;this guide&lt;/a&gt; to set it up in your JavaScript project. In order for ESLint to work, you need to configure it with specific &lt;a href="https://eslint.org/docs/rules/" rel="noopener noreferrer"&gt;rules&lt;/a&gt;. You can use the recommended configuration which only addresses common problems, or check out &lt;a href="https://www.npmjs.com/package/eslint-config-airbnb-base" rel="noopener noreferrer"&gt;Airbnb’s style guide&lt;/a&gt; if you want a more comprehensive &lt;a href="https://github.com/airbnb/javascript" rel="noopener noreferrer"&gt;set of rules&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While ESLint was built to be used via the command line, many people (myself included) prefer to integrate it into their code editor to get live feedback on code as it is being written. Bringing ESLint integration into Vim is pretty easy with &lt;a href="https://github.com/w0rp/ale" rel="noopener noreferrer"&gt;ALE&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once you &lt;a href="https://github.com/w0rp/ale#installation" rel="noopener noreferrer"&gt;install ALE&lt;/a&gt;, open up Vim in a JavaScript project that has been configured to use ESLint, and try to create an error on purpose. It should work straightaway without further configuration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu4twejk5feooi4mr1s69.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu4twejk5feooi4mr1s69.png" alt="Linting JavaScript with ALE in Neovim" width="800" height="625"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, indicators for errors and warnings appear next to the problematic lines as the code is written, and the reason for the error is printed at the bottom of the screen when the cursor is on the line where is error is found. In this case, ESLint is warning me that about unused variables and also about the &lt;code&gt;console&lt;/code&gt; statement.&lt;/p&gt;

&lt;p&gt;Additionally, you can open the location list to view all the errors in a file and jump to each error using &lt;code&gt;:lnext&lt;/code&gt; (or &lt;code&gt;:lne&lt;/code&gt;) to navigate to the next item and &lt;code&gt;:lprevious&lt;/code&gt; (or &lt;code&gt;:lpr&lt;/code&gt;) to navigate to the previous item.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flpmqw3sobz1rtn5b419f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flpmqw3sobz1rtn5b419f.png" alt="Jump between ESLint errors via the location list" width="800" height="625"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An easier way to &lt;a href="https://github.com/w0rp/ale#5ix-how-can-i-navigate-between-errors-quickly" rel="noopener noreferrer"&gt;jump between linting errors quickly&lt;/a&gt; is to use the &lt;code&gt;&amp;lt;Plug&amp;gt;&lt;/code&gt; keybindings shown below. You can change &lt;code&gt;[c&lt;/code&gt; and &lt;code&gt;]c&lt;/code&gt; to your preferred key combination.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nmap &amp;lt;silent&amp;gt; [c &amp;lt;Plug&amp;gt;(ale_previous_wrap)
nmap &amp;lt;silent&amp;gt; ]c &amp;lt;Plug&amp;gt;(ale_next_wrap)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By the way, I’m not a fan of the default indicators used by ALE for errors and warnings, so I changed them to something that’s aesthetically more pleasing using 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;let g:ale_sign_error = '❌'
let g:ale_sign_warning = '⚠️'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzeyupk908q9ykkhk9fxr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzeyupk908q9ykkhk9fxr.png" alt="New indicators in ALE" width="800" height="625"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Code formatting with Prettier
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://prettier.io/" rel="noopener noreferrer"&gt;Prettier&lt;/a&gt; is a code formatting tool that has gained huge momentum in the JavaScript ecosystem since coming on the scene around early 2017. It basically ensures that a codebase complies to a set code style guide regardless of the number of developers working on the project.&lt;/p&gt;

&lt;p&gt;It works by formatting a file (or many files) according to the &lt;a href="https://prettier.io/docs/en/options.html" rel="noopener noreferrer"&gt;options&lt;/a&gt; specified in its configuration file. As such, all files formatted by Prettier adhere to the same guidelines for indendation, quote style, spacing, line width, and several other stylistic concerns.&lt;/p&gt;

&lt;p&gt;To use Prettier, you need to &lt;a href="https://prettier.io/docs/en/install.html" rel="noopener noreferrer"&gt;add it to your project&lt;/a&gt;. You can use Prettier’s &lt;a href="https://prettier.io/docs/en/cli.html" rel="noopener noreferrer"&gt;CLI&lt;/a&gt;, or configure it as a &lt;a href="https://prettier.io/docs/en/precommit.html" rel="noopener noreferrer"&gt;pre-commit hook&lt;/a&gt;, but I prefer the instant feedback of having it format my code immediately I save a file.&lt;/p&gt;

&lt;p&gt;ESLint also has some code fixing capabilities, but some of these conflict with Prettier’s methods. It is better to have ESLint take care of code quality concerns, while leaving Prettier to focus on code formatting concerns.&lt;/p&gt;

&lt;p&gt;To achieve this, you need to disable the ESLint rules that conflict with Prettier with &lt;a href="https://github.com/prettier/eslint-config-prettier" rel="noopener noreferrer"&gt;eslint-config-prettier&lt;/a&gt; while adding Prettier’s code fixing capabilities to ESLint using &lt;a href="https://github.com/prettier/eslint-plugin-prettier" rel="noopener noreferrer"&gt;eslint-plugin-prettier&lt;/a&gt;. To do so, run the following command in your project directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ yarn add eslint-config-prettier eslint-plugin-prettier -D
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then add the following to your ESLint configuration file. This configures both &lt;code&gt;eslint-plugin-prettier&lt;/code&gt; and &lt;code&gt;eslint-config-prettier&lt;/code&gt; in a single step.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "extends": ["plugin:prettier/recommended"]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s all you need to do to integrate Prettier with ESLint. The next step is to configure Vim so that all JavaScript code is formatted nicely as soon as a file is saved. ALE solves this nicely for us!&lt;/p&gt;

&lt;p&gt;In addition to supporting linters that report errors, ALE can also run fixers to format the code in a Vim buffer. All you need to do is specify &lt;code&gt;eslint&lt;/code&gt; as a fixer for JavaScript in your &lt;code&gt;.vimrc&lt;/code&gt; as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let g:ale_fixers['javascript'] = ['eslint']

" Fix files automatically on save
let g:ale_fix_on_save = 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, linting and formatting errors will be annotated by ESLint, while Prettier formats the code on save.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ho2r7t5va10gkojmegp.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ho2r7t5va10gkojmegp.gif" alt="Prettier in action" width="800" height="573"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also format the code on demand using the &lt;code&gt;:ALEFix&lt;/code&gt; command. The easiest way to use it is to create a key mapping in your Vim configuration file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nmap &amp;lt;F6&amp;gt; &amp;lt;Plug&amp;gt;(ale_fix)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Super fast fuzzy file search
&lt;/h2&gt;

&lt;p&gt;File searching is important in any editor, and while Vim supports this via the &lt;code&gt;:find&lt;/code&gt; command, it’s very basic and doesn’t support fuzzy finding as far as I can tell. There are a couple of tools out there that bring fuzzy file search to vim. After using &lt;a href="https://github.com/ctrlpvim/ctrlp.vim" rel="noopener noreferrer"&gt;CtrlP&lt;/a&gt; for a while, I switched to &lt;a href="https://github.com/junegunn/fzf.vim#installation" rel="noopener noreferrer"&gt;Fzf.vim&lt;/a&gt; which is a wrapper for the command line fuzzy finder with the same name, &lt;a href="https://github.com/junegunn/fzf" rel="noopener noreferrer"&gt;Fzf&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Fzf looks like a simple tool on the surface, but it’s actually very powerful. Here’s a few ways I take advantage of it on a daily basis:&lt;/p&gt;

&lt;h3&gt;
  
  
  Fuzzy file finder
&lt;/h3&gt;

&lt;p&gt;My primary use of Fzf is to search files in a project. This is achieved using the &lt;code&gt;:Files&lt;/code&gt; command which I heartily bound to Ctrl-P in my Vim config. I also modified the &lt;code&gt;$FZF_DEFAULT_COMMAND&lt;/code&gt; variable to use &lt;a href="https://github.com/BurntSushi/ripgrep" rel="noopener noreferrer"&gt;Ripgrep&lt;/a&gt; for listing the files. After installing Ripgrep, put this in your &lt;code&gt;.bashrc&lt;/code&gt; or &lt;code&gt;.zshrc&lt;/code&gt; to search for all files except those in hidden directorys (like &lt;code&gt;.git&lt;/code&gt;) or those that have been ignored in your &lt;code&gt;.gitignore&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export FZF_DEFAULT_COMMAND='rg --files --follow --hidden'

# Fish syntax
# set -gx FZF_DEFAULT_COMMAND 'rg --files --follow --hidden'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additonally, Fzf provides the &lt;code&gt;:Buffers&lt;/code&gt; amd &lt;code&gt;:History&lt;/code&gt; commands for searching open buffers and your buffer history. I have mapped these to &lt;code&gt;&amp;lt;Leader&amp;gt;b&lt;/code&gt; and &lt;code&gt;&amp;lt;Leader&amp;gt;h&lt;/code&gt; respectively.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nnoremap &amp;lt;C-p&amp;gt; :Files&amp;lt;CR&amp;gt;
nnoremap &amp;lt;Leader&amp;gt;b :Buffers&amp;lt;CR&amp;gt;
nnoremap &amp;lt;Leader&amp;gt;h :History&amp;lt;CR&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similarly to CtrlP, once Fzf is open, you can hit the Enter key to open the selected file in the current window, or use Ctrl-T, Ctrl-X or Ctrl-V to open selected files in a in new tab, horizontal split, or vertical split respectively.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F956lmx9ohc7x93par8rp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F956lmx9ohc7x93par8rp.png" alt="FZF in Vim" width="800" height="625"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Searching for tags
&lt;/h3&gt;

&lt;p&gt;Fzf provides two main commands for searching tags in a project. The first one&lt;code&gt;:BTags&lt;/code&gt; allows you to search for tags in the current buffer. It doesn’t require a tags file, and is great for jumping between methods quickly.&lt;/p&gt;

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

&lt;p&gt;The second one, &lt;code&gt;:Tags&lt;/code&gt;, allows you to perform project wide search for tags, but it does require a tags file. You can use &lt;a href="https://github.com/universal-ctags/ctags" rel="noopener noreferrer"&gt;Universal Ctags&lt;/a&gt; to generate a tags file for your project and combine it with &lt;a href="https://github.com/ludovicchabant/vim-gutentags" rel="noopener noreferrer"&gt;Gutentags&lt;/a&gt; to automate the creation and updating of tags in the background.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyj1fo2x7hck9frk7z7ne.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyj1fo2x7hck9frk7z7ne.png" alt="Using the Fzf :Tags command" width="800" height="625"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have mapped both commands mapped to &lt;code&gt;&amp;lt;Leader&amp;gt;t&lt;/code&gt; and &lt;code&gt;&amp;lt;Leader&amp;gt;T&lt;/code&gt; respectively.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nnoremap &amp;lt;Leader&amp;gt;t :BTags&amp;lt;CR&amp;gt;
nnoremap &amp;lt;Leader&amp;gt;T :Tags&amp;lt;CR&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Project-wide search
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;:Rg&lt;/code&gt; command provided by Fzf uses Ripgrep to find all occurrences of a pattern across an entire project, so you can search for a specific string, select the matches and navigate the results using the quickfix list. I often use it to check for where a method is used in my codebase, and I found it incredibly helpful in a recent project I was refactoring.&lt;/p&gt;

&lt;p&gt;When using &lt;code&gt;:Rg&lt;/code&gt;, you can narrow down the results and hit Enter on the selected result to open the file in the current window. This will skip the quickfix list altogether. To select multiple results, just use theTab key on each file you want to select or Alt-A to select all results and then Enter to populate the quickfix list which you can navigate with &lt;code&gt;:cnext&lt;/code&gt; and &lt;code&gt;:cprev&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foekcoz7vwxf3ybv4lwn1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foekcoz7vwxf3ybv4lwn1.png" alt="Project-wide search with :Rg" width="800" height="625"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is so much more that can be done with Fzf. For example you can navigate Vim’s &lt;code&gt;:help&lt;/code&gt; documentation with &lt;code&gt;:Helptags&lt;/code&gt;, search for Snippets (&lt;code&gt;:Snippets&lt;/code&gt;), Git Commits (&lt;code&gt;:Commits&lt;/code&gt;), Command history (&lt;code&gt;:History:&lt;/code&gt;), and more. Use &lt;code&gt;:h fzf-vim-commands&lt;/code&gt; to find out all the options available to you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Intelligent code completion
&lt;/h2&gt;

&lt;p&gt;For most people, having a good code completion experience is pivotal to having a modern development setup. Vim supports some basic functionality out of the box through &lt;code&gt;omnicomplete&lt;/code&gt;, but we’re going to replace that with something much more powerful.&lt;/p&gt;

&lt;p&gt;Since I started using Vim, I’ve tried an few plugins for auto-completion, including &lt;a href="https://github.com/Shougo/deoplete.nvim" rel="noopener noreferrer"&gt;Deoplete&lt;/a&gt; and &lt;a href="https://github.com/Valloric/YouCompleteMe" rel="noopener noreferrer"&gt;YouCompleteMe&lt;/a&gt; but neither provided the experience I desired.&lt;/p&gt;

&lt;p&gt;I discovered &lt;a href="https://dev.to/scottw/coc-nvim-1a7f"&gt;Coc.nvim&lt;/a&gt; recently and it’s been great! It brings several IDE-like features to Vim and is easy to set up. It’s built upon the concept of &lt;a href="https://langserver.org/" rel="noopener noreferrer"&gt;language servers&lt;/a&gt;, which power features like auto-completion, go-to-definition, hover tooltips, and more in modern editors.&lt;/p&gt;

&lt;p&gt;Follow the &lt;a href="https://github.com/neoclide/coc.nvim/wiki/Install-coc.nvim" rel="noopener noreferrer"&gt;installation guide&lt;/a&gt; to get Coc.nvim up and running. Once Coc.nvim has been installed, you need to install some language server extensions to provide intellisense support for JavaScript and Typescript projects. You can so so with &lt;code&gt;:CocInstall&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;:CocInstall coc-tsserver coc-json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;a href="https://github.com/neoclide/coc-tsserver" rel="noopener noreferrer"&gt;coc-tsserver&lt;/a&gt;, you get so many &lt;a href="https://github.com/neoclide/coc-tsserver#features" rel="noopener noreferrer"&gt;features&lt;/a&gt; that make writing, debugging and refactoring JavaScript or Typescript a breeze. Without configuring anything, auto-completion should just work and you can see function signatures and relevant suggestions as you type.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg35uwipi1hm0w7a0worc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg35uwipi1hm0w7a0worc.png" alt="Code completion with Coc.nvim" width="800" height="587"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can even replace ALE’s linting and code formatting capabilities with the help of &lt;a href="https://github.com/neoclide/coc-eslint" rel="noopener noreferrer"&gt;coc-eslint&lt;/a&gt; and &lt;a href="https://github.com/neoclide/coc-prettier" rel="noopener noreferrer"&gt;coc-prettier&lt;/a&gt;, but as I’m already happy with my setup, I have opted not to do so at this time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;This post has covered several plugins that can help you get more utility from Vim when it comes to JavaScript development. While you don’t have to use any of this stuff to be productive with Vim, it can certainly help you avoid bugs, and speed up the development process.&lt;/p&gt;

&lt;p&gt;I hope you’ve found this article useful. If you have any questions, or experience any troubles while configuring a plugin, do leave a comment below and I’ll get back to you. If you’re curious about my personal configuration, you can check out &lt;a href="https://github.com/ayoisaiah/dotfiles/tree/master/vim" rel="noopener noreferrer"&gt;my dotfiles&lt;/a&gt; or &lt;a href="https://twitter.com/@ayisaiah" rel="noopener noreferrer"&gt;send me a message on Twitter&lt;/a&gt;.&lt;/p&gt;

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

</description>
      <category>vim</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Learn Node.js and MongoDB by building a URL Shortener app</title>
      <dc:creator>Ayooluwa Isaiah</dc:creator>
      <pubDate>Mon, 21 Jan 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/ayoisaiah/learn-nodejs-and-mongodb-by-building-a-url-shortener-app-4kk8</link>
      <guid>https://dev.to/ayoisaiah/learn-nodejs-and-mongodb-by-building-a-url-shortener-app-4kk8</guid>
      <description>&lt;p&gt;In this article, you’ll learn how to build a URL Shortener application with Node.js and MongoDB. Here’s a &lt;a href="https://freshman-shortener.herokuapp.com/"&gt;live demo&lt;/a&gt; of what we’ll be building. You can find the complete source code for this project in this &lt;a href="https://github.com/freshman-tech/url-shortener"&gt;GitHub repo&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;I assume basic familiarity with JavaScript as well as the command line. If you haven’t built a basic Node application before, you might want to &lt;a href="https://dev.to/microservice"&gt;start here&lt;/a&gt; first, then return to this tutorial at a later time.&lt;/p&gt;

&lt;p&gt;You also need to have Node.js and npm installed on your computer. You can visit the &lt;a href="https://nodejs.org/en/download/"&gt;Node.js website&lt;/a&gt; to view installation instructions for your operating system. &lt;a href="https://npmjs.com"&gt;npm&lt;/a&gt; comes bundled with Node, so once you install Node, you’ll have access to the &lt;code&gt;npm&lt;/code&gt; command too.&lt;/p&gt;

&lt;p&gt;The versions I used while building this project are as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js v11.2.0&lt;/li&gt;
&lt;li&gt;npm v6.6.0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can view the version of Node and &lt;code&gt;npm&lt;/code&gt; you have installed by running the following commands in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node -v
npm -v
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Grab the starter files
&lt;/h2&gt;

&lt;p&gt;Grab the starter files for this project at this &lt;a href="https://github.com/Freshman-tech/url-shortener-starter-files"&gt;GitHub repository&lt;/a&gt;. Clone the repo to your computer and &lt;code&gt;cd&lt;/code&gt; into the created directory. Then run &lt;code&gt;npm install&lt;/code&gt; from the project root to install all the dependencies specified in the &lt;code&gt;package.json&lt;/code&gt; file. I’ll get into what each of the dependencies do later on as we progress.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install MongoDB
&lt;/h2&gt;

&lt;p&gt;MongoDB is a free and open-source NoSQL document database commonly used in modern web applications. You’ll need to have it installed on your machine. At the time of writing, the latest stable version is &lt;strong&gt;4.0.5&lt;/strong&gt;. This is the version I used throughout this tutorial.&lt;/p&gt;

&lt;p&gt;Here are the installation instructions for &lt;a href="https://docs.mongodb.com/manual/administration/install-on-linux/"&gt;Linux&lt;/a&gt;, &lt;a href="https://docs.mongodb.com/manual/tutorial/install-mongodb-on-os-x/"&gt;macOS&lt;/a&gt; and &lt;a href="https://docs.mongodb.com/v3.2/tutorial/install-mongodb-on-windows/"&gt;Windows&lt;/a&gt;. If you’re on Ubuntu like myself, you can install MongoDB using &lt;code&gt;apt&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;sudo apt update
sudo apt install -y mongodb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can checkout what version of mongoDB you have installed using &lt;code&gt;mongo --version&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The database server should be automatically started after the installation process, but you should verify this before moving on from this step. On Ubuntu, you can use the following command to check the status of the mongoDB server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo systemctl status mongodb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see this output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yCjLMXJL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://freshman.tech/assets/dist/images/learn-mongodb/mongodb-status-ubuntu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yCjLMXJL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://freshman.tech/assets/dist/images/learn-mongodb/mongodb-status-ubuntu.png" alt="Terminal showing active status of MongoDB server" width="800" height="626"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If not, you can start it using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo systemctl start mongodb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Set up a basic Node server
&lt;/h2&gt;

&lt;p&gt;Looking through the &lt;code&gt;src&lt;/code&gt; folder within the project directory, you’ll see that we’ve got a &lt;code&gt;server.js&lt;/code&gt; file and a &lt;code&gt;public&lt;/code&gt; folder containing the markup and styles for the application frontend. The &lt;code&gt;server.js&lt;/code&gt; file is where the bulk of the application code will be written in.&lt;/p&gt;

&lt;p&gt;Unlike the previous Node tutorial where I used the built-in http module to set up the Node server, we’ll be using &lt;a href="https://expressjs.com/"&gt;Express&lt;/a&gt;, a popular Node.js web application framework in this instance.&lt;/p&gt;

&lt;p&gt;There are other web frameworks out there, but Express is simple enough, well documented and well supported so you shouldn’t run into many issues when using it in your applications.&lt;/p&gt;

&lt;p&gt;If you look through the &lt;code&gt;package.json&lt;/code&gt; file, you will see the &lt;code&gt;express&lt;/code&gt; package is part of the dependencies that we installed earlier. Let’s go ahead and use it to set up the Node server in &lt;code&gt;server.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&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;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;port&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;4100&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="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;port&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Express running → PORT &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="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;port&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can start the server by running &lt;code&gt;npm start&lt;/code&gt; in the terminal. I am making use of the &lt;a href="https://www.npmjs.com/package/nodemon"&gt;Nodemon&lt;/a&gt; package to auto restart the Node server when changes are made to it so we don’t have to do so ourselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set up the application frontend
&lt;/h2&gt;

&lt;p&gt;As mentioned earlier, the app frontend lives in the &lt;code&gt;public&lt;/code&gt; folder. We need to set up a new route on the server so that when a user visits the application, the HTML file will be sent and rendered in the browser.&lt;/p&gt;

&lt;p&gt;Change the &lt;code&gt;server.js&lt;/code&gt; file to look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="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="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&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;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&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;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&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;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;htmlPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public&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;index.html&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;sendFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;htmlPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;port&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;4100&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="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;port&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Express running → PORT &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="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;port&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://nodejs.org/api/path.html"&gt;path&lt;/a&gt; is a built-in module in Node.js. It allows us to link to directories and file paths in Node.js. The &lt;code&gt;sendFile()&lt;/code&gt; method takes an absolute path to the file, so &lt;code&gt;__dirname&lt;/code&gt; is used to avoid hardcoding the path. &lt;code&gt;__ dirname&lt;/code&gt; is the directory in which the executing file is located, so &lt;code&gt;path.join(__dirname, 'public', 'index.html')&lt;/code&gt; will resolve to &lt;code&gt;src/public/index.html&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Navigate to &lt;code&gt;http://localhost:4100&lt;/code&gt; in your browser. Notice that the HTML is rendered correctly. However, the styles are missing even though &lt;code&gt;style.css&lt;/code&gt; was linked correctly in &lt;code&gt;index.html&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8pMMBBsf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://freshman.tech/assets/dist/images/learn-mongodb/frontend-no-styles.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8pMMBBsf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://freshman.tech/assets/dist/images/learn-mongodb/frontend-no-styles.png" alt="Missing styles on the Application frontend" width="800" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When the browser encounters the reference to &lt;code&gt;style.css&lt;/code&gt;, it fires off a request to the server for that file. But since we haven’t configured our server to handle requests for static files (such as images, CSS and JavaScript), the server does nothing, and the request fails leaving the page without styles.&lt;/p&gt;

&lt;p&gt;To fix this situation, we need to configure express to handle requests for static files correctly. We can do this using the a &lt;a href="https://expressjs.com/en/starter/static-files.html"&gt;built-in middleware function&lt;/a&gt; in express as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// beginning of the file&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="c1"&gt;// rest of the file&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now reload the page. It should work correctly:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NGKZTWTF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://freshman.tech/assets/dist/images/learn-mongodb/frontend-with-styles.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NGKZTWTF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://freshman.tech/assets/dist/images/learn-mongodb/frontend-with-styles.png" alt="Application styles are properly loaded" width="800" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Submit the form to the server
&lt;/h2&gt;

&lt;p&gt;We need to write client side JavaScript to submit the contents of the form input to the server when a user submits the form.&lt;/p&gt;

&lt;p&gt;We can do this without using any client side JavaScript by setting the &lt;code&gt;action&lt;/code&gt; attribute of the form to a route on the server and setting the &lt;code&gt;method&lt;/code&gt; attribute to &lt;code&gt;POST&lt;/code&gt;, but I’ve opted to use JavaScript here so that we can handle the response and display the shortened url to the user without a full page refresh.&lt;/p&gt;

&lt;p&gt;Create a new &lt;code&gt;main.js&lt;/code&gt; file in the &lt;code&gt;public&lt;/code&gt; directory and add the following code into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.url-form&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.result-section&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&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;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&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;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.url-input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/new&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&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;Accept&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;application/json&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;Content-Type&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;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&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="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="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;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&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;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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusText&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="nx"&gt;response&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="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasChildNodes&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;removeChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastChild&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;insertAdjacentHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;afterbegin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`
        &amp;lt;div class="result"&amp;gt;
          &amp;lt;a target="_blank" class="short-url" rel="noopener" href="/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;short_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;
            &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;origin&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="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;short_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
          &amp;lt;/a&amp;gt;
        &amp;lt;/div&amp;gt;
      `&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&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="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code listens for the &lt;code&gt;submit&lt;/code&gt; event on the form, prevents the form submission and fires of a POST request to the server with the value of the form input in request body. The reason we wrap the body object in &lt;code&gt;JSON.stringify&lt;/code&gt; is so that we can consume it as JSON on the server.&lt;/p&gt;

&lt;p&gt;Note that we’re posting the data to the &lt;code&gt;/new&lt;/code&gt; route which hasn’t been created on the server yet. We’ll create it in the next section. Before that, make sure you reference &lt;code&gt;main.js&lt;/code&gt; in your &lt;code&gt;index.html&lt;/code&gt; file before the closing body tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;http-equiv=&lt;/span&gt;&lt;span class="s"&gt;"X-UA-Compatible"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"ie=edge"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;URL Shortener&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"style.css"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  // rest of the code

  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"main.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Access the form body on the server
&lt;/h2&gt;

&lt;p&gt;Let’s go ahead and create the &lt;code&gt;/new&lt;/code&gt; route that will process the URLs to be shortened. Add this below the root route in &lt;code&gt;server.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/new&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;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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first thing we need to do is access the JSON data that was sent from the client in the request body. To do this, we need to use of the &lt;a href="https://www.npmjs.org/package/body-parser"&gt;body-parser&lt;/a&gt; package. This package parses all incoming request bodies and makes them accessible on &lt;code&gt;req.body&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Since this package has been installed already, we can use it right away in &lt;code&gt;server.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&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;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&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;bodyParser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;body-parser&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;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bodyParser&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="c1"&gt;// rest of the file&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we should be able to access the request body in the &lt;code&gt;req.body&lt;/code&gt; property in the &lt;code&gt;/new&lt;/code&gt; route:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/new&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;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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&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;You can try this out by entering a URL into the form and submitting it. Then navigate to the terminal where your server is running to see the JSON data printed in the terminal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yu13BUq2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://freshman.tech/assets/dist/images/learn-mongodb/json-data-in-terminal.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yu13BUq2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://freshman.tech/assets/dist/images/learn-mongodb/json-data-in-terminal.png" alt="Request body is printed to the terminal" width="800" height="626"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This means we can access the url using &lt;code&gt;req.body.url&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validate the URL
&lt;/h2&gt;

&lt;p&gt;Before we shorten the URL, we need to validate if the URL that was submitted is valid. Client side validation is handled for us in the browser because we’ve set the type of the input to &lt;code&gt;url&lt;/code&gt; so the form won’t be submitted if the value doesn’t have a valid URL structure.&lt;/p&gt;

&lt;p&gt;To make the application more robust, we need to validate the URL on the server as well. There are several npm packages that can handle this, but I’ve opted to do so using a few built-in Node modules.&lt;/p&gt;

&lt;p&gt;The first thing we need to do is check if the URL has a valid structure, then we perform a DNS lookup to see if the domain is operational. A domain like &lt;code&gt;https://google.com&lt;/code&gt; will pass both tests, but &lt;code&gt;http://jidfsdm.com&lt;/code&gt; will fail the second one since that site does not exist.&lt;/p&gt;

&lt;p&gt;Require the built-in &lt;a href="https://nodejs.org/api/dns.html"&gt;dns&lt;/a&gt; module at the top of &lt;code&gt;server.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dns&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then change up the &lt;code&gt;/new&lt;/code&gt; route as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/new&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;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;let&lt;/span&gt; &lt;span class="nx"&gt;originalUrl&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="nx"&gt;originalUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;URL&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;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&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;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="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;invalid URL&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;dns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;originalUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&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;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Address not found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://nodejs.org/api/url.html#url_the_whatwg_url_api"&gt;URL class&lt;/a&gt; returns a new &lt;code&gt;URL&lt;/code&gt; object with several properties if the input URL has a valid structure. Otherwise, it throws an error which we can &lt;code&gt;catch&lt;/code&gt; and send back to the client.&lt;/p&gt;

&lt;p&gt;If the URL input passes the first test, we then check to see if the domain is operational by passing the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/URL/hostname"&gt;hostname&lt;/a&gt; portion of the URL (the domain) to &lt;code&gt;dns.lookup&lt;/code&gt; which checks if the domain is live. If so, we can connect to our MongoDB instance and create the shortened version of the url as you’ll see.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set up environmental variables
&lt;/h2&gt;

&lt;p&gt;Environmental variables are a great way to configure how your program should work. They are key-value pairs that are stored on the local system where your program is being run, and are accessible from within your code.&lt;/p&gt;

&lt;p&gt;It’s considered best practice to set app configuration data such as API keys, tokens, passwords and other sensitive details as environmental variables instead of hardcoding it into the program itself. This prevents you from accidentally exposing it to others, and also makes the values really easy to change without having to touch your code.&lt;/p&gt;

&lt;p&gt;In Node.js, you can access variables defined in your environment via the &lt;code&gt;process.env&lt;/code&gt; object. You can check the contents of this project via the Node.js REPL as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kzban144--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://freshman.tech/assets/images/learn-mongodb/process-env-poster.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kzban144--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://freshman.tech/assets/images/learn-mongodb/process-env-poster.png" alt="Node REPL showing contents of process.env" width="800" height="626"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Aside from the operating system variables that are present by default, we can create project specific variables using a &lt;code&gt;.env&lt;/code&gt; file. To avoid checking these files into source control, you should add &lt;code&gt;.env&lt;/code&gt; to your &lt;code&gt;.gitignore&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;.env&lt;/code&gt; file at the root of your project directory and paste the following code into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DATABASE=mongodb://localhost:27017
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we’ve added the URL of our local MongoDB instance as an environmental variable. Port 27017 is the port that MongoDB runs on. The next thing to do is load the values defined in &lt;code&gt;.env&lt;/code&gt; into &lt;code&gt;process.env&lt;/code&gt;. The easiest way to do this is with the &lt;a href="https://www.npmjs.com/package/dotenv"&gt;dotenv&lt;/a&gt; package which is already part of our app dependencies.&lt;/p&gt;

&lt;p&gt;Add the following at the very top of &lt;code&gt;server.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// rest of the file&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will read the contents of the &lt;code&gt;.env&lt;/code&gt; file in the root of your project, parse its contents and initialize the values on &lt;code&gt;process.env&lt;/code&gt;. Now, you’ll be able to access any set variable under &lt;code&gt;process.env.&amp;lt;KEY&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect to MongoDB
&lt;/h2&gt;

&lt;p&gt;Let’s go ahead and connect to our local MongoDB instance in &lt;code&gt;server.js&lt;/code&gt; as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="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="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&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;bodyParser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;body-parser&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;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&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;dns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dns&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;MongoClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mongodb&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;databaseUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DATABASE&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;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bodyParser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;urlencoded&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;extended&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="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bodyParser&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="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="nx"&gt;MongoClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;databaseUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;useNewUrlParser&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;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&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;app&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="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shortener&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="k"&gt;catch&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;console&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to connect to the database&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// rest of the file&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, we’re importing &lt;code&gt;MongoClient&lt;/code&gt; from &lt;a href="https://mongodb.github.io/node-mongodb-native/"&gt;mongodb&lt;/a&gt; which is the the native driver for interacting with a MongoDB instance in Node.js. Then we connect to the MongoDB instance specified in the &lt;code&gt;DATABASE&lt;/code&gt; environmental variable.&lt;/p&gt;

&lt;p&gt;If the connection is successful, we get a reference to the MongoDB instance client and can select a database using the &lt;code&gt;client.db()&lt;/code&gt; method. Note that this method creates the database if it does not already exist.&lt;/p&gt;

&lt;p&gt;Here, we are selecting a reference to the &lt;code&gt;shortener&lt;/code&gt; database and storing that reference in &lt;code&gt;app.locals&lt;/code&gt; which is an object provided by &lt;code&gt;express&lt;/code&gt;. This object allows us to set local variables that persist throughout the life of the application, and can be accessed in other middleware functions (functions that have access to the &lt;code&gt;req&lt;/code&gt; or &lt;code&gt;res&lt;/code&gt; objects) via &lt;code&gt;req.app.locals&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The reason we’re storing a reference to the database in &lt;code&gt;app.locals.db&lt;/code&gt; is so that we can reuse the db object without having to open another connection to the MongoDB instance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shorten URLs
&lt;/h2&gt;

&lt;p&gt;The next step is to actually shorten the URL and store it in the database. To create a unique short id for each url, we will be making use of the &lt;a href="https://github.com/ai/nanoid"&gt;nanoid&lt;/a&gt; package.&lt;/p&gt;

&lt;p&gt;Require it at the top of &lt;code&gt;server.js&lt;/code&gt; below the other &lt;code&gt;require&lt;/code&gt; statements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nanoid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nanoid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then create a new &lt;code&gt;shortenURL&lt;/code&gt; function in &lt;code&gt;server.js&lt;/code&gt; as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// beginning of the file&lt;/span&gt;

&lt;span class="nx"&gt;MongoClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;databaseUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;useNewUrlParser&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;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&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;app&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="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shortener&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="k"&gt;catch&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;console&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to connect to the database&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;shortenURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shortenedURLs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shortenedURLs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;shortenedURLs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;findOneAndUpdate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;original_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;$setOnInsert&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;original_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;short_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;nanoid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&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="na"&gt;returnOriginal&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="na"&gt;upsert&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="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// rest of the file&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to reference a collection before we can add any data into the database. We can do so using the using the &lt;code&gt;db.collection()&lt;/code&gt; method. If the collection does not exist yet, it’s created.&lt;/p&gt;

&lt;p&gt;Before we shorten the URL and add it to the database, we need to check if the URL has not been shortened already to prevent duplicate database entries for one URL. We can do this using the &lt;code&gt;findOneAndUpdate()&lt;/code&gt; method on the collection which allows us to modify a document that already exists in the database collection or create it if it doesn’t exist.&lt;/p&gt;

&lt;p&gt;This method takes a few arguments. The first one is an object that is used to filter the collection. Here we are passing an object whose &lt;code&gt;original_url&lt;/code&gt; property matches the url we are about to shorten. If a document in the database matches this filter, it will be returned and updated according to the &lt;a href="https://docs.mongodb.com/manual/reference/operator/update/"&gt;update operators&lt;/a&gt; set in the second argument.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;$setOnInsert&lt;/code&gt; operator allows us to set the value of the document only if its being inserted. This means that the document will not be modified if it already exists, but if it doesn’t, it will be created with its value set to whatever we set in &lt;code&gt;$setOnInsert&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In this case, the document will have two properties: &lt;code&gt;original_url&lt;/code&gt; which is the url to be shortened, and &lt;code&gt;short_id&lt;/code&gt; which is a unique 7 character id for that url.&lt;/p&gt;

&lt;p&gt;We also need to set the &lt;code&gt;upsert&lt;/code&gt; option to &lt;code&gt;true&lt;/code&gt;. This ensures that the document is created if it doesn’t exist. Otherwise, &lt;code&gt;$setOnInsert&lt;/code&gt; has no effect. Finally, setting &lt;code&gt;returnOriginal&lt;/code&gt; to &lt;code&gt;false&lt;/code&gt; ensures that &lt;code&gt;findOneAndUpdate&lt;/code&gt; returns the new document if one is upserted, which is what we want in this case.&lt;/p&gt;

&lt;p&gt;We can make use of the &lt;code&gt;shortenURL()&lt;/code&gt; function in the &lt;code&gt;/new&lt;/code&gt; route like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/new&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;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;let&lt;/span&gt; &lt;span class="nx"&gt;originalUrl&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="nx"&gt;originalUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;URL&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;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&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;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="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;invalid URL&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;dns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;originalUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&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;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Address not found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&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;app&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="nx"&gt;shortenURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;originalUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&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;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;res&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="na"&gt;original_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;original_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;short_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;short_id&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;catch&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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, the document that was inserted will be sent to the client as JSON. It will be displayed on the page like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hWI1_W0d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://freshman.tech/assets/dist/images/learn-mongodb/shortened-url.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hWI1_W0d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://freshman.tech/assets/dist/images/learn-mongodb/shortened-url.png" alt="URL is shortened" width="800" height="584"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Set up a catch-all route for all shortened URLs
&lt;/h2&gt;

&lt;p&gt;Apart from the &lt;code&gt;/&lt;/code&gt; and &lt;code&gt;/new&lt;/code&gt; routes, we need to handle the other requests for the shortened URLs. Specifically, we need to redirect them to the original URLs. Here’s how we can do so using Express:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/:short_id&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;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;shortId&lt;/span&gt; &lt;span class="o"&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;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;short_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we’re using named route parameters to capture the value of the &lt;code&gt;short_id&lt;/code&gt; part of the URL. This value can then be accessed in the &lt;code&gt;req.params&lt;/code&gt; object under the &lt;code&gt;short_id&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;Once we have the short id, we need to check if a url with that short id exists in the database. Let’s create a new function for this purpose just below &lt;code&gt;shortenURL&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;checkIfShortIdExists&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&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;gt;&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shortenedURLs&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;findOne&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;short_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;findOne&lt;/code&gt; method returns a document that matches the filter object passed to it or &lt;code&gt;null&lt;/code&gt; if no documents match the filter.&lt;/p&gt;

&lt;p&gt;We can then use the function in our catch-all route like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/:short_id&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;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;shortId&lt;/span&gt; &lt;span class="o"&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;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;short_id&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;db&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&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;app&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="nx"&gt;checkIfShortIdExists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;shortId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;doc&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;doc&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="k"&gt;return&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;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Uh oh. We could not find a link at that URL&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;original_url&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;catch&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="nx"&gt;error&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 short id exists in our database collection, we will redirect the user to the &lt;code&gt;original_url&lt;/code&gt; associated with that short id. Otherwise, an error message is sent to the user.&lt;/p&gt;

&lt;p&gt;Now, you should be able to shorten long urls, visit the shortened url and be redirected to the original url.&lt;/p&gt;

&lt;h2&gt;
  
  
  Visualise your database with a GUI
&lt;/h2&gt;

&lt;p&gt;A GUI will allow us to connect to our MongoDB instance and visualise all the data that is present. You also be able to create new data, update existing data and perform other similar operations.&lt;/p&gt;

&lt;p&gt;There are several MongoDB GUIs out there, but the one I like to use is &lt;a href="https://nosqlbooster.com/"&gt;NoSQLBooster&lt;/a&gt;. It is available for Linux, macOS and Windows, and you can download it &lt;a href="https://nosqlbooster.com/downloads"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once you install and open the app, it should connect to your local MongoDB instance by default. Otherwise, you can click the &lt;strong&gt;Connect&lt;/strong&gt; button in the top left and connect to it from there.&lt;/p&gt;

&lt;p&gt;Once connected, you’ll able to view all the collections present in the database, and interact with the data present in those collections.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FxCb0IDC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://freshman.tech/assets/dist/images/learn-mongodb/nosqlbooster.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FxCb0IDC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://freshman.tech/assets/dist/images/learn-mongodb/nosqlbooster.png" alt="NoSQLBooster for Linux" width="800" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy to Heroku
&lt;/h2&gt;

&lt;p&gt;Before we deploy the app to Heroku, we need to create a cloud hosted MongoDB instance first. We can do so using &lt;a href="https://www.mongodb.com/cloud/atlas"&gt;MongoDB Atlas&lt;/a&gt;. Create an account at that link and create a new cluster when you are redirected to the &lt;strong&gt;Create New Cluster&lt;/strong&gt; page.&lt;/p&gt;

&lt;p&gt;Next, click the &lt;strong&gt;Security&lt;/strong&gt; tab and hit the &lt;strong&gt;Add new user&lt;/strong&gt; button. Give the user a name and password, then click the &lt;strong&gt;Add user&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tuoXT66v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://freshman.tech/assets/dist/images/learn-mongodb/atlas-add-user.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tuoXT66v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://freshman.tech/assets/dist/images/learn-mongodb/atlas-add-user.png" alt="Add user in MongoDB Atlas" width="800" height="556"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Following that, click &lt;strong&gt;IP Whitelist&lt;/strong&gt; and then &lt;strong&gt;Add IP Address&lt;/strong&gt;. This allows you to choose what IP addresses can access your cluster. Since this is just a demo application, you can allow access from anywhere by clicking the &lt;strong&gt;Allow Access From Anywhere&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oCShxo9a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://freshman.tech/assets/dist/images/learn-mongodb/atlas-ip.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oCShxo9a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://freshman.tech/assets/dist/images/learn-mongodb/atlas-ip.png" alt="Add IP Address in MongoDB Atlast" width="800" height="469"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, go back to the Overview, and hit the &lt;strong&gt;Connect&lt;/strong&gt; button. Choose &lt;strong&gt;Connect your Application&lt;/strong&gt; and then &lt;strong&gt;Short SRV connection string&lt;/strong&gt;. Take note of that string as we’ll be using it shortly on Heroku.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DXfnxFxI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://freshman.tech/assets/dist/images/learn-mongodb/atlas-srv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DXfnxFxI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://freshman.tech/assets/dist/images/learn-mongodb/atlas-srv.png" alt="Atlas SRV connection string" width="800" height="669"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Head over to the Heroku website and &lt;a href="https://signup.heroku.com/"&gt;sign up for a free account&lt;/a&gt;. Once your account is activated, &lt;a href="https://dashboard.heroku.com/new-app?org=personal-apps"&gt;follow this link&lt;/a&gt; to create a new app. Give it a unique name. I called mine &lt;strong&gt;freshman-shortener&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Next, &lt;a href="https://devcenter.heroku.com/articles/heroku-command-line"&gt;follow the instructions here&lt;/a&gt; to install the Heroku CLI on your machine. Then run the &lt;code&gt;heroku login&lt;/code&gt; command in the terminal to login to your Heroku account.&lt;/p&gt;

&lt;p&gt;Make sure you’ve initialised a git repository for your project. If not, run the &lt;code&gt;git init&lt;/code&gt; command at the root of your project directory, then run the command below to set heroku as a remote for your git repo. Replace &lt;code&gt;&amp;lt;app name&amp;gt;&lt;/code&gt; with the name of your application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;heroku git:remote -a &amp;lt;app name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, create a &lt;code&gt;Procfile&lt;/code&gt; in the root of your project directory (&lt;code&gt;touch Procfile&lt;/code&gt;) and paste in the following contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;web: node src/server.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Following that, specify the version of Node you are running in your &lt;code&gt;package.json&lt;/code&gt; file under the &lt;code&gt;engines&lt;/code&gt; key. I specified version &lt;code&gt;11.2.0&lt;/code&gt; since that’s the version I’m running on my computer. You should change that value to match the version of Node you have on your machine.&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"url-shortener"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"URL Shortener Demo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src/server.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&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;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx nodemon src/server.js"&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;"repository"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"git"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"git+https://github.com/freshman-tech/url-shortener.git"&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;"keywords"&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="s2"&gt;"url shortener"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"mongodb"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"nodejs"&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;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ayo Isaiah"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"license"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MIT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bugs"&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;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/freshman-tech/url-shortener/issues"&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;"homepage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/freshman-tech/url-shortener#readme"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"devDependencies"&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;"nodemon"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.18.9"&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;"dependencies"&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;"body-parser"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.18.3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dotenv"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^6.2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"express"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^4.16.4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"mongodb"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^3.1.10"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"nanoid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^2.0.1"&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;"engines"&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;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"11.2.0"&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;p&gt;Before you deploy the app, head over to the &lt;strong&gt;Settings&lt;/strong&gt; tab in the Heroku dashboard and hit &lt;strong&gt;Reveal Config Vars&lt;/strong&gt;. This is where you will set the environmental variables for your app.&lt;/p&gt;

&lt;p&gt;As we did earlier on the local &lt;code&gt;.env&lt;/code&gt; file, enter &lt;code&gt;DATABASE&lt;/code&gt; as the KEY and the SRV string from MongoDB Atlas as the value, then click &lt;strong&gt;Add&lt;/strong&gt;. Remember to replace &lt;code&gt;&amp;lt;PASSWORD&amp;gt;&lt;/code&gt; in that string with password for the user you created earlier.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CMk-VgGO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://freshman.tech/assets/dist/images/learn-mongodb/heroku-config.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CMk-VgGO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://freshman.tech/assets/dist/images/learn-mongodb/heroku-config.png" alt="Set up Heroku config values" width="800" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice how easy it is to change the database location depending on the environment the program is running without touching the application code. This is one huge advantage of using environmental variables for project configuration.&lt;/p&gt;

&lt;p&gt;Finally, commit your code and push it to the Heroku remote using the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git add .
git commit -m "Initial commit"
git push heroku master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the deployment process is done, you can open &lt;code&gt;https://&amp;lt;your-app-name&amp;gt;.heroku.com&lt;/code&gt; to view and test your project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;We’ve successfully created a fully featured URL shortener and learnt the basics of Express and MongoDB along the way. We also learnt how to set up a cloud based MongoDB instance on MongoDB Atlas and how to deploy the app to Heroku.&lt;/p&gt;

&lt;p&gt;I hope this exercise was helpful to you. If you have any questions regarding this tutorial, please leave a comment below and I’ll get back to you.&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://freshman.tech/url-shortener/"&gt;freshman.tech&lt;/a&gt; on January 24, 2019. &lt;a href="https://freshman.tech/newsletter/"&gt;Subscribe to my newsletter&lt;/a&gt; to get my latest tutorials on JavaScript and CSS delivered to your inbox.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>mongodb</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
