<?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: David Neal</title>
    <description>The latest articles on DEV Community by David Neal (@reverentgeek).</description>
    <link>https://dev.to/reverentgeek</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3194%2F54ad2686-59eb-42a1-bcd4-f44cb30123ef.png</url>
      <title>DEV Community: David Neal</title>
      <link>https://dev.to/reverentgeek</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/reverentgeek"/>
    <language>en</language>
    <item>
      <title>Build Your First CRUD API with Node.js, MongoDB, and Claude Code</title>
      <dc:creator>David Neal</dc:creator>
      <pubDate>Wed, 28 Oct 2026 17:59:10 +0000</pubDate>
      <link>https://dev.to/reverentgeek/build-your-first-crud-api-with-nodejs-mongodb-and-claude-code-9bm</link>
      <guid>https://dev.to/reverentgeek/build-your-first-crud-api-with-nodejs-mongodb-and-claude-code-9bm</guid>
      <description>&lt;p&gt;&lt;em&gt;Atlas Ephemeral Clusters let you create a temporary, free MongoDB Atlas database with a single API call, with no account or API keys. In this tutorial, Claude Code provisions an ephemeral cluster, builds a Fastify CRUD API against it, and then you claim the cluster to keep your data.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You know the free sample stations at the warehouse club? Nobody hands you a clipboard before you get the tiny cup of ravioli. Nobody asks you to verify your email address, pick a subscription tier, or configure network access rules. You taste the ravioli. &lt;em&gt;Then&lt;/em&gt; you decide if it goes in the cart.&lt;/p&gt;

&lt;p&gt;Now think about the last time you wanted to try an idea against a real cloud database. Create an account. Verify the email. Create an organization. Create a project. Pick a cloud provider and a region. Create a database user. Allow-list your IP address. Somewhere around step six, the idea you were excited about has quietly left the building.&lt;/p&gt;

&lt;p&gt;That friction is worse now that AI coding agents are in the mix. Tools like &lt;a href="https://claude.com/product/claude-code" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt; can scaffold an entire API in minutes, but they'd historically hit a wall at "please go click through a signup form." The agent can write the code. It still can't taste the ravioli for you.&lt;/p&gt;

&lt;p&gt;The new &lt;strong&gt;Atlas Ephemeral Clusters&lt;/strong&gt; feature gives you a real, live MongoDB Atlas database from a single API call. No signup, no API keys. Make one call, receive a connection string, and about 15 seconds later you (or your AI agent) are writing data to the cloud.&lt;/p&gt;

&lt;p&gt;In this tutorial, we'll put that to the test by having Claude Code build a complete Task Board API with Node.js, &lt;a href="https://fastify.dev/" rel="noopener noreferrer"&gt;Fastify&lt;/a&gt;, and &lt;a href="https://www.mongodb.com/products/platform/?utm_campaign=devrel&amp;amp;utm_source=Third+Party+Content&amp;amp;utm_medium=Call+to+Action+Link&amp;amp;utm_content=mongodb-blog-medium&amp;amp;utm_term=marissa.doherty" rel="noopener noreferrer"&gt;MongoDB Atlas&lt;/a&gt;. First, here's what we'll cover.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you'll learn&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to provision a MongoDB Atlas database with one &lt;code&gt;curl&lt;/code&gt; command
&lt;/li&gt;
&lt;li&gt;How to prompt an AI agent to provision the database and build a full CRUD API against it
&lt;/li&gt;
&lt;li&gt;How to evolve the schema on a live prototype without a rewrite
&lt;/li&gt;
&lt;li&gt;What happens when an ephemeral cluster expires
&lt;/li&gt;
&lt;li&gt;How to claim your cluster and keep your data&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foljz1syvs2u4wrc9wegv.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foljz1syvs2u4wrc9wegv.png" alt="The lifecycle of using Claude Code to provision an Atlas Ephemeral Cluster. Claude uses the API, provisions the database, and then builds a Fastify API with the ephemeral cluster." width="800" height="568"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Want to skip ahead? The finished Task Board API is on &lt;a href="https://github.com/mongodb-developer/atlas-ephemeral-clusters-nodejs-api-example" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. You'll still need your own ephemeral cluster, so start with Step 1, then point the code at your new connection string.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What is an ephemeral cluster?
&lt;/h2&gt;

&lt;p&gt;An ephemeral cluster is a temporary, free MongoDB Atlas cluster you can create &lt;em&gt;without an Atlas account or API keys&lt;/em&gt;. With one API call, you get back everything you need to start building:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;What it's for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;connectionString&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A ready-to-use &lt;code&gt;mongodb+srv://&lt;/code&gt; URI with credentials baked in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;clusterId&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The cluster's ID, used to check its status (and to look up a lost &lt;code&gt;claimUrl&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;claimUrl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A link to attach the cluster to an Atlas account and keep it (valid for 7 days after creation)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;expiresAt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;When the unclaimed cluster is paused and goes offline (it isn't deleted yet)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;status&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cluster state: &lt;code&gt;PROVISIONING&lt;/code&gt;, &lt;code&gt;ACTIVE&lt;/code&gt;, or &lt;code&gt;PAUSED&lt;/code&gt; (you're looking for &lt;code&gt;ACTIVE&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;termsOfService&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The legal fine print you're agreeing to by using it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That makes ephemeral clusters perfect for prototypes, demos, workshops, and "I just want to try something" moments. It's the perfect scratch database, &lt;em&gt;and&lt;/em&gt; you can convert it to a permanent database if you choose.&lt;/p&gt;

&lt;p&gt;Keep a few caveats in mind while the cluster is unclaimed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There's no dashboard until you claim it.
&lt;/li&gt;
&lt;li&gt;Network access is open to any IP address.
&lt;/li&gt;
&lt;li&gt;Anyone holding the connection string can read &lt;em&gt;and write&lt;/em&gt; your data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How long does an ephemeral cluster last?
&lt;/h3&gt;

&lt;p&gt;An unclaimed cluster stays live for a couple of days. When &lt;code&gt;expiresAt&lt;/code&gt; passes, the cluster is paused: it goes offline, and your app can't reach it. Your data isn't deleted, though. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;claimUrl&lt;/code&gt; remains valid for up to 7 days after the cluster is created. Claim it within that window (before or after it pauses), and it converts into a permanent free-tier Atlas cluster. Your data comes along for the ride. Miss the 7-day window, and the cluster and its data are deleted for good.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Time after creation&lt;/th&gt;
&lt;th&gt;What happens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;About 15 seconds&lt;/td&gt;
&lt;td&gt;Status changes to &lt;code&gt;ACTIVE&lt;/code&gt;, and the cluster is ready to use&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2 days&lt;/td&gt;
&lt;td&gt;The cluster pauses: it goes offline, but your data is kept&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Up to 7 days&lt;/td&gt;
&lt;td&gt;The &lt;code&gt;claimUrl&lt;/code&gt; still works, even if the cluster is paused&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;After 7 days&lt;/td&gt;
&lt;td&gt;The unclaimed cluster and its data are deleted&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7ray8psz997igtz8a6ej.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7ray8psz997igtz8a6ej.png" alt="Illustration of a gloved hand holding a note that reads, &amp;quot;Your prototype if you choose to accept it. This database will self-destruct in 7 days.&amp;quot;" width="800" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What are the limits of an ephemeral cluster?
&lt;/h3&gt;

&lt;p&gt;An unclaimed cluster is a free &lt;code&gt;M0&lt;/code&gt; cluster with the usual free-tier limits:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cluster tier&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;M0&lt;/code&gt; (free)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloud provider and region&lt;/td&gt;
&lt;td&gt;AWS &lt;code&gt;us-east-1&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MongoDB version&lt;/td&gt;
&lt;td&gt;8.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage&lt;/td&gt;
&lt;td&gt;512 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Throughput&lt;/td&gt;
&lt;td&gt;100 operations per second&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connections&lt;/td&gt;
&lt;td&gt;500 concurrent connections&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You can't add database users, restrict IP access, or scale the cluster until you claim it.&lt;/p&gt;

&lt;p&gt;MongoDB also caps the number of ephemeral clusters everyone can create in total. If the API responds with a &lt;code&gt;429&lt;/code&gt;, &lt;a href="https://www.mongodb.com/docs/atlas/cli/current/atlas-cli-getting-started/?utm_campaign=devrel&amp;amp;utm_source=Third+Party+Content&amp;amp;utm_medium=Call+to+Action+Link&amp;amp;utm_content=mongodb-blog-medium&amp;amp;utm_term=marissa.doherty" rel="noopener noreferrer"&gt;create a standard free cluster&lt;/a&gt; instead. See the &lt;a href="https://www.mongodb.com/docs/atlas/tutorial/create-ephemeral-cluster/?utm_campaign=devrel&amp;amp;utm_source=Third+Party+Content&amp;amp;utm_medium=Call+to+Action+Link&amp;amp;utm_content=mongodb-blog-medium&amp;amp;utm_term=marissa.doherty" rel="noopener noreferrer"&gt;Ephemeral Clusters docs&lt;/a&gt; for the details.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why are ephemeral clusters useful for AI agents?
&lt;/h3&gt;

&lt;p&gt;MongoDB has always been a flexible choice across the development lifecycle. Your data can change as your application evolves, and you can use the same database from first sketch to production scale. Getting started used to be the most friction-filled part of the journey. With &lt;strong&gt;Atlas Ephemeral Clusters&lt;/strong&gt;, that part now takes one API call and no forms.&lt;/p&gt;

&lt;p&gt;For AI agents, an ephemeral cluster is the perfect solution. An agent can provision its own database in the middle of a session, wire it up, and keep building. It no longer has to stop and wait for a human to click through a web console.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you create an Atlas ephemeral cluster?
&lt;/h2&gt;

&lt;p&gt;Before we hand the keys to an agent, let's see how the API works. This is the entire database provisioning process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s1"&gt;'https://cloud.mongodb.com/api/atlas/v2/unauth/ephemeralClusters:create'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Accept: application/vnd.atlas.preview+json'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you were to run that command, you'd get a response like this (with real values where you see placeholders):&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;"claimUrl"&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://account.mongodb.com/account/register?claimId=&amp;lt;claimId&amp;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;"clusterId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;clusterId&amp;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;"connectionString"&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+srv://&amp;lt;username&amp;gt;:&amp;lt;password&amp;gt;@cluster0.xxxxxxx.mongodb.net/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expiresAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-18T17:29:07Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PROVISIONING"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"termsOfService"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"By using this API and any resources provisioned through it, you agree to be bound by MongoDB's Cloud Terms of Service at https://www.mongodb.com/legal/terms-and-conditions/cloud; and Privacy Policy at https://www.mongodb.com/legal/privacy/privacy-policy."&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;Notice the &lt;code&gt;status&lt;/code&gt; says &lt;code&gt;PROVISIONING&lt;/code&gt;. The cluster needs a moment to spin up, so check on it using the &lt;code&gt;clusterId&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;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="s1"&gt;'https://cloud.mongodb.com/api/atlas/v2/unauth/ephemeralClusters/&amp;lt;clusterId&amp;gt;'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Accept: application/vnd.atlas.preview+json'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I tried it, the status came back &lt;code&gt;ACTIVE&lt;/code&gt; after about 15 seconds. At that point, the &lt;code&gt;connectionString&lt;/code&gt; points to a live cluster you can connect to and get to work. Done!&lt;/p&gt;

&lt;p&gt;One command in, one database out. Sweet.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to keep your ephemeral cluster credentials safe
&lt;/h3&gt;

&lt;p&gt;One thing to watch: the status response replaces the password in &lt;code&gt;connectionString&lt;/code&gt; with a &lt;code&gt;&amp;lt;db_password&amp;gt;&lt;/code&gt; placeholder. The create response is the &lt;em&gt;only&lt;/em&gt; time Atlas returns the real password. Save that first response somewhere safe and git-ignored.&lt;/p&gt;

&lt;p&gt;Treat the &lt;code&gt;claimUrl&lt;/code&gt; and &lt;code&gt;clusterId&lt;/code&gt; as secrets, too. Anyone who has them can claim your cluster.&lt;/p&gt;

&lt;p&gt;Now let's see what an AI agent can do with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What do you need to build a CRUD API with Claude Code?
&lt;/h2&gt;

&lt;p&gt;Before we jump into building the API, you’ll need the following installed on your computer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://nodejs.org/" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; version 24 or higher (latest LTS recommended)
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://claude.com/product/claude-code" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt; (or equivalent AI agent)
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;curl&lt;/code&gt; for testing the API (already installed, if you have a modern OS)
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.mongodb.com/docs/mongodb-shell/install/?utm_campaign=devrel&amp;amp;utm_source=Third+Party+Content&amp;amp;utm_medium=Call+to+Action+Link&amp;amp;utm_content=mongodb-blog-medium&amp;amp;utm_term=marissa.doherty" rel="noopener noreferrer"&gt;&lt;code&gt;mongosh&lt;/code&gt;&lt;/a&gt; (optional) so the agent can verify the database connection
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;jq&lt;/code&gt; (optional) for working with JSON on the command line; get it from &lt;a href="https://jqlang.org/download/" rel="noopener noreferrer"&gt;jqlang.org&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pro tip:&lt;/strong&gt; To make it even easier for Claude Code to work with MongoDB, install the &lt;a href="https://www.mongodb.com/docs/claude/?utm_campaign=devrel&amp;amp;utm_source=Third+Party+Content&amp;amp;utm_medium=Call+to+Action+Link&amp;amp;utm_content=mongodb-blog-medium&amp;amp;utm_term=marissa.doherty" rel="noopener noreferrer"&gt;MongoDB Claude Plugin&lt;/a&gt; with &lt;code&gt;claude plugin install mongodb@claude-plugins-official&lt;/code&gt;. It bundles the &lt;a href="https://www.mongodb.com/docs/mcp-server/?utm_campaign=devrel&amp;amp;utm_source=Third+Party+Content&amp;amp;utm_medium=Call+to+Action+Link&amp;amp;utm_content=mongodb-blog-medium&amp;amp;utm_term=marissa.doherty" rel="noopener noreferrer"&gt;MongoDB MCP Server&lt;/a&gt;, so the agent can query your database directly, and &lt;a href="https://www.mongodb.com/docs/agent-skills/?utm_campaign=devrel&amp;amp;utm_source=Third+Party+Content&amp;amp;utm_medium=Call+to+Action+Link&amp;amp;utm_content=mongodb-blog-medium&amp;amp;utm_term=marissa.doherty" rel="noopener noreferrer"&gt;MongoDB Agent Skills&lt;/a&gt; for tasks like schema design and query optimization. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 1: How to provision a MongoDB database with Claude Code
&lt;/h2&gt;

&lt;p&gt;We could run those &lt;code&gt;curl&lt;/code&gt; commands ourselves, but there's no need. The Ephemeral Clusters docs are written so an agent can follow them, which means the provisioning prompt can be short. Create an empty project folder, start Claude Code in it, and give it this prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create a MongoDB Atlas Ephemeral Cluster for this project by following
https://www.mongodb.com/docs/atlas/tutorial/create-ephemeral-cluster/

Save the connection details to a git-ignored .env file, wait until the
cluster is ACTIVE, and confirm you can connect. Then tell me when it
expires and how to claim it.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What's happening in this prompt?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The docs do the heavy lifting.&lt;/strong&gt; Instead of pasting the API call into the prompt, we point the agent at the documentation. It reads the endpoint, headers, and response fields for itself.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;We tell it where the secrets go.&lt;/strong&gt; The connection string has credentials baked in, and the password only comes back once. A git-ignored &lt;code&gt;.env&lt;/code&gt; file keeps it safe and ready for the app.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Wait until ACTIVE" saves us a headache.&lt;/strong&gt; Without it, an eager agent might start building against a cluster that's still provisioning.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;We ask for the expiration and claim details up front.&lt;/strong&gt; So the expiration date doesn't sneak up on us. (Spoiler: it snuck up on me anyway.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Agents improvise, so your session won't match mine word for word. In my run, Claude Code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Called the create endpoint, then checked the status until the cluster reported &lt;code&gt;ACTIVE&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Connected with &lt;code&gt;mongosh&lt;/code&gt; and got a successful ping back from MongoDB 8.0
&lt;/li&gt;
&lt;li&gt;Saved &lt;code&gt;MONGODB_URI&lt;/code&gt;, &lt;code&gt;ATLAS_EPHEMERAL_CLUSTER_ID&lt;/code&gt;, and &lt;code&gt;ATLAS_EPHEMERAL_CLAIM_URL&lt;/code&gt; to &lt;code&gt;.env&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Saved the raw API response to &lt;code&gt;.ephemeral-cluster.json&lt;/code&gt; and added both files to &lt;code&gt;.gitignore&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Warned me not to delete those files, since the create call is the only time Atlas returns the real password
&lt;/li&gt;
&lt;li&gt;Told me the cluster pauses in 2 days and is deleted after 7, that only a human can open the claim URL, and summarized the free-tier limits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We have a live cloud database and haven't written a line of code yet. Time to fix that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: How to build a Fastify CRUD API with Claude Code
&lt;/h2&gt;

&lt;p&gt;Now for the fun part. In the same session, give Claude Code the spec for the Task Board API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Build a Task Board API in this folder with Node.js, Fastify, and the
official MongoDB Node.js driver, using MONGODB_URI from .env.

- Boards: name, description, owner, createdAt
- Cards: title, description, status ("todo", "in-progress", "done"),
  position, boardId, createdAt, updatedAt
- Full CRUD for boards, and for cards under /boards/:boardId/cards
- Routes to reorder cards and to move a card to a different status
- Deleting a board also deletes its cards
- Validate input with Fastify JSON schemas, use proper HTTP status codes,
  and add GET /health
- Write a README with setup steps and example curl commands
- No auth or frontend yet

When you're done, start the server and smoke test every route against
the cluster.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What's in this prompt?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The data model and routes are spelled out.&lt;/strong&gt; Boards and cards, nesting, reorder and move routes. We own the design, and the agent fills in the implementation.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The details that matter are explicit.&lt;/strong&gt; Validation, status codes, and the cascading delete are the kinds of things that get skipped when you don't ask for them.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Out-of-scope is explicit, too.&lt;/strong&gt; Telling an agent what &lt;em&gt;not&lt;/em&gt; to build is just as valuable as telling it what to build. (Agents will happily build all kinds of things you didn't ask for.)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;We ask it to prove it works.&lt;/strong&gt; The smoke test runs against the real cluster, not a mock.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's the flow you can expect Claude Code to follow without any additional help from you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scaffold the project:

&lt;ul&gt;
&lt;li&gt;A config module
&lt;/li&gt;
&lt;li&gt;A MongoDB connection plugin with indexes
&lt;/li&gt;
&lt;li&gt;JSON schemas for validation
&lt;/li&gt;
&lt;li&gt;Route files for health, boards, and cards
&lt;/li&gt;
&lt;li&gt;A Fastify app factory with centralized error handling
&lt;/li&gt;
&lt;li&gt;A server entry point with graceful shutdown
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Write a &lt;code&gt;README.md&lt;/code&gt; with setup instructions and example &lt;code&gt;curl&lt;/code&gt; commands
&lt;/li&gt;
&lt;li&gt;Start the server and run an end-to-end smoke test against the &lt;em&gt;live cloud cluster&lt;/em&gt;: create, read, update, delete, reorder, move, cascade delete, plus 400 and 404 cases.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The wild part is the smoke test. Minutes after creating the project folder, the agent is testing your API against a real Atlas cluster in the cloud. Same session, no signup, no console, no "hold on while I go create a database user."&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: How to test your API with curl
&lt;/h2&gt;

&lt;p&gt;Trust, but verify. With the server running (start it using the instructions in the generated README), let's hit the API ourselves.&lt;/p&gt;

&lt;p&gt;First, the health check, which pings the database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://localhost:3000/health
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should get back a response showing the API is up and the database connection is healthy. Next, create a board and add a card to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:3000/boards &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"name": "Launch Plan", "description": "Everything for the big day", "owner": "user-david"}'&lt;/span&gt;

&lt;span class="c"&gt;# Grab the board id from the response, then:&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:3000/boards/&amp;lt;boardId&amp;gt;/cards &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"title": "Write the announcement post", "status": "todo"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each create request returns a &lt;code&gt;201&lt;/code&gt; with the new document, including its generated id. Grab those ids to drill deeper into the API. And remember, every document you create is landing in a real MongoDB Atlas cluster that didn't exist five minutes ago.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pro tip:&lt;/strong&gt; Ask the agent to generate verification commands for you. Prompt it with "Give me the commands I can use to verify the data returned by the API," and you'll get back a full &lt;code&gt;curl&lt;/code&gt; + &lt;code&gt;jq&lt;/code&gt; walkthrough of the data tree, including a one-shot script with counts at every level.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 4: How to add a new collection without a migration
&lt;/h2&gt;

&lt;p&gt;Here's where MongoDB's flexibility earns its keep. A prototype's data model is never done. With a document database, adding a collection doesn't mean migrations, downtime, or a sad afternoon of writing SQL scripts.&lt;/p&gt;

&lt;p&gt;Let's add a Comments feature so users can discuss individual cards. Same session, same database, one follow-up prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Add a Comments feature so users can discuss individual cards.

- Comments: text, author, cardId, createdAt
- Full CRUD under /boards/:boardId/cards/:cardId/comments
- Deleting a card deletes its comments, and deleting a board deletes
  the comments on all of its cards
- Follow the existing patterns for validation and status codes
- Add the new routes to the README with example curl commands
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Watch for Claude Code to add the comments collection and index, the nested routes, and the cascading deletes (board → cards → comments). Then it updates the README and verifies the whole thing end-to-end. A new feature and a new collection, with no migration scripts in sight.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to seed realistic sample data with Claude Code
&lt;/h3&gt;

&lt;p&gt;To make the prototype feel real, follow up with one more prompt: &lt;em&gt;"Seed the boards, cards, and comments with sample projects and tasks. The information should be realistic."&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;Expect the agent to write a seed script with a few plausible projects, wire it up to &lt;code&gt;npm run seed&lt;/code&gt;, and fill your board with realistic data. &lt;/p&gt;

&lt;p&gt;My run produced 3 boards, 11 cards, and 13 comments across a website redesign, a mobile app release, and a platform API. It even included staggered timestamps and multi-author comment threads. Not bad for a one-sentence prompt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: What happens when an ephemeral cluster expires?
&lt;/h2&gt;

&lt;p&gt;This is a step you don't have to repeat, because I took the hit for you. I started building this on a Friday, came back to the project after the weekend, and asked Claude Code to check on the database. The verdict:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The cluster had passed its &lt;code&gt;expiresAt&lt;/code&gt; date, unclaimed
&lt;/li&gt;
&lt;li&gt;The API couldn't reach the database at all&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whoops.&lt;/p&gt;

&lt;p&gt;For a moment, I assumed the worst: cluster deleted, seed data gone, start over. The good news is not all was lost. When an ephemeral cluster expires, it's paused, not destroyed. The cluster goes offline, and your app stops working. Your data, though, is still sitting there, and the &lt;code&gt;claimUrl&lt;/code&gt; works for up to 7 days after the cluster was created.&lt;/p&gt;

&lt;p&gt;So I opened the &lt;code&gt;claimUrl&lt;/code&gt; the agent saved back in Step 1 (told you we'd want it later). I created my Atlas account and claimed the "dead" cluster. It came back online as a standard free-tier Atlas cluster with every board, card, comment, and seed document intact.&lt;/p&gt;

&lt;p&gt;Even better, the original connection string and credentials keep working after a claim. I didn't have to touch &lt;code&gt;.env&lt;/code&gt;, and I kept right on prototyping.&lt;/p&gt;

&lt;p&gt;Two lessons from my scare:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Treat &lt;code&gt;expiresAt&lt;/code&gt; as the day your prototype goes offline, not the day you lose everything
&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;claimUrl&lt;/code&gt; is your safety net, which is exactly why we had the agent save the cluster details in Step 1&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you ever lose the &lt;code&gt;claimUrl&lt;/code&gt;, the agent can look it up again with the &lt;code&gt;clusterId&lt;/code&gt; and the status endpoint.&lt;/p&gt;

&lt;h3&gt;
  
  
  What if you miss the 7-day claim window?
&lt;/h3&gt;

&lt;p&gt;If you miss the full 7-day claim window, recovery still takes one prompt: &lt;em&gt;"Provision a fresh ephemeral cluster and re-seed."&lt;/em&gt; Claude Code then:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Calls the API to create a new cluster
&lt;/li&gt;
&lt;li&gt;Drops the new connection string into &lt;code&gt;.env&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Updates the saved cluster info
&lt;/li&gt;
&lt;li&gt;Re-runs the seed script
&lt;/li&gt;
&lt;li&gt;Verifies the data via the API&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Total damage: about a minute. But claiming beats rebuilding, which brings us to the last step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: How to claim an ephemeral cluster and keep your data
&lt;/h2&gt;

&lt;p&gt;Don't wait for the expiration scare as I did. When you know you want to keep what you've built, open the &lt;code&gt;claimUrl&lt;/code&gt; in a browser. This is the one step an agent can't do for you. Here's how it goes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sign in to your MongoDB Atlas account, or create one (new accounts verify their email first).
&lt;/li&gt;
&lt;li&gt;Choose an existing organization, or create a new one for the cluster.
&lt;/li&gt;
&lt;li&gt;Configure IP access. The cluster has been open to any IP address (&lt;code&gt;0.0.0.0/0&lt;/code&gt;), so this is a good moment to allow only your own IP address.
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Claim Cluster&lt;/strong&gt;. This is a one-time action and can't be undone.&lt;/li&gt;
&lt;/ol&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzxcdzf6sxpkrqnmp82c6.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzxcdzf6sxpkrqnmp82c6.jpg" alt="Screenshot of the &amp;quot;claim your cluster&amp;quot; form: &amp;quot;Your cluster awaits!&amp;quot; with connection IP address restriction options" width="800" height="567"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your cluster lands in a new project, and your data comes along. You get full admin access to scale it, add database users, and lock it down. It no longer expires, though like any free-tier cluster, Atlas pauses it after 30 days of inactivity.&lt;/p&gt;

&lt;p&gt;Here's what I love about this design: the signup form didn't disappear; it just moved to the end. You sign up after the database has proven it's worth keeping, not before you've written a line of code. And because it becomes a standard Atlas cluster, your prototype's database can graduate to production without switching engines.&lt;/p&gt;

&lt;p&gt;The 7-day claim window is forgiving but not infinite, and your prototype goes offline the moment it expires. Claim early and skip the drama. Learn from my ravioli.&lt;/p&gt;

&lt;h2&gt;
  
  
  What should you build next with ephemeral clusters?
&lt;/h2&gt;

&lt;p&gt;Back to the sample station. Free samples work because you get to taste before you commit. Atlas Ephemeral Clusters bring the same deal to databases. You and your AI agent get a real cluster first, and you only sign up after the prototype has proven useful.&lt;/p&gt;

&lt;p&gt;This Task Board API has plenty of room to grow. Next up, you may want to explore one of the most useful design decisions in MongoDB, &lt;strong&gt;embedded vs. referenced documents&lt;/strong&gt;, by adding checklists to cards. Unlike comments, checklists belong &lt;em&gt;inside&lt;/em&gt; the card document. After that, consider adding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Filtering and search
&lt;/li&gt;
&lt;li&gt;Users and assignments
&lt;/li&gt;
&lt;li&gt;An aggregation-powered stats endpoint
&lt;/li&gt;
&lt;li&gt;Authentication, to take this thing from prototype to production-ready&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the meantime:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Try the one-liner yourself and see how fast you're connected
&lt;/li&gt;
&lt;li&gt;Point your favorite AI agent at it with the prompts above and swap in your own app idea
&lt;/li&gt;
&lt;li&gt;Grab the complete Task Board API source code on &lt;a href="https://github.com/mongodb-developer/atlas-ephemeral-clusters-nodejs-api-example" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Read the &lt;a href="https://www.mongodb.com/docs/atlas/tutorial/create-ephemeral-cluster/?utm_campaign=devrel&amp;amp;utm_source=Third+Party+Content&amp;amp;utm_medium=Call+to+Action+Link&amp;amp;utm_content=mongodb-blog-medium&amp;amp;utm_term=marissa.doherty" rel="noopener noreferrer"&gt;Ephemeral Clusters documentation&lt;/a&gt; for the full API reference and limits
&lt;/li&gt;
&lt;li&gt;Install the &lt;a href="https://www.mongodb.com/docs/claude/?utm_campaign=devrel&amp;amp;utm_source=Third+Party+Content&amp;amp;utm_medium=Call+to+Action+Link&amp;amp;utm_content=mongodb-blog-medium&amp;amp;utm_term=marissa.doherty" rel="noopener noreferrer"&gt;MongoDB Claude Plugin&lt;/a&gt; to give your agent the MCP Server and MongoDB Agent Skills
&lt;/li&gt;
&lt;li&gt;Dig into the &lt;a href="https://www.mongodb.com/docs/drivers/node/current/?utm_campaign=devrel&amp;amp;utm_source=Third+Party+Content&amp;amp;utm_medium=Call+to+Action+Link&amp;amp;utm_content=mongodb-blog-medium&amp;amp;utm_term=marissa.doherty" rel="noopener noreferrer"&gt;MongoDB Node.js driver docs&lt;/a&gt; to see what the agent wired up under the hood
&lt;/li&gt;
&lt;li&gt;Set a reminder to claim your cluster (seriously)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now go forth and prototype something cool! If you build something fun on an ephemeral cluster, I'd love to hear about it. What would you build if the database was never the hard part?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One API call creates a live MongoDB Atlas cluster, with no account or API keys.
&lt;/li&gt;
&lt;li&gt;Unclaimed clusters pause after 2 days. You can claim them for up to 7 days after creation; after that, they're deleted.
&lt;/li&gt;
&lt;li&gt;AI agents like Claude Code can provision and use the database without leaving the session.
&lt;/li&gt;
&lt;li&gt;MongoDB's flexible document model lets you add collections to a live prototype without migrations.
&lt;/li&gt;
&lt;li&gt;Claiming converts the cluster to a standard free-tier Atlas cluster, and your data and connection string keep working.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Do I need a MongoDB Atlas account to use an ephemeral cluster?
&lt;/h3&gt;

&lt;p&gt;No. You can create an ephemeral cluster with a single API call, with no account, email verification, or API keys. You only need an Atlas account to claim the cluster and keep it.&lt;/p&gt;

&lt;h3&gt;
  
  
  How long does an Atlas ephemeral cluster last?
&lt;/h3&gt;

&lt;p&gt;An unclaimed ephemeral cluster stays active for 2 days. After that, it pauses and goes offline, but your data is kept. You can claim it for up to 7 days after it was created. After 7 days, the unclaimed cluster and its data are deleted.&lt;/p&gt;

&lt;h3&gt;
  
  
  What happens to my data when an ephemeral cluster expires?
&lt;/h3&gt;

&lt;p&gt;Your data isn't deleted when the cluster expires. The cluster pauses, so your app can't connect to it. If you claim the cluster within 7 days of creating it, it comes back online with your data intact.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I use an ephemeral cluster in production?
&lt;/h3&gt;

&lt;p&gt;Not while it's unclaimed. Unclaimed clusters are open to any IP address, can't have their own database users, and expire. After you claim one, it becomes a standard free-tier Atlas cluster that you can secure, scale, and take to production.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I use ephemeral clusters with AI agents other than Claude Code?
&lt;/h3&gt;

&lt;p&gt;Yes. The Ephemeral Clusters API is a plain HTTP API, and the docs are written so any agent can follow them. Any AI coding agent that can make HTTP requests can provision and use an ephemeral cluster.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Build a Command-Line Application With Deno 2.0</title>
      <dc:creator>David Neal</dc:creator>
      <pubDate>Mon, 11 Nov 2024 19:07:28 +0000</pubDate>
      <link>https://dev.to/reverentgeek/build-a-command-line-application-with-deno-20-2bh7</link>
      <guid>https://dev.to/reverentgeek/build-a-command-line-application-with-deno-20-2bh7</guid>
      <description>&lt;p&gt;Command-line interfaces (CLI) are often used for automating tasks, such as building reports, synchronizing data between systems, migrating data, deploying applications, and so on and on. Over the years, I have built countless CLI apps to save time. If I ever find myself doing something more than once, I try to find a way to automate it!&lt;/p&gt;

&lt;p&gt;Deno 2.0 is an excellent solution for writing CLI apps. It supports TypeScript and JavaScript, it's cross-platform (runs on Windows, macOS, and Linux), has dozens of powerful tools in its &lt;a href="https://jsr.io/@std" rel="noopener noreferrer"&gt;standard library&lt;/a&gt;, and can also tap into &lt;a href="https://docs.deno.com/examples/npm/" rel="noopener noreferrer"&gt;most Node.js modules&lt;/a&gt;. The only limit is your imagination!&lt;/p&gt;

&lt;p&gt;In this tutorial, you will learn how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a command-line interface with Deno 2.0&lt;/li&gt;
&lt;li&gt;Parse command-line arguments&lt;/li&gt;
&lt;li&gt;Print help and version information&lt;/li&gt;
&lt;li&gt;Prompt for additional information&lt;/li&gt;
&lt;li&gt;Compile your app into a standalone executable&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Set Up Your CLI Project
&lt;/h2&gt;

&lt;p&gt;First, let's make sure you have the tools you need!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.deno.com/runtime/getting_started/installation/" rel="noopener noreferrer"&gt;Install Deno&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A good text editor, such as &lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;Visual Studio Code&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Open your computer's terminal (or command prompt). Change the current directory to the folder where you normally save projects.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you don't already have a folder where you store software projects, I like creating a folder at the root of my home directory named &lt;code&gt;projects&lt;/code&gt;. More than likely, when you open your computer's terminal/console app, you are automatically placed in your "user home" folder. Use &lt;code&gt;mkdir projects&lt;/code&gt; (or &lt;code&gt;md projects&lt;/code&gt; if you're on Windows) to create the folder. Then, use &lt;code&gt;cd projects&lt;/code&gt; to change to that new folder.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Verify you have Deno 2.0 (or higher) installed using 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;deno &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;deno 2.0.5 &lt;span class="o"&gt;(&lt;/span&gt;stable, release, aarch64-apple-darwin&lt;span class="o"&gt;)&lt;/span&gt;
v8 12.9.202.13-rusty
typescript 5.6.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you receive an error, or if your version of Deno is 1.x, follow the &lt;a href="https://docs.deno.com/runtime/getting_started/installation/" rel="noopener noreferrer"&gt;installation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next, enter the following commands to initialize a new Deno project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;deno init deno-cli-demo
&lt;span class="nb"&gt;cd &lt;/span&gt;deno-cli-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're going to use Deno's &lt;a href="https://jsr.io/@std/cli" rel="noopener noreferrer"&gt;@std/cli&lt;/a&gt; standard library, so add that to the project using 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;deno add jsr:@std/cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create Your First CLI App
&lt;/h2&gt;

&lt;p&gt;Open up your new project using your preferred editor. Create a new file named &lt;code&gt;hello.ts&lt;/code&gt; 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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;now&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;Date&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;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The current time is: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toTimeString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Welcome to Deno 🦕 Land!&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;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From your terminal, enter the following command to run the script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;deno run hello.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You've built your first Deno CLI application! Feel free to play around with writing other things to the console.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Command-Line Arguments
&lt;/h2&gt;

&lt;p&gt;Arguments? No, we're not talking about getting into a heated debate with your terminal. Although that can certainly happen. Computers can be rather obstinate.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Command-line arguments&lt;/em&gt; are options and values you might provide the CLI when you run the app. When you enter &lt;code&gt;deno run hello.ts&lt;/code&gt;, &lt;code&gt;deno&lt;/code&gt; is the CLI, and &lt;code&gt;run hello.ts&lt;/code&gt; are two &lt;em&gt;arguments&lt;/em&gt; you provide to the CLI.&lt;/p&gt;

&lt;p&gt;Create a new file named &lt;code&gt;add.ts&lt;/code&gt; and add 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;parseArgs&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;@std/cli/parse-args&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;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseArgs&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;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Arguments:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&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;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&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;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;a&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;b&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; = `&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The idea is to take two numbers and add them together. Try it out!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;deno run add.ts 1 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Experiment with additional arguments. Or none at all. The &lt;code&gt;parseArgs&lt;/code&gt; function can also handle arguments traditionally called &lt;em&gt;switches&lt;/em&gt; and &lt;em&gt;flags&lt;/em&gt;. Try the following and observe the output.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;deno run add.ts 3 4 &lt;span class="nt"&gt;--what&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;up &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; no
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advanced Command-Line Arguments
&lt;/h2&gt;

&lt;p&gt;We've only just scratched the surface of what you can do with command-line arguments. Let's try a more advanced example!&lt;/p&gt;

&lt;p&gt;Create a new file named &lt;code&gt;sum.ts&lt;/code&gt; and add 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;parseArgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ParseOptions&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;@std/cli/parse-args&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;meta&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;./deno.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="kd"&gt;with&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="s2"&gt;json&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="nf"&gt;printUsage&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="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Usage: sum &amp;lt;number1&amp;gt; &amp;lt;number2&amp;gt; ... &amp;lt;numberN&amp;gt;&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Options:&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;  -h, --help        Show this help message&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;  -v, --version     Show the version number&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;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ParseOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;boolean&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="s2"&gt;help&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;version&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;alias&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="s2"&gt;help&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;h&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;version&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;v&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;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseArgs&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;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&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;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;help&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;printUsage&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="nf"&gt;exit&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="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&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;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Pro tip: add a version to your deno.json file&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;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1.0.0&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="nf"&gt;exit&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="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// validate all arguments are numbers&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;arg&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&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;numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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="s2"&gt;ERROR: All arguments must be numbers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;printUsage&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="nf"&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="c1"&gt;// sum up the number arguments&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;val&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;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// print the numbers and the total&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; + &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt; = &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;sum&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;p&gt;Whoa, there's a lot going on here 😬 Let's try it out first, and then we'll cover some of the highlights. Try the following commands and see how the output changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;deno run sum.ts 1 2 3 4 5
deno run sum.ts &lt;span class="nt"&gt;--help&lt;/span&gt;
deno run sum.ts &lt;span class="nt"&gt;--version&lt;/span&gt;
deno run sum.ts &lt;span class="nt"&gt;-h&lt;/span&gt;
deno run sum.ts 1 2 three
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, if you go back and look through the code in &lt;code&gt;sum.ts&lt;/code&gt;, you can probably figure out some of the logic involved in handling the different arguments. The main thing I want to point out is this block of 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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ParseOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;boolean&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="s2"&gt;help&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;version&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;alias&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="s2"&gt;help&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;h&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;version&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;v&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;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseArgs&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;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&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;parseArgs&lt;/code&gt; function supports quite a few &lt;em&gt;options&lt;/em&gt; to support a wide variety of arguments.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;boolean: ["help", "version"]&lt;/code&gt;: defines the &lt;code&gt;--help&lt;/code&gt; and &lt;code&gt;--version&lt;/code&gt; flags.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;alias: { "help": "h", "version": "v" }&lt;/code&gt;: defines alternate &lt;code&gt;-h&lt;/code&gt; and &lt;code&gt;-v&lt;/code&gt; flags.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you may have guessed, &lt;code&gt;Deno.exit(0);&lt;/code&gt; causes Deno to immediately stop the current script.&lt;/p&gt;

&lt;h2&gt;
  
  
  More Input Required (Prompts)
&lt;/h2&gt;

&lt;p&gt;Imagine you're creating a CLI app to automate a report. Your app might connect to a system that requires authentication, such as a database or API.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Never embed secrets (sensitive information such as user names, passwords, API keys, connection strings, etc.) in your code. You don't want your secrets ending up in the hands of the wrong people!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this case, the CLI should prompt for the sensitive information when it runs. Let's create another example to demonstrate how this is done.&lt;/p&gt;

&lt;p&gt;First, add a new dependency using 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;deno add jsr:@std/dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a new file named &lt;code&gt;taskRunner.ts&lt;/code&gt; 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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Spinner&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;@std/cli/unstable-spinner&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="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;simulateTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&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;string&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;spinner&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;Spinner&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="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;yellow&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;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;spinner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&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;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;spinner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&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;finish&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&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;duration&lt;/span&gt; &lt;span class="o"&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;finish&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&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="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&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="s2"&gt; (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&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="s2"&gt;s).`&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 is a module that exports a function named &lt;code&gt;simulateTask&lt;/code&gt; that we'll use from the CLI app. The &lt;code&gt;simulateTask&lt;/code&gt; function includes a few tricks, such as keeping track of how long the task runs and displaying a cool spinning animation while the task is running 🤓&lt;/p&gt;

&lt;p&gt;Create a new file named &lt;code&gt;updater.ts&lt;/code&gt; 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="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@std/dotenv/load&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;parseArgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ParseOptions&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;@std/cli/parse-args&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;promptSecret&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;@std/cli/prompt-secret&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;meta&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;./deno.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="kd"&gt;with&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="s2"&gt;json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;simulateTask&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;./taskRunner.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;function&lt;/span&gt; &lt;span class="nf"&gt;printUsage&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="s2"&gt;Usage: &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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;  updater --input &amp;lt;input file&amp;gt; --output &amp;lt;output file&amp;gt;&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Options:&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;  -h, --help        Show this help message&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;  -v, --version     Show the version number&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;  -i, --input       Input file&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;  -o, --output      Output 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;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ParseOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;boolean&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="s2"&gt;help&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;version&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;string&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="s2"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;output&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;default&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="s2"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data.csv&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;output&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;report.pdf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;alias&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="s2"&gt;help&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;h&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;version&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;v&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;i&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;output&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;o&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;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseArgs&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;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&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;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;help&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;printUsage&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="nf"&gt;exit&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="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&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;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Pro tip: add a version to your deno.json file&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;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1.0.0&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="nf"&gt;exit&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="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// validate the input and output arguments&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;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;output&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="s2"&gt;You must specify both an input and output file&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;printUsage&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="nf"&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="c1"&gt;// attempt to get the username and password from environment variables&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&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;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MY_APP_USER&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;password&lt;/span&gt; &lt;span class="o"&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;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MY_APP_PASSWORD&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;user&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&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;userPrompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please enter the username:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;userPrompt&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&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;passPrompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;promptSecret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please enter the password:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;passPrompt&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="c1"&gt;// simulating a few long-running tasks&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;simulateTask&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;span class="s2"&gt;`Reading input file [&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;args&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;]`&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;simulateTask&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="s2"&gt;`Connecting with user [&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;simulateTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`Reading data from external system`&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;simulateTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`Writing output file [&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;output&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Done!&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;Some of this code may look familiar to the previous &lt;code&gt;sum.ts&lt;/code&gt; CLI example. Try running the app and see what happens!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;deno run updater.ts &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Did you get a strange message like &lt;code&gt;⚠️  Deno requests read access to...&lt;/code&gt;? What's going on here?&lt;/p&gt;

&lt;h3&gt;
  
  
  Deno security and permissions
&lt;/h3&gt;

&lt;p&gt;Deno is &lt;a href="https://docs.deno.com/runtime/fundamentals/security/" rel="noopener noreferrer"&gt;secure by default&lt;/a&gt;. It won't be able to read/write files, access your local environment variables, connect to your network, and a number of other potentially risky operations unless you explicitly give it permission.&lt;/p&gt;

&lt;p&gt;Now run it using the following command. Don't worry. It's not doing anything; it's just &lt;em&gt;simulating&lt;/em&gt; what a real app might look like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;deno run &lt;span class="nt"&gt;--allow-read&lt;/span&gt; &lt;span class="nt"&gt;--allow-env&lt;/span&gt; updater.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wasn't that cool??&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%2Fyhd0qspengiysmu8e097.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyhd0qspengiysmu8e097.gif" alt="Updater Demo" width="720" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Deno tasks
&lt;/h3&gt;

&lt;p&gt;However, including those permissions is a lot to type every time you want to run the CLI app. Let's add a task to make it easier. Open up your &lt;code&gt;deno.json&lt;/code&gt; file and update the &lt;code&gt;"tasks"&lt;/code&gt; 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="nl"&gt;"tasks"&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;"updater"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"deno run --allow-read --allow-env updater.ts"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can use 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;deno task updater &lt;span class="nt"&gt;--input&lt;/span&gt; data.csv &lt;span class="nt"&gt;--output&lt;/span&gt; report.pdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Prompts and environment variables
&lt;/h3&gt;

&lt;p&gt;Let's revisit the code in &lt;code&gt;updater.ts&lt;/code&gt;. Specifically, this block of 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;// attempt to get the username and password from environment variables&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&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;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MY_APP_USER&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;password&lt;/span&gt; &lt;span class="o"&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;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MY_APP_PASSWORD&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;user&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&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;userPrompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please enter the username:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;userPrompt&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&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;passPrompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;promptSecret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please enter the password:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;passPrompt&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's common practice to store app configuration as environment variables. Deno can access environment variables (with permission) using &lt;code&gt;Deno.env.get()&lt;/code&gt;. If &lt;code&gt;MY_APP_USER&lt;/code&gt; or &lt;code&gt;MY_APP_PASSWORD&lt;/code&gt; are not set as environment variables, the app will use &lt;code&gt;prompt()&lt;/code&gt; or &lt;code&gt;promptSecret()&lt;/code&gt; to ask for those values. As you've already seen, &lt;code&gt;promptSecret()&lt;/code&gt; hides the characters you type.&lt;/p&gt;

&lt;p&gt;Remember the dependency we added for &lt;code&gt;jsr:@std/dotenv&lt;/code&gt;? This standard library reads a file named &lt;code&gt;.env&lt;/code&gt; and adds any values as environment variables.&lt;/p&gt;

&lt;p&gt;Create a new file in the project named &lt;code&gt;.env&lt;/code&gt; and add the following text.&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;MY_APP_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;loluser
&lt;span class="nv"&gt;MY_APP_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;p@ssw0rd1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now run the &lt;code&gt;updater&lt;/code&gt; task again. It should run without prompting you for a username or password.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compile Your CLI App to an Executable
&lt;/h2&gt;

&lt;p&gt;Want to share your CLI app with others or run the app on another computer? You can compile a Deno-powered CLI app into a standalone executable! Standalone means it can run without installing Deno or any of the libraries. Everything is bundled 📦&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pro tip:&lt;/strong&gt; Use a task scheduler to run the executable periodically.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;deno compile &lt;span class="nt"&gt;--allow-read&lt;/span&gt; &lt;span class="nt"&gt;--allow-env&lt;/span&gt; updater.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should now have an executable version!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./updater &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Among other things, you can create executables &lt;a href="https://docs.deno.com/runtime/reference/cli/compiler/#cross-compilation" rel="noopener noreferrer"&gt;for other platforms&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up and Additional CLI Tools for Deno
&lt;/h2&gt;

&lt;p&gt;Thank you for joining me on this journey of learning Deno! If you have any questions or suggestions, please drop them in the comments.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Get the &lt;a href="https://github.com/reverentgeek/deno-cli-demo" rel="noopener noreferrer"&gt;source code&lt;/a&gt; for the examples&lt;/li&gt;
&lt;li&gt;Connect with me: &lt;a href="https://x.com/reverentgeek" rel="noopener noreferrer"&gt;X/Twitter&lt;/a&gt; | &lt;a href="https://staging.bsky.app/profile/reverentgeek.com" rel="noopener noreferrer"&gt;BlueSky&lt;/a&gt; | &lt;a href="https://www.threads.net/@reverentgeek" rel="noopener noreferrer"&gt;Threads&lt;/a&gt; | &lt;a href="https://www.youtube.com/ReverentGeek" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt; | &lt;a href="https://www.linkedin.com/in/davidneal" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here are more CLI tools for Deno you might explore!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://jsr.io/@sallai/ask" rel="noopener noreferrer"&gt;ask&lt;/a&gt;: additional command-line prompts for Deno.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jsr.io/@nothing628/chalk" rel="noopener noreferrer"&gt;chalk&lt;/a&gt;: custom text styles and color for the command-line.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://cliffy.io/" rel="noopener noreferrer"&gt;cliffy&lt;/a&gt;: advanced command-line tools for Deno.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>deno</category>
      <category>cli</category>
    </item>
    <item>
      <title>Build Web Apps Absurdly Fast with Vite</title>
      <dc:creator>David Neal</dc:creator>
      <pubDate>Fri, 13 Sep 2024 11:55:29 +0000</pubDate>
      <link>https://dev.to/builderio/build-web-apps-absurdly-fast-with-vite-1dj7</link>
      <guid>https://dev.to/builderio/build-web-apps-absurdly-fast-with-vite-1dj7</guid>
      <description>&lt;p&gt;Let’s face it: the frontend build process has been slow and complicated for too long. Whether you’re dealing with Webpack, Parcel, or your custom Frankenstein monster setup (we’ve all been there), configuring and bundling a web app can be a drag.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://vitejs.dev/" rel="noopener noreferrer"&gt;Vite&lt;/a&gt; is the new kid on the block, ready to blow your mind with speed and simplicity. And Vite isn’t just important for developers; it’s a game-changer for businesses. Speedy development cycles equals happy devs. Happy devs build better code. That means faster time-to-market and lower costs for your business or clients.&lt;/p&gt;

&lt;p&gt;Vite is designed to make the pain of slow builds disappear. It takes advantage of native ES modules in the browser, eliminating the need for bundled code on dev builds. Translation: You get hot module replacement (HMR) so fast that your F5 key will feel neglected. Let’s dive into how you can make your projects Vite-powered and never look back.&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%2Fcdn.builder.io%2Fapi%2Fv1%2Fimage%2Fassets%2FTEMP%2F109e96ea21252b82d1e31b1b1b1f63aae2ee6009b7262745e0c6b253fa5e21d2%3FplaceholderIfAbsent%3Dtrue%26width%3D24" 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%2Fcdn.builder.io%2Fapi%2Fv1%2Fimage%2Fassets%2FTEMP%2F109e96ea21252b82d1e31b1b1b1f63aae2ee6009b7262745e0c6b253fa5e21d2%3FplaceholderIfAbsent%3Dtrue%26width%3D24" alt="exclamation point icon" width="18" height="18"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vite is a French word meaning “quick” or “fast.” It’s pronounced “veet” and rhymes with meet.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;span&gt;Why Vite? Ain’t nobody got time for slow builds&lt;/span&gt;
&lt;/h2&gt;

&lt;p&gt;We all know how painful it is to stare at our screen, watching that progress bar crawl along. It’s enough to make you question your life choices.&lt;/p&gt;

&lt;p&gt;Vite offers near-instantaneous cold server start, lightning-fast HMR, and builds that are quicker than you can say “npm install.”&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Lightning-fast dev startup:&lt;/strong&gt; Traditional tools like Webpack bundle all your code before serving it. Vite uses native ES modules to serve your code, so it starts fast — the browser loads only what it needs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hot Module Replacement:&lt;/strong&gt; Changes reflect instantly, making your development experience smoother than a jazz saxophone solo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Out-of-the-box TypeScript support:&lt;/strong&gt; Vite handles TypeScript without setting up a complicated build pipeline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimized builds:&lt;/strong&gt; When it’s time to ship, Vite uses Rollup to bundle your code efficiently. It splits your code into chunks, so users aren’t downloading stuff they don’t need.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  &lt;span&gt;Get started with Vite&lt;/span&gt;
&lt;/h2&gt;

&lt;p&gt;Let’s take Vite for a spin and find out what it can do!&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Set up your Vite project
&lt;/h3&gt;

&lt;p&gt;First things first, let’s create a new Vite project. Assuming you have &lt;a href="https://nodejs.org/" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; installed, open up your terminal and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm create vite@latest my-awesome-vite-project &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--template&lt;/span&gt; react
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command creates a new Vite project with &lt;a href="https://react.dev/" rel="noopener noreferrer"&gt;React&lt;/a&gt;. Feel free to replace “react” with “vue” or “vanilla” if that’s more your jam. &lt;a href="https://svelte.dev/" rel="noopener noreferrer"&gt;Svelte&lt;/a&gt;, &lt;a href="https://preactjs.com/" rel="noopener noreferrer"&gt;Preact&lt;/a&gt;, &lt;a href="https://qwik.dev/" rel="noopener noreferrer"&gt;Qwik&lt;/a&gt;, and others are supported, too!&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Install dependencies and run the dev server
&lt;/h3&gt;

&lt;p&gt;Now, let’s install our dependencies and fire up that blazing-fast dev server:&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;my-awesome-vite-project
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Boom! Your dev server is now running faster than a caffeinated squirrel. Open up your browser. Change one of the source files and marvel at the speed!&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Structure your project
&lt;/h3&gt;

&lt;p&gt;Vite gives you a pretty bare-bones structure to start with. Here’s a typical setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;my-awesome-vite-project/
├── public/
│   └── vite.svg
├── src/
│   └── assets/
│       └── react.svg
│   ├── App.css
│   ├── App.jsx
│   ├── index.css
│   └── main.jsx
├── index.html
└── vite.config.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Feel free to add more folders like &lt;code&gt;styles&lt;/code&gt;, &lt;code&gt;utils&lt;/code&gt;, or &lt;code&gt;services&lt;/code&gt;. Vite won’t judge you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Customize your Vite config
&lt;/h3&gt;

&lt;p&gt;Open up &lt;code&gt;vite.config.js&lt;/code&gt;, and let’s add a few settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&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;vite&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;react&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;@vitejs/plugin-react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nf"&gt;react&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&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;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;outDir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dist&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;sourcemap&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This config sets up your dev server port, output directory for builds, and enables source maps. There are lots more ways to configure Vite to meet your needs!&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Add some code and watch the magic happen
&lt;/h3&gt;

&lt;p&gt;Let’s create a new component to see Vite’s HMR in action. Create a new file &lt;code&gt;src/Hello.jsx&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Hello&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;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setName&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;World&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Hello, &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;!&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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="nf"&gt;setName&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;target&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="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Enter a name"&lt;/span&gt;
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, import and use this component in your &lt;code&gt;App.jsx&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;reactLogo&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;./assets/react.svg&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;viteLogo&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;../public/vite.svg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./App.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Hello&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;./Hello.jsx&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="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCount&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&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="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Hello&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"https://vitejs.dev"&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"_blank"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;viteLogo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"logo"&lt;/span&gt; &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Vite logo"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"https://react.dev"&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"_blank"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;reactLogo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"logo react"&lt;/span&gt; &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"React logo"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Vite + React&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"card"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;count&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;count&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="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          count is &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          Edit &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;code&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;src/App.jsx&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;code&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; and save to test HMR
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"read-the-docs"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Click on the Vite and React logos to learn more
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save these files and watch as Vite updates your app faster than you can say, “Why didn’t I switch to Vite sooner?”&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;span&gt;Neat stuff Vite can do&lt;/span&gt;
&lt;/h2&gt;

&lt;p&gt;Vite isn’t a one-trick pony. It’s got some cool features:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Static assets? Got you covered&lt;/strong&gt;: Vite optimizes your images and other assets automatically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment variables? Yep&lt;/strong&gt;: Use &lt;code&gt;.env&lt;/code&gt; files to manage different environments easily.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plugins? Tons of ’em&lt;/strong&gt;: Need more features? There’s probably a plugin for that.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CSS pre-processors? Sure thing&lt;/strong&gt;: Want to use Sass? Just install it and start using it. Vite figures out the rest.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Example of adding Sass to Vite
&lt;/h3&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; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; sass
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a file named &lt;code&gt;App.scss&lt;/code&gt; in the &lt;code&gt;src&lt;/code&gt; folder and add some code, such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;awesome-color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;#ff69b4&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;awesome-color&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;Update &lt;code&gt;App.jsx&lt;/code&gt; to import the new Sass file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./App.scss&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;
  
  
  &lt;span&gt;Wrapping up: Vite is sweet!&lt;/span&gt;
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Speed matters&lt;/strong&gt; – Vite’s dev server is so fast you’ll forget you ever had to refresh a page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easy setup&lt;/strong&gt; – With one simple command, you can spin up a project, ready to go.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Framework agnostic&lt;/strong&gt; – Whether you’re into React, Vue, Svelte, Preact, Solid, Qwik, or you prefer the taste of plain vanilla JavaScript, Vite has your back.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production ready&lt;/strong&gt; – Rollup optimization ensures your code is as lean and fast as possible.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, go forth and build something awesome! Vite 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%2Fcdn.builder.io%2Fapi%2Fv1%2Fimage%2Fassets%252FYJIGb4i01jvw0SRdL5Bt%252F744261e2707a434981f77a463b26d0a5%3Fwidth%3D195" 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%2Fcdn.builder.io%2Fapi%2Fv1%2Fimage%2Fassets%252FYJIGb4i01jvw0SRdL5Bt%252F744261e2707a434981f77a463b26d0a5%3Fwidth%3D195" alt="David Neal" width="195" height="195"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;span&gt;About Me&lt;/span&gt;
&lt;/h2&gt;

&lt;p&gt;Hi, I’m David! I’ve been a web developer pretty much since the beginning of the “World Wide Web”. My mission is to help folks in technology be more awesome! I also do all my own stunts… I mean… illustrations and graphics. You can learn more about me over on my website &lt;a href="//ReverentGeek.com"&gt;ReverentGeek.com.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vite</category>
      <category>webdev</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Three Ways to Share Node.js Modules Across Multiple Projects</title>
      <dc:creator>David Neal</dc:creator>
      <pubDate>Fri, 14 May 2021 16:59:46 +0000</pubDate>
      <link>https://dev.to/reverentgeek/three-ways-to-share-node-js-modules-across-multiple-projects-4cdd</link>
      <guid>https://dev.to/reverentgeek/three-ways-to-share-node-js-modules-across-multiple-projects-4cdd</guid>
      <description>&lt;p&gt;A friend recently asked me about splitting some of their Node.js application into a shared library to be used across multiple projects. There are at least three solutions, and they all have tradeoffs between convenience and portability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Move Your Code Into A Separate Project
&lt;/h2&gt;

&lt;p&gt;The first step across all the following options is to move your code into a separate Node.js project. This project will have its own &lt;code&gt;package.json&lt;/code&gt; file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 1: Link to a Local Project Folder
&lt;/h2&gt;

&lt;p&gt;Once you've moved your shared code into a separate project, link the project as a dependency using &lt;a href="https://docs.npmjs.com/cli/v7/commands/npm-link" rel="noopener noreferrer"&gt;npm link&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;npm &lt;span class="nb"&gt;link&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;../relative-path-to/library]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: The shared library can be maintained in a separate repository or the same repository as your other projects (a.k.a, &lt;em&gt;monorepo&lt;/em&gt;).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; Any changes you make to the library project will be immediately available in the other local projects that depend on it. This option is the most convenient method for local development.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt; Other developers who work on these projects will have to go through specific steps to set it up. This option is the most &lt;em&gt;inconvenient&lt;/em&gt; method for collaborating with other developers, especially if you are not using a monorepo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 2: Install From a Git Repository
&lt;/h2&gt;

&lt;p&gt;Once you've moved your shared code into a separate project, push the library code into a Git repository. Then, install the library as a dependency using &lt;a href="https://docs.npmjs.com/cli/v7/commands/npm-install" rel="noopener noreferrer"&gt;npm install&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;npm &lt;span class="nb"&gt;install&lt;/span&gt; &amp;lt;git-host&amp;gt;:&amp;lt;git-user&amp;gt;/&amp;lt;repo-name&amp;gt;
&lt;span class="c"&gt;# or&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &amp;lt;git repo url&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;To get a new version of your library into your other projects, push updates to the library repository. Then, run &lt;a href="https://docs.npmjs.com/cli/v7/commands/npm-update" rel="noopener noreferrer"&gt;npm update&lt;/a&gt; within each project to pull down those changes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: You may want to think through using a specific commit, branch, or tag to control when other projects receive updates.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; You may use private repositories with npm to keep your code safe. And, it is relatively easy for other developers to use your module, as long as they have access to your Git repositories.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt; There are more steps involved to share changes with your other projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 3: Publish to npm
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.npmjs.com/cli/v7/commands/npm-publish" rel="noopener noreferrer"&gt;Publishing a library to npm&lt;/a&gt; is not as scary as it sounds. The first step is to make sure your &lt;code&gt;package.json&lt;/code&gt; has the basic required information.&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="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my-awesome-library&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;version&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1.0.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;description&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Use this to become more awesome&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;main&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;author&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bacon McBaconFace &amp;lt;username@mydomain.com&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;license&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MIT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;repository&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;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="s2"&gt;git&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;git+https://mygithost.com/username/my-awesome-library.git&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: It's a good idea to include &lt;code&gt;README.md&lt;/code&gt; and &lt;code&gt;LICENSE&lt;/code&gt; files in your repository.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Next, publish your package to the registry.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;If you're not already signed in, you will receive a prompt to sign in or create an account.&lt;/p&gt;

&lt;p&gt;Finally, install your new package as a dependency using npm.&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;my-awesome-library
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To get a new version of your library into your other projects:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Make changes to the code&lt;/li&gt;
&lt;li&gt;Update the version number in &lt;code&gt;package.json&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;Push the updates to the git repository&lt;/li&gt;
&lt;li&gt;Publish the latest package using &lt;code&gt;npm publish&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;npm update&lt;/code&gt; within each project&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; Arguably the easiest method for collaborating with other developers since it's the same dependency pattern familiar to Node.js folks. It also increases the opportunity developers will discover your project and use it for themselves.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt; The most steps involved to get changes to your library into your other projects.&lt;/p&gt;

&lt;p&gt;Did I miss anything? What tips and tricks do you have for sharing modules across multiple projects?&lt;/p&gt;

</description>
      <category>node</category>
      <category>npm</category>
    </item>
    <item>
      <title>Create PDFs with Node.js and Puppeteer</title>
      <dc:creator>David Neal</dc:creator>
      <pubDate>Thu, 06 Feb 2020 17:41:06 +0000</pubDate>
      <link>https://dev.to/reverentgeek/convert-text-html-to-pdf-with-node-js-and-puppeteer-4c53</link>
      <guid>https://dev.to/reverentgeek/convert-text-html-to-pdf-with-node-js-and-puppeteer-4c53</guid>
      <description>&lt;p&gt;I love to play music, especially in a band. There's something amazing about the synergy of multiple people harmonizing voices and instruments. However, for a band to be successful, everyone needs to be on the &lt;em&gt;same page&lt;/em&gt; or it just sounds like a mess. &lt;/p&gt;

&lt;p&gt;Come to think of it, I could make a lot of comparisons between a group of people that play well together in a band and a productive, highly-performing software team. But, I digress. Maybe another time!&lt;/p&gt;

&lt;p&gt;One way a band to be on the &lt;em&gt;same page&lt;/em&gt; is to follow sheet music or chord charts.&lt;/p&gt;

&lt;p&gt;I recently updated a personal project named &lt;a href="https://github.com/reverentgeek/charter" rel="noopener noreferrer"&gt;Charter&lt;/a&gt; that uses Node.js to convert ChordPro formatted text files into PDF chord charts. It was a lot of fun!&lt;/p&gt;

&lt;p&gt;Now, you may be thinking, &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I'm not a musician or singer. Why do I care?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Regardless of your personal need for chord charts, you might find the source code for this project useful. Here are a few things you might learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a CLI app using Node.js and Yargs&lt;/li&gt;
&lt;li&gt;Load a text file and parse text&lt;/li&gt;
&lt;li&gt;Use Jest to test Node.js code&lt;/li&gt;
&lt;li&gt;Use handlebars to turn text into HTML&lt;/li&gt;
&lt;li&gt;Use Puppeteer to convert HTML into a PDF&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Regarding PDF rendering, I tried lots of solutions including &lt;a href="https://wkhtmltopdf.org/" rel="noopener noreferrer"&gt;wkhtmltopdf&lt;/a&gt;, &lt;a href="https://www.npmjs.com/package/electron-pdf" rel="noopener noreferrer"&gt;electron-pdf&lt;/a&gt;, &lt;a href="https://phantomjs.org/" rel="noopener noreferrer"&gt;phantomjs&lt;/a&gt;, &lt;a href="https://stackoverflow.com/questions/46077392/additional-options-in-chrome-headless-print-to-pdf" rel="noopener noreferrer"&gt;automating Chrome&lt;/a&gt; with command-line switches, and finally landed on using the latest version of &lt;a href="https://developers.google.com/web/tools/puppeteer" rel="noopener noreferrer"&gt;Puppeteer&lt;/a&gt;. Puppeteer supports a &lt;a href="https://github.com/puppeteer/puppeteer/blob/v2.1.1/docs/api.md#pagepdfoptions" rel="noopener noreferrer"&gt;ton of options&lt;/a&gt; for generating PDFs. There's a lot of untapped potential here for doing lots of cool things!&lt;/p&gt;

&lt;h2&gt;
  
  
  Save Any Web Page to PDF using Node.js
&lt;/h2&gt;

&lt;p&gt;Assuming you already have &lt;a href="https://nodejs.org" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; installed, open your terminal or command line, create a new project folder, and initialize it.&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;mkdir &lt;/span&gt;pdf-test
&lt;span class="nb"&gt;cd &lt;/span&gt;pdf-test
npm init &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, install Puppeteer as a dependency.&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;puppeteer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is an example of how to use Puppeteer to turn the Google home page into a PDF. Create a new file named &lt;code&gt;index.js&lt;/code&gt; and paste 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="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;const&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&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;puppeteer&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="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;browser&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;puppeteer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&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;page&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;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&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;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&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://google.com&lt;/span&gt;&lt;span class="dl"&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;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./google.pdf&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Letter&lt;/span&gt;&lt;span class="dl"&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;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;Run the application using 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;node &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can change URL to something else, such as the dev.to home page. Keep in mind that some pages load dynamically with JavaScript, so without setting more options, the resulting PDF could appear incomplete.&lt;/p&gt;

&lt;h2&gt;
  
  
  Convert a Local HTML File to PDF
&lt;/h2&gt;

&lt;p&gt;Puppeteer is not limited to loading web pages. You can also load local HTML files. This is how the Charter application creates a chord chart. The Charter app first parses a ChordPro text file, generates an HTML file, and then uses Puppeteer to render the HTML and save as a PDF.&lt;/p&gt;

&lt;p&gt;Create a new file named &lt;code&gt;sample.html&lt;/code&gt; and paste the following HTML.&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;style&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="o"&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;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Verdana&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Arial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Helvetica&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&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;h1&amp;gt;&lt;/span&gt;Hello World!&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Yay! My own PDF generator!&lt;span class="nt"&gt;&amp;lt;/p&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;Create a new file named &lt;code&gt;local.js&lt;/code&gt; and paste 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="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;const&lt;/span&gt; &lt;span class="nx"&gt;path&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;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;puppeteer&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;puppeteer&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="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;htmlFile&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="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./sample.html&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;browser&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;puppeteer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&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;page&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;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&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;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;file://&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;htmlFile&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;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./sample.pdf&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Letter&lt;/span&gt;&lt;span class="dl"&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;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;Next, run the code 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;node local.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should now have your very own "Hello World!" PDF in your project folder!&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Chord Chart with Charter
&lt;/h2&gt;

&lt;p&gt;If you have Node.js 12.x or higher installed and want to take the CLI app for a spin, you can use &lt;code&gt;npx&lt;/code&gt; to run the application directly. Of course, you will need a ChordPro text file to test. Here's an example, or you can find others on the Internet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{title: Amazing Grace}
{artist: Words by: John Newton, John P. Rees}
{artist: Music by: William W. Walker, Edwin Othello Excell}
{key: G}  
{tempo: 90}
{time: 3/4&amp;nbsp;}

{comment: Verse 1}
A - [G]mazing [G/D]grace  [D7]how  [Em]sweet [C]the   [G]sound
That [G]saved a [G/D]wretch  [D/C]like    [G/B]me
I [G]once was [G/B]lost but [C]now am  [G]found
[G/B]Was   [Em]blind but [G/D]now    [D7]I    [G]see
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;npx&lt;/code&gt; will take a little while to download the first time, due to Puppeteer using Chromium.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx chord-charter &lt;span class="nt"&gt;-f&lt;/span&gt; amazing-grace.chordpro
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I hope you find the project useful! Get out there and be awesome!&lt;/p&gt;

</description>
      <category>node</category>
    </item>
    <item>
      <title>My Most Embarrassing Mistakes as a Programmer</title>
      <dc:creator>David Neal</dc:creator>
      <pubDate>Wed, 30 Oct 2019 20:07:45 +0000</pubDate>
      <link>https://dev.to/reverentgeek/my-most-embarrassing-mistakes-as-a-programmer-1c2p</link>
      <guid>https://dev.to/reverentgeek/my-most-embarrassing-mistakes-as-a-programmer-1c2p</guid>
      <description>&lt;p&gt;My most embarrassing mistakes as a programmer, in no particular order:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I didn't ask for help&lt;/li&gt;
&lt;li&gt;I was afraid of what others would think&lt;/li&gt;
&lt;li&gt;I didn't follow through on a promise&lt;/li&gt;
&lt;li&gt;I didn't step up and take responsibility&lt;/li&gt;
&lt;li&gt;I avoided conflict&lt;/li&gt;
&lt;li&gt;I didn't encourage others&lt;/li&gt;
&lt;li&gt;I didn't publicly celebrate the success of others&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My biggest mistakes had nothing to do with programming.&lt;/p&gt;

&lt;p&gt;The most difficult parts of any job, and the most &lt;em&gt;valuable&lt;/em&gt; activities of any job, have nothing at all to do with technology.&lt;/p&gt;

&lt;p&gt;It's people. How we value ourselves. How we value others.&lt;/p&gt;

&lt;p&gt;"Soft skills" is an awful term. The things we call "soft skills," such as listening, communication, team dynamics, empathy, ought to be given so much more priority in our workplaces. These are &lt;em&gt;core&lt;/em&gt; skills that apply to anyone in any profession.&lt;/p&gt;

&lt;p&gt;Here's the mindset I want. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To be the most effective member of any team, I must first strive to model integrity and work ethic. I need to be respectful, helpful, patient, gracious, trustworthy, and hardworking. I need to be transparent, such as asking for help, owning mistakes, and asking forgiveness, regardless of my fear of the outcome, because it's the right thing to do. When I see a failure in our system, not just in the work we create but also our system of work and treatment of people, I need to take responsibility, speak up, and see it through correction. I need to give people the benefit of the doubt, try to understand their perspective and circumstance, be slow to anger, and be quick to forgive. I need to encourage and lift others up and help them to see their career and life through a bigger lens.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I am a work in progress. I may not achieve these things every day, but that's my goal.&lt;/p&gt;

&lt;p&gt;I hope this post gets you thinking of ways you can have a positive impact on the people you work with.&lt;/p&gt;

&lt;p&gt;Remember, every day is a fresh start to be more awesome!&lt;/p&gt;

</description>
      <category>team</category>
      <category>leadership</category>
      <category>career</category>
    </item>
    <item>
      <title>A Note of Encouragement</title>
      <dc:creator>David Neal</dc:creator>
      <pubDate>Wed, 23 Oct 2019 18:57:02 +0000</pubDate>
      <link>https://dev.to/reverentgeek/a-note-of-encouragement-1api</link>
      <guid>https://dev.to/reverentgeek/a-note-of-encouragement-1api</guid>
      <description>&lt;p&gt;Earlier this year, I set up a new computer for my son. In the process, I created an "Easter Egg" for him to find. I left a note on the desktop with some instructions on how to run a command-line interface (CLI) application I created.&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;&amp;gt;&lt;/span&gt; heybuddy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9e9fzs546xnsvalxnigy.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9e9fzs546xnsvalxnigy.png" alt="Hey buddy, I just want you to know... I know you can do it. Don't give up." width="712" height="180"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As I hoped, I'll sometimes walk by and see he has a terminal window open and has run the app several times 😊&lt;/p&gt;

&lt;p&gt;The source code for this application is on &lt;a href="https://github.com/reverentgeek/notes-of-encouragement-cli" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. It's written in JavaScript and uses &lt;a href="https://nodejs.org" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt;. There are instructions in the &lt;code&gt;readme&lt;/code&gt; on how to customize the messages, change the name of the CLI app itself, and install it on someone's computer.&lt;/p&gt;

&lt;p&gt;You can certainly take the idea and create your own customized encouragement CLI app, or take it further to create a web, desktop, or mobile application. Or, how about an encouragement robot!&lt;/p&gt;

&lt;p&gt;Use the power of technology to make a positive impact on the people you care about! Let me know if there's anything I can do to help 🙂 Thanks for reading, and I hope you have an awesome day!&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9ef5x0acqqidye8kitua.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9ef5x0acqqidye8kitua.jpg" alt="Get out there and be awesome!" width="800" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
      <category>cli</category>
    </item>
    <item>
      <title>Respect: The Most Fundamental Principle</title>
      <dc:creator>David Neal</dc:creator>
      <pubDate>Tue, 14 May 2019 13:16:08 +0000</pubDate>
      <link>https://dev.to/reverentgeek/respect-the-most-fundamental-principle-5hgn</link>
      <guid>https://dev.to/reverentgeek/respect-the-most-fundamental-principle-5hgn</guid>
      <description>&lt;p&gt;Much has been said about "lean" and "agile" approaches to work. Reflecting over more than ten years of personal experience, I have come to this conclusion: Respect for people is the most fundamental principle. When respect is the lens and filter through which decisions are made, there is a much higher chance for success.&lt;/p&gt;

&lt;p&gt;Having respect for people:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;requires listening, empathy, and deeper understanding&lt;/li&gt;
&lt;li&gt;prioritizes health, family, and well-being&lt;/li&gt;
&lt;li&gt;promotes connections and relationships&lt;/li&gt;
&lt;li&gt;fosters community, commitment, and loyalty&lt;/li&gt;
&lt;li&gt;empowers employees to make positive changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When respect is modeled top-down, leadership is motivated to maximize focus and alignment, minimize wasteful activities, and reduce friction. Leadership is obligated to make better decisions for the sustainability of the company because its people deserve stability, work-life balance, and personal/professional development.&lt;/p&gt;

&lt;p&gt;Employees are motivated to do their best work. Mutual respect helps individuals come together as a team, investing in one another, and fighting for each other's success. &lt;/p&gt;

&lt;p&gt;When conflicts arise, respect allows people to focus on solving the issue rather than attacking individuals.&lt;/p&gt;

&lt;p&gt;People are people. They are not your best "assets." They are not fungible resources. &lt;/p&gt;

&lt;p&gt;The "Golden Rule" is still the golden rule.&lt;/p&gt;

</description>
      <category>lean</category>
      <category>agile</category>
      <category>teams</category>
      <category>leadership</category>
    </item>
    <item>
      <title>Makin' Bacon with Node.js, Hapi, and Vue</title>
      <dc:creator>David Neal</dc:creator>
      <pubDate>Wed, 18 Jul 2018 16:46:50 +0000</pubDate>
      <link>https://dev.to/reverentgeek/makin-bacon-with-nodejs-hapi-and-vue-19d7</link>
      <guid>https://dev.to/reverentgeek/makin-bacon-with-nodejs-hapi-and-vue-19d7</guid>
      <description>&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%2Fwfe57xl2uz9sd24cqgkm.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%2Fwfe57xl2uz9sd24cqgkm.png" alt="Makin' Bacon" width="800" height="835"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I recently created a new demo for my &lt;a href="https://speakerdeck.com/reverentgeek/node-dot-js-crash-course-kcdc-2018" rel="noopener noreferrer"&gt;Node.js Crash Course&lt;/a&gt; talk that I've given at several conferences. This application is a "bacon ipsum" generator using &lt;a href="https://nodejs.org/" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt;, &lt;a href="https://hapijs.com/" rel="noopener noreferrer"&gt;Hapi&lt;/a&gt;, and &lt;a href="https://vuejs.org/" rel="noopener noreferrer"&gt;Vue&lt;/a&gt;. I know I'm not the first to come up with the idea of a "bacon ipsum" generator, but I thought it'd be a fun project to create one in Node.js.&lt;/p&gt;

&lt;p&gt;API Requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using a list of bacon names and other associated words...&lt;/li&gt;
&lt;li&gt;Return a specified number of paragraphs, maximum of 25&lt;/li&gt;
&lt;li&gt;Paragraphs should consist of between 3 to 6 sentences&lt;/li&gt;
&lt;li&gt;Each sentence should be capitalized&lt;/li&gt;
&lt;li&gt;A sentence should end with random punctuation, weighted towards more frequent use of periods&lt;/li&gt;
&lt;li&gt;A sentence should consist of between 4 to 12 unique words&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;UI Requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Customer can choose to generate 1 to 21 "pounds" of bacon&lt;/li&gt;
&lt;li&gt;Customer can easily copy the generated text to their clipboard&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://node-bacon-generator.herokuapp.com/" rel="noopener noreferrer"&gt;See it in action!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you click on the "Make the Bacon!" button, the Vue application uses &lt;a href="https://www.npmjs.com/package/axios" rel="noopener noreferrer"&gt;Axios&lt;/a&gt; to call the API for bacon. When the API call returns, the Vue app updates its state with the array of paragraphs. This triggers re-rendering the UI to list the paragraphs and show the "Copy to the clipboard!" button.&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;makeTheBacon&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="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/bacon/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;numberOfPounds&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;res&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paragraphs&lt;/span&gt; &lt;span class="o"&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;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paragraphs&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;h3&gt;
  
  
  Vue computed properties
&lt;/h3&gt;

&lt;p&gt;The Vue app uses a computed property, &lt;code&gt;hazBacon&lt;/code&gt;, to show/hide the "Copy to the clipboard!" button based on there being any paragraphs of bacon text to display.&lt;/p&gt;

&lt;p&gt;When copying text to the clipboard, the Vue app uses another computed property, &lt;code&gt;paragraphText&lt;/code&gt;, to join the array of paragraphs together into a single string.&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;computed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;paragraphText&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="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paragraphs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="dl"&gt;"&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="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;hazBacon&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="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paragraphs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;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="nx"&gt;poundText&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="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;numberOfPounds&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pound&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pounds&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;h3&gt;
  
  
  Other dependencies
&lt;/h3&gt;

&lt;p&gt;In addition to Node.js, Hapi, and Vue, here are the dependencies and plugins used by the project. Some of these are my current favorites for building Node.js applications.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/vue-clipboard2" rel="noopener noreferrer"&gt;Vue-Clipboard2&lt;/a&gt; - Vue component used to copy text to the clipboard&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/axios" rel="noopener noreferrer"&gt;Axios&lt;/a&gt; - HTTP client for browsers or Node.js&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/fs-extra" rel="noopener noreferrer"&gt;fs-extra&lt;/a&gt; - Promise-based file system module&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/joi" rel="noopener noreferrer"&gt;Joi&lt;/a&gt; - Object schema validation plugin for Hapi&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/eslint" rel="noopener noreferrer"&gt;ESLint&lt;/a&gt; - JavaScript linting&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/nodemon" rel="noopener noreferrer"&gt;Nodemon&lt;/a&gt; - Developer tool that automatically restarts the application when any change is made to source code&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/boom" rel="noopener noreferrer"&gt;Boom&lt;/a&gt; - Hapi plugin for returning errors&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/hapi-pino" rel="noopener noreferrer"&gt;hapi-pino&lt;/a&gt; - Pino logging plugin for Hapi&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/inert" rel="noopener noreferrer"&gt;Inert&lt;/a&gt; - Static resource plugin for Hapi&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/lab" rel="noopener noreferrer"&gt;Lab&lt;/a&gt; and &lt;a href="https://www.npmjs.com/package/code" rel="noopener noreferrer"&gt;Code&lt;/a&gt; - Test and assertion utilities for Hapi&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a alt="Check out the source!" href="https://github.com/reverentgeek/node-bacon-generator" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fkik97ugyk5bo4bai45qo.png" width="800" height="233"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;
&lt;a href="https://github.com/reverentgeek/node-bacon-generator" rel="noopener noreferrer"&gt;github.com/reverentgeek/node-bacon-generator&lt;/a&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Happy computering!
&lt;/h3&gt;

</description>
      <category>node</category>
      <category>hapi</category>
      <category>vue</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Choose Your Own Framework: The Curse of JavaScript Fatigue</title>
      <dc:creator>David Neal</dc:creator>
      <pubDate>Wed, 13 Jun 2018 17:13:03 +0000</pubDate>
      <link>https://dev.to/reverentgeek/choose-your-own-framework-the-curse-of-javascript-fatigue-1j5g</link>
      <guid>https://dev.to/reverentgeek/choose-your-own-framework-the-curse-of-javascript-fatigue-1j5g</guid>
      <description>&lt;p&gt;How will your project die? Choose from 2,147,483,647 possible endings!&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%2F7d82a7ths1lk9k0x89n5.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%2F7d82a7ths1lk9k0x89n5.jpg" alt="How will your project die?" width="800" height="1137"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>humor</category>
      <category>funny</category>
      <category>javascript</category>
    </item>
    <item>
      <title>The MONSTER at the end of this Book</title>
      <dc:creator>David Neal</dc:creator>
      <pubDate>Fri, 25 May 2018 22:32:13 +0000</pubDate>
      <link>https://dev.to/reverentgeek/the-monster-at-the-end-of-this-book-2oca</link>
      <guid>https://dev.to/reverentgeek/the-monster-at-the-end-of-this-book-2oca</guid>
      <description>&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%2F8mmydpz9tikq3eqw1g2r.jpeg" 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%2F8mmydpz9tikq3eqw1g2r.jpeg" width="800" height="923"&gt;&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%2F9jg6e8q2pi4pkwt72p9d.jpeg" 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%2F9jg6e8q2pi4pkwt72p9d.jpeg" width="800" height="578"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>leadership</category>
      <category>career</category>
      <category>funny</category>
    </item>
    <item>
      <title>5 Essential Ingredients for an Awesome Tech Talk</title>
      <dc:creator>David Neal</dc:creator>
      <pubDate>Mon, 01 Jan 2018 20:27:49 +0000</pubDate>
      <link>https://dev.to/reverentgeek/5-essential-ingredients-for-an-awesome-tech-talk-1caa</link>
      <guid>https://dev.to/reverentgeek/5-essential-ingredients-for-an-awesome-tech-talk-1caa</guid>
      <description>&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fvuust9ei2s6ps8bil3qm.jpeg" 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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fvuust9ei2s6ps8bil3qm.jpeg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I believe one of the big reasons people are afraid of public speaking (or writing) is &lt;a href="https://en.wikipedia.org/wiki/Impostor_syndrome" rel="noopener noreferrer"&gt;impostor syndrome&lt;/a&gt;. You may think…&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I’m not an expert! I barely know anything! Everyone will know I’m a fraud!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Psst… here’s a secret: You don’t have to be an expert!&lt;/p&gt;

&lt;p&gt;In fact, you have something no one else in the entire world has. &lt;em&gt;Your&lt;/em&gt; experience. &lt;/p&gt;

&lt;p&gt;Here’s a recipe for turning your experience into an awesome presentation!&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Why did you choose this technology?
&lt;/h2&gt;

&lt;p&gt;People can argue semantics, “best practices,” architecture, speed, or “Technology A” vs. “Technology B” all day long. But, no one can dispute &lt;em&gt;your&lt;/em&gt; experience. Tell people your story. What was your motivation for choosing this technology, or for replacing “Technology A” with “Technology B?” What problems were you trying to solve? Chances are, your reasons “why” will resonate with other people in similar situations.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. What does this technology do?
&lt;/h2&gt;

&lt;p&gt;Do some research and put together a short list of highlights, features, and capabilities. This is your opportunity to geek out and dive a little deeper into this technology beyond your particular use case. Seek to find other scenarios where this technology is a good fit. This information can help bridge gaps for your audience.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. How did you make it work?
&lt;/h2&gt;

&lt;p&gt;Give your audience more of your story. They want to hear about the challenges and issues you faced along the way. Most of all, they want your insights! You learned the hard way what worked and what didn’t work, so share the good stuff! Including…&lt;/p&gt;

&lt;h3&gt;
  
  
  What mistakes did you make?
&lt;/h3&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Feb6rvehvv10dxe04t87y.jpeg" 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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Feb6rvehvv10dxe04t87y.jpeg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;No way! I already feel like an impostor, and now you want me to remove any doubt?!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You might think admitting your mistakes will harm your reputation or credibility. Nope! Being transparent about your mistakes and what you learned from them &lt;em&gt;adds&lt;/em&gt; to your credibility! I don’t know about you, but I make mistakes all the time. I want to hear from &lt;em&gt;real&lt;/em&gt; humans doing &lt;em&gt;real&lt;/em&gt; work -- people I can relate to.&lt;/p&gt;

&lt;p&gt;If you were to do it all over, what would you do differently? Turn your challenges and mistakes into key takeaways for your audience!&lt;/p&gt;

&lt;h3&gt;
  
  
  Demo, if you must
&lt;/h3&gt;

&lt;p&gt;A good demo can be the final puzzle piece needed to convince someone to try this technology. A working demo is great, but so are screen shots or a recorded video!&lt;/p&gt;

&lt;p&gt;For the love of all that is holy, don’t do any live coding unless you are specifically demoing features of an IDE!&lt;/p&gt;

&lt;p&gt;Your demo should only exist to reinforce your “why” for choosing this technology. Make it short and to the point, and show them something awesome! Don’t waste your audience’s time showing “how.” They can easily find that information for themselves on the Internet later. Your job is to convince them to go try it!&lt;/p&gt;

&lt;p&gt;In my opinion, no demo is better than a bad one.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Where to get started?
&lt;/h2&gt;

&lt;p&gt;You did the research. You made it work. Give your audience links to put them on the fast track! Better yet, give them &lt;em&gt;one&lt;/em&gt; link where they can go to get your slides, your contact information, and a list of all the resources you recommend.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Drop the mic*
&lt;/h2&gt;

&lt;p&gt;Wrap it up. Send them off with a call to action and high-fives!&lt;/p&gt;

&lt;h2&gt;
  
  
  Bake in the oven at 350 for 45 minutes
&lt;/h2&gt;

&lt;p&gt;Imagine yourself in the future. You just finished giving a talk covering these five essential ingredients. Guess what?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;BOOM.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You just gave an awesome talk. Your audience is now walking away with &lt;em&gt;valuable&lt;/em&gt; information they could not have found anywhere else.&lt;/p&gt;

&lt;p&gt;Now, go make it happen!&lt;/p&gt;

&lt;p&gt;*Metaphorically speaking. Real microphones are fragile, and expensive. Just ask &lt;a href="https://twitter.com/kylewelch" rel="noopener noreferrer"&gt;Kyle Welch&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>publicspeaking</category>
      <category>technology</category>
      <category>impostorsyndrome</category>
      <category>community</category>
    </item>
  </channel>
</rss>
