<?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: Matthew Jacobs</title>
    <description>The latest articles on DEV Community by Matthew Jacobs (@codingmatty).</description>
    <link>https://dev.to/codingmatty</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F136524%2Fadbb0f5c-6a4c-4034-b348-43274cb78e19.jpg</url>
      <title>DEV Community: Matthew Jacobs</title>
      <link>https://dev.to/codingmatty</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/codingmatty"/>
    <language>en</language>
    <item>
      <title>Deploying Your First App with Dokku: A Step-By-Step Guide</title>
      <dc:creator>Matthew Jacobs</dc:creator>
      <pubDate>Tue, 30 Apr 2024 12:00:23 +0000</pubDate>
      <link>https://dev.to/codingmatty/deploying-your-first-app-with-dokku-a-step-by-step-guide-724</link>
      <guid>https://dev.to/codingmatty/deploying-your-first-app-with-dokku-a-step-by-step-guide-724</guid>
      <description>&lt;p&gt;TL;DR Use this gist as a reference for all of the steps found in this article: &lt;a href="https://gist.github.com/codingmatty/05922f9dc5dcb75bd8495d62b4211e27?ref=codingmatty.com"&gt;dokku-setup.sh&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_0s6ZgLe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.codingmatty.com/content/images/2024/04/DALL-E-2024-04-27-17.07.58---A-digital-illustration-for-a-blog-post-about-self-hosting-applications.-The-image-features-a-modern-home-office-setup-with-a-sleek-computer-displaying.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_0s6ZgLe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.codingmatty.com/content/images/2024/04/DALL-E-2024-04-27-17.07.58---A-digital-illustration-for-a-blog-post-about-self-hosting-applications.-The-image-features-a-modern-home-office-setup-with-a-sleek-computer-displaying.webp" alt="Deploying Your First App with Dokku: A Step-By-Step Guide" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dokku.com/?ref=codingmatty.com"&gt;Dokku&lt;/a&gt; is a command-line application that makes deploying web applications and databases on your server (or &lt;a href="https://cloud.google.com/learn/what-is-a-virtual-private-server?ref=codingmatty.com"&gt;VPS&lt;/a&gt;) simple. It describes itself as&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;An open source PAAS alternative to Heroku.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It doesn't have an official UI, which makes it a little less inviting to newcomers, but I believe it has actually helped me become more comfortable with the command line. And once you set up one application, it gets easier and easier.&lt;/p&gt;

&lt;p&gt;I recently tried to install and use &lt;a href="https://coolify.io/?ref=codingmatty.com"&gt;Coolify&lt;/a&gt;, and if you need a UI to understand everything, I get it. I was hopeful that I would like it, but I felt lost when I attempted to use it, and it requires &lt;strong&gt;root&lt;/strong&gt; access to control Docker, which I did not feel comfortable with.&lt;br&gt;&lt;br&gt;
There are some unofficial UIs for Dokku, but I haven't tried them out, and they don't seem current.&lt;/p&gt;



&lt;p&gt;I have been a proponent of self-hosting for a long time. I had an OwnCloud instance and Plex setup back in 2018. But after a few failed hard drives (and the realization that I only ever watch movies once), I decided to spin everything down and use commercial software that is cheap (free) and has built-in redundancies that I don't have to worry about.&lt;/p&gt;

&lt;p&gt;But in 2021, my friend got me interested in &lt;a href="https://www.chia.net/?ref=codingmatty.com"&gt;Chia&lt;/a&gt;, and now I have a server sitting in my office "farming," which doesn't take a lot of CPU or memory, so I decided to start hosting my custom-built budget app so I wouldn't have to pay for hosting.&lt;/p&gt;

&lt;p&gt;Fast forward to today, and I have about six applications running on this server that I use often. Some of them I built for my own use, and some are open-source projects.&lt;/p&gt;
&lt;h2&gt;
  
  
  Server Setup
&lt;/h2&gt;

&lt;p&gt;This article assumes you already have a server or VPS setup, but I wanted to explain how I set up my server.&lt;/p&gt;
&lt;h3&gt;
  
  
  Physical
&lt;/h3&gt;

&lt;p&gt;These specs seem to be more than enough to host quite a bit of applications without concern:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;256 GB SSD&lt;/li&gt;
&lt;li&gt;16 GB RAM&lt;/li&gt;
&lt;li&gt;3.6GHz 6-Core CPU&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Software
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Ubuntu Server&lt;/li&gt;
&lt;li&gt;UFW (Firewall)&lt;/li&gt;
&lt;li&gt;Dokku Version 0.34.4 (&lt;a href="https://dokku.com/docs/getting-started/installation/?ref=codingmatty.com"&gt;Installation Instructions&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Topology
&lt;/h3&gt;

&lt;p&gt;In this diagram, you can see how I have my server set up behind &lt;a href="//cloudflare.com"&gt;Cloudflare&lt;/a&gt; using their &lt;a href="https://www.cloudflare.com/application-services/products/dns/?ref=codingmatty.com"&gt;DNS tool&lt;/a&gt; to avoid exposing my IP address and help cache assets to limit the need to make direct calls to my server.&lt;/p&gt;

&lt;p&gt;NGINX is running as my reverse proxy, directing a domain name to a specific container. &lt;a href="https://www.docker.com/?ref=codingmatty.com"&gt;Docker&lt;/a&gt; runs the applications and databases as isolated containers. These applications are independent of Dokku but are effectively controlled by Dokku, so we don't need to do anything with them directly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Rtl_1zsq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.codingmatty.com/content/images/2024/04/image-5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Rtl_1zsq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.codingmatty.com/content/images/2024/04/image-5.png" alt="Server Topology" width="800" height="881"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Deploying an Application
&lt;/h2&gt;

&lt;p&gt;For this article, we'll deploy an application that has been pretty useful to me since I recently installed it: &lt;a href="https://linkwarden.app/?ref=codingmatty.com"&gt;Linkwarden&lt;/a&gt;. Linkwarden is a web-based bookmarks manager that allows you to collaborate, organize, and archive content from links in one place.&lt;/p&gt;

&lt;p&gt;The basics of this installation will be to create an application pointing to a git repository with a Dockerfile that will build the container that runs the application.&lt;/p&gt;

&lt;p&gt;While this article assumes you have installed Dokku using the &lt;a href="https://dokku.com/docs/getting-started/installation/?ref=codingmatty.com"&gt;instructions&lt;/a&gt; on their website, there are a few configuration steps that we want to run before continuing. You shouldn't need to execute these commands again if you have already run them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dokku plugin:install postgres
dokku letsencrypt:set --global email &amp;lt;your-email@email.com&amp;gt;
dokku letsencrypt:cron-job --add
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first command will install the Postgres plugin so we can use it to set up a Postgres instance that will be independent of the actual application. The other commands set up &lt;a href="https://letsencrypt.org/?ref=codingmatty.com"&gt;LetsEncrypt&lt;/a&gt; so we can fetch an SSL certificate once, and it will automatically renew itself.&lt;/p&gt;




&lt;p&gt;The first thing we'll do is create the application Docker container and the database container and link them together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dokku apps:create linkwarden
dokku postgres:create linkwarden-db
dokku postgres:link linkwarden-db linkwarden
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;dokku postgres:link&lt;/code&gt; line adds an environment variable, &lt;code&gt;DATABASE_URL&lt;/code&gt; to the &lt;code&gt;linkwarden&lt;/code&gt; application container.&lt;/p&gt;

&lt;p&gt;This next step is something specific to Linkwarden, but it is helpful to know for any application that may be storing files is to point the data directory of the Docker container to a directory on the local machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo mkdir -p /opt/linkwarden/data
dokku storage:mount linkwarden /opt/linkwarden/data:/data/data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you ever need to rebuild the application from scratch, you won't lose the files that Linkwarden has saved. In this case, it's archived downloads of the link content.&lt;/p&gt;

&lt;p&gt;Now, let's point the application container to the git repository, set the necessary environment variable, and then build it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dokku git:sync linkwarden git@github.com:linkwarden/linkwarden.git main
dokku config:set linkwarden NEXTAUTH_SECRET=`openssl rand -hex 32`
dokku ps:rebuild linkwarden
dokku ports:add linkwarden http:80:5000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;NEXTAUTH_SECRET='openssl rand -hex 32'&lt;/code&gt; sets &lt;code&gt;NEXTAUTH_SECRET&lt;/code&gt; to a randomly generated string and avoids exposing secrets in the command line.&lt;/p&gt;

&lt;p&gt;Once you run &lt;code&gt;dokku ps:rebuild&lt;/code&gt; Dokku will pull down the necessary images based on the Dockerfile in the git repository and attempt to build and start up the application.&lt;/p&gt;

&lt;p&gt;That last line sets up &lt;code&gt;nginx&lt;/code&gt;, which is the reverse proxy that Dokku uses to point port 80 from your machine to port 5000 of the application container.&lt;/p&gt;

&lt;p&gt;If you don't know what port the application container is exposing, you can run &lt;code&gt;dokku ports:report&lt;/code&gt; to see the detected ports being exposed and copy that config to the &lt;code&gt;dokku ports:add&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;The last thing we need to do is set up a domain to be able to access the application. Dokku does have a global domain based on the machine name by default, and then each app would automatically be set to &lt;code&gt;&amp;lt;app-name&amp;gt;.global-domain.tld&lt;/code&gt; but I deactivated that (&lt;code&gt;dokku domains:clear-global&lt;/code&gt;) to be able to add specific domains or subdomains manually.&lt;/p&gt;

&lt;p&gt;This next part is a little complicated and probably causes me the most headaches because you need to make sure port 80 is accessible from the internet to be able to get an SSL certificate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Disable enforced SSL in Cloudflare&lt;/li&gt;
&lt;li&gt;Open up Port 80 in your NAT/Router&lt;/li&gt;
&lt;li&gt;Enable Port 80 via UFW (&lt;code&gt;sudo ufw allow 80&lt;/code&gt;)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dokku domains:add linkwarden linkwarden.your-domain.com
dokku letsencrypt:enabled linkwarden
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you run into issues, I would be careful not to attempt to run the &lt;code&gt;letsencrypt&lt;/code&gt; command too much because there is a rate limit of 5 times per hour per email and domain combination.&lt;/p&gt;

&lt;p&gt;Success will look something like the image below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RkCtmFZS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.codingmatty.com/content/images/2024/04/CleanShot-2024-04-28-at-14.16.20%402x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RkCtmFZS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.codingmatty.com/content/images/2024/04/CleanShot-2024-04-28-at-14.16.20%402x.png" alt="Successful SSL Certificate Generation" width="800" height="622"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If your DNS is not set up correctly, you may see something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2024/04/28 18:17:22 Could not obtain certificates:
    error: one or more domains had a problem:
[linkwarden-test.codingmatty.com] acme: error: 400 :: urn:ietf:params:acme:error:dns :: DNS problem: NXDOMAIN looking up A for linkwarden-test.codingmatty.com - check that a DNS record exists for this domain; DNS problem: NXDOMAIN looking up AAAA for linkwarden-test.codingmatty.com - check that a DNS record exists for this domain
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your port 80 isn't open, you may see an error message like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2024/04/28 18:18:48 Could not obtain certificates:
    error: one or more domains had a problem:
[linkwarden-test.codingmatty.com] acme: error: 403 :: urn:ietf:params:acme:error:unauthorized :: 2606:4700:3032::6815:4638: Invalid response from http://linkwarden-test.codingmatty.com/.well-known/acme-challenge/n9q8vjktpRkDGq5bKU2cvRLAGqkJy-3Lc2taCCyaxvw: 522
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you have successfully received your SSL certificate, Dokku will automatically add port 443 to point to the same port of the Docker container that port 80 was pointing to with HTTPS enabled. You can validate this by running &lt;code&gt;dokku ports:report&lt;/code&gt; and seeing &lt;code&gt;https:443:5000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I strongly recommend removing access to port 80 by reversing all of the previous steps to expose it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enforce SSL in Cloudflare&lt;/li&gt;
&lt;li&gt;Close Port 80 in your NAT/Router&lt;/li&gt;
&lt;li&gt;Disable Port 80 via UFW (&lt;code&gt;sudo ufw deny 80&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dokku ports:remove http:80:5000&lt;/code&gt; to remove the port being directed to the Docker container &lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;That's it! You now have a running instance of Linkwarden 🎉&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--95iUdO7w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.codingmatty.com/content/images/2024/04/image-3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--95iUdO7w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.codingmatty.com/content/images/2024/04/image-3.png" alt="Linkwarden Login Page" width="800" height="673"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this case, you should sign up, and then, if you want to prevent anyone else from signing up, go ahead and add the proper environment variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dokku config:set linkwarden NEXT_PUBLIC_DISABLE_REGISTRATION=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will rebuild the application container and prevent any future sign-ups. Similar options are pretty typical with many self-hostable applications with login screens.&lt;/p&gt;




&lt;p&gt;I hope this post helps you get up and running with Dokku. I would love to hear about what you've deployed on your server 😄&lt;/p&gt;

&lt;p&gt;Let me know if you have any questions.&lt;/p&gt;

&lt;p&gt;Find me on &lt;a href="https://www.threads.net/@codingmatty?ref=codingmatty.com"&gt;Threads&lt;/a&gt; or email me at &lt;a href="//mailto:codingmatty@gmail.com"&gt;codingmatty@gmail.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>tech</category>
    </item>
    <item>
      <title>Type Strict Environment Variables</title>
      <dc:creator>Matthew Jacobs</dc:creator>
      <pubDate>Sat, 22 Oct 2022 15:11:09 +0000</pubDate>
      <link>https://dev.to/codingmatty/type-strict-environment-variables-1686</link>
      <guid>https://dev.to/codingmatty/type-strict-environment-variables-1686</guid>
      <description>&lt;p&gt;I love Typescript.&lt;/p&gt;

&lt;p&gt;And Environment variables are one of the essential parts of storing run-time configuration securely.&lt;/p&gt;

&lt;p&gt;But one of the most significant issues with using environment variables within a Typescript application is the lack of types.&lt;/p&gt;

&lt;p&gt;Let's say you are trying to use the &lt;code&gt;PORT&lt;/code&gt; environment variable, but you end up getting an error like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--h-UBGJCE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.codingmatty.com/content/images/2022/10/image.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--h-UBGJCE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.codingmatty.com/content/images/2022/10/image.png" alt="" width="800" height="182"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Because the default type for &lt;strong&gt;every&lt;/strong&gt; environment variable is &lt;code&gt;string | undefined&lt;/code&gt;, you end up having to validate them in creative ways. Here is one solution to the above problem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const port: number = process.env.PORT ? parseInt(process.env.PORT) : 4000;

startServer(port);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But how pretty does that look? 🤮&lt;/p&gt;

&lt;p&gt;And this is just an example of one environment variable that needs to be validated. What about multiple variables? What about enumerations (i.e. &lt;code&gt;development&lt;/code&gt;, &lt;code&gt;production&lt;/code&gt;)? Using environment variables on their own is kind of a bane. So let's look at a better way.&lt;/p&gt;

&lt;p&gt;I almost decided to build another library myself to provide a solution to this issue, but I decided to check out what's out there first.&lt;/p&gt;

&lt;p&gt;Luckily, there are quite a few projects that have sought to solve this problem 😄&lt;/p&gt;

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

&lt;p&gt;First, what would be an ideal solution?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Declarative-like. I initially thought a single yaml file to describe the environment would work, but I learned that it's not that easy to convert a yaml file to Typescript types without an extra step.&lt;/li&gt;
&lt;li&gt;Useful errors. What environment variable(s) are not set up correctly and why? Is a variable not a number? Not the correct enumeration? Whatever the solution is should describe the problem well.&lt;/li&gt;
&lt;li&gt;Ability to derive other configurations from environment variables. This one is more of a nice-to-have feature. I like to have booleans like &lt;code&gt;isEmailEnabled&lt;/code&gt; to enable/disable emails from being sent when the API Key isn't provided instead of checking for the existence of the API Key directly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I ended up deciding to use &lt;code&gt;env-var&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hEj0cDK0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.codingmatty.com/content/images/2022/10/env-var-npm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hEj0cDK0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.codingmatty.com/content/images/2022/10/env-var-npm.png" alt="" width="800" height="700"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;env-var usage stats on NPM as of 10/22/22&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It uses a fully type-safe builder pattern API which allows you to set the conditions by chaining functions together, making it relatively readable.&lt;/p&gt;

&lt;p&gt;As a small example, this is how we can define the &lt;code&gt;port&lt;/code&gt; variable in the example above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const port: number = env.get('PORT').default(4000).asPortNumber();

startServer(port);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This looks much nicer, albeit more verbose, but way more readable. The &lt;code&gt;asPortNumber()&lt;/code&gt; is the function that triggers validation, but it also has the bonus of making sure the port number is valid, i.e. is it between 0 and 65535?&lt;/p&gt;

&lt;p&gt;To encapsulate all environment variables into a single place and avoid piecemealed validations throughout the codebase, I created a &lt;code&gt;config.ts&lt;/code&gt; file at the root of my source directory.&lt;/p&gt;

&lt;p&gt;Below is a code snippet of my config file with most of my environment variables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// config.ts
import env from 'env-var';

export const port = env.get('PORT').default(4000).asPortNumber();
export const environment = env
  .get('NODE_ENV')
  .default('development')
  .asEnum(['production', 'staging', 'test', 'development']);
export const isDeployed =
  environment === 'staging' || environment === 'production';

export const secret = env.get('SECRET').required().asString();
export const sentryDsn = env.get('SENTRY_DSN').asUrlString();

export const sendgridApiKey = env.get('SENDGRID_API_KEY').asString();
export const sendgridFromEmail = env
  .get('SENDGRID_FROM_EMAIL')
  .required(!!sendgridApiKey)
  .asString();
export const isEmailEnabled = sendgridApiKey &amp;amp;&amp;amp; sendgridFromEmail;

export const redisHost = env.get('REDIS_HOST').asString();
export const redisPort = env.get('REDIS_PORT').asPortNumber();
export const redisConfig =
  redisHost &amp;amp;&amp;amp; redisPort
    ? {
        host: redisHost,
        port: redisPort,
      }
    : undefined;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Usage would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import * as config from './config'
const port: number = config.port;

// OR

import { environment } from './config'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, I have limited the need to check for any environment variable to be &lt;code&gt;undefined&lt;/code&gt;, and my types are strict. I am even able to derive some extra variables to make my code more readable, like how &lt;code&gt;isDeployed&lt;/code&gt; is being derived from the value of &lt;code&gt;environment&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here is a quick explanation of some of the variables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;port: number&lt;/code&gt; - defaulted to 4000.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;environment: EnvrionmentEnum&lt;/code&gt; - defaulted to &lt;code&gt;development&lt;/code&gt; .&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;isDeployed: boolean&lt;/code&gt; - derives from the &lt;code&gt;environment&lt;/code&gt; variable.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;secret: string&lt;/code&gt; - will throw an error if not defined.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sentryDsn: string&lt;/code&gt; - will throw an error if it's not in URL format.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sendgridApiKey: string | undefined&lt;/code&gt; - optional API key for Sendgrid.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sendgridFromEmail: string | undefined&lt;/code&gt; - will throw an error if it's not defined but the SendGrid API Key &lt;em&gt;is&lt;/em&gt; defined.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;isEmailEnabled: boolean&lt;/code&gt; - derives from whether other Sendgrid variables are defined. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;redisConfig&lt;/code&gt; - an &lt;code&gt;IORedis&lt;/code&gt; configuration object based on environment variables &lt;code&gt;REDIS_HOST&lt;/code&gt; and &lt;code&gt;REDIS_PORT&lt;/code&gt;. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can even do a quick check of a dotenv file (or check your system's configuration) via a command like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .env
PORT="xyz"

---

$ npx dotenv - .env -- ts-node config.ts

.../node_modules/env-var/lib/variable.js:47
    throw new EnvVarError(errMsg)
          ^
EnvVarError: env-var: "PORT" should be a valid integer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ NODE_ENV="test" npx ts-node src/config.ts

.../node_modules/env-var/lib/variable.js:47
    throw new EnvVarError(errMsg)
          ^
EnvVarError: env-var: "NODE_ENV" should be one of [production, staging, test, development]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will help you confirm whether your .env file is set up correctly or tell you exactly what errors you have.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alternatives
&lt;/h2&gt;

&lt;p&gt;Disclaimer: I haven't tested any of these out, but I figured I wanted to mention them for completion.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;tconf&lt;/code&gt;
This library seemed like what I wanted to use, but it requires a lot of different components, including a type file, a yaml configuration file, and a file to glue all of those together. It might be good for larger projects with many variables, but it seemed overkill for my use case. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;unified-env&lt;/code&gt;
This library seems robust, allowing you to tie in variables from the environment as CLI arguments and from a .env file. It provides one file to parse all of the variables with validation options.
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/@velsa/ts-env"&gt;&lt;code&gt;@velsa/ts-env&lt;/code&gt;&lt;/a&gt;`&lt;code&gt;This library is similar to&lt;/code&gt;env-var`, it just uses one function to parse an environment variable with options for validating the value. But it hasn't been updated in a while and doesn't look very widely used.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ts-app-env&lt;/code&gt;
This library looks exactly like &lt;code&gt;@velsa/ts-env&lt;/code&gt;, but maybe a little more up-to-date. The documentation is a little lacking, though.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ts-dotenv&lt;/code&gt;
This library wraps the dotenv library with the ability to type information with a singular schema object. It seems excellent, but if you don't use .env files in production, it may add an unnecessary step.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;I hope this post helps you find sanity in using environment variables in your Typescript application.&lt;/p&gt;

&lt;p&gt;Let me know if you have any questions.&lt;/p&gt;

&lt;p&gt;You can find me on &lt;a href="https://twitter.com/codingmatty"&gt;Twitter&lt;/a&gt; or email me at &lt;a href="//mailto:codingmatty@gmail.com"&gt;codingmatty@gmail.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>node</category>
    </item>
    <item>
      <title>Imperfect Code</title>
      <dc:creator>Matthew Jacobs</dc:creator>
      <pubDate>Mon, 11 Mar 2019 19:47:04 +0000</pubDate>
      <link>https://dev.to/codingmatty/imperfect-code-4b76</link>
      <guid>https://dev.to/codingmatty/imperfect-code-4b76</guid>
      <description>&lt;p&gt;I have a confession to make - I have a hard time providing code reviews. I tend to nit pick. A lot.&lt;/p&gt;

&lt;p&gt;I notice that everyone seems to have a different way of writing code. And sometimes it can be really interesting to see new techniques. But often it can just add a mess to a codebase. Over time, when different developers come in, mark the codebase with their particular style, and leave that codebase for whatever reason, you start to feel like a geologist digging through the layers of the earths' crust.&lt;/p&gt;

&lt;p&gt;I mostly review code for Javascript codebases. It’s a great language, and I love it. But it can allow for some fascinating syntax and still work as expected. And when you add JSX all hell breaks loose.&lt;/p&gt;

&lt;p&gt;I have a tendency to want perfection. But code isn’t meant to be perfect. Code is a medium by which to describe what a computer should do. You can have the best code in the world, and no one will use it because maybe it doesn’t do anything useful.&lt;/p&gt;

&lt;p&gt;Code seems to have an ever decreasing half-life. By which I mean that as soon as you write some code, it starts to decay. It’s usefulness as code becomes less and less, even though it’s functionality may keep living on. Because &lt;em&gt;code&lt;/em&gt; is meant to be read by &lt;strong&gt;humans&lt;/strong&gt; , not machines. Machines could care less what your variable names are, or whether you are using a typed language or not. They will do &lt;em&gt;exactly&lt;/em&gt; what you tell them to. Nothing more, and nothing less.&lt;/p&gt;

&lt;p&gt;If you have worked on any system that you may consider as “legacy” you may know exactly what I mean. The larger a codebase gets, the less likely that someone will go in and clean it up. When someone has to add or fix a feature, they will likely go in and surgically update as little code as possible.&lt;/p&gt;

&lt;p&gt;I have a colleague who recently told me that he is less and less inclined to write new code lately. This is because he has started to realize that the more code you write, the more bugs you produce. He is starting to see code, effectively, as a liability instead of a an asset.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimgs.xkcd.com%2Fcomics%2Ffixing_problems.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimgs.xkcd.com%2Fcomics%2Ffixing_problems.png" alt="XKCD Comic"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;I remember when I first started to transition into web development. I started learning Angular on my own. I learned all of the right ways to write Angular, and internalized the best practices. I even started a pretty ambitious side project to get used to working with the technologies that I wanted to and get my foot into the door at the right company. When I got hired at a company using Angular, I was so excited!&lt;/p&gt;

&lt;p&gt;I came in with no real experience in web development. I came in with high expectations of my Angular knowledge. That was until I looked at the codebase. I was so lost 🙈. Part of it was my inexperience, but the other part was how they architected their codebase. It was &lt;em&gt;not&lt;/em&gt; using the best practices that I had come to appreciate. When I learned Angular I thought that nothing should go on &lt;code&gt;$rootScope&lt;/code&gt; — you might as well place things on the global scope 😱. This was apparently not a shared sentiment. And that was just a small piece of the gigantic puzzle.&lt;/p&gt;

&lt;p&gt;This is when I truly learned that code is not written in a vacuum. Everyone brings different experiences and motivations to the way they write and architect codebases. In fact, this is something I am still learning today.&lt;/p&gt;




&lt;p&gt;Let's get back to my code review struggle. How have I learned to deal with my own issues and provide valuable feedback without nit-picking everything?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;I realize that the code isn’t as important as the function or feature that we are trying to provide.&lt;/p&gt;

&lt;p&gt;When I start to review code I first ask does it do what we expect it to do, and are there tests to verify this? Are there any possible side effects? If the code does what it’s supposed to do, and there are no foreseeable side effects, then I will let go of any stylistic details or logical order. Most of the time it doesn’t matter if we “bail out early” unless we would be doing a lot of computation work otherwise. I may add some comments about readability sometimes, but I will give it a checkmark and move on.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Try to preempt the problem.&lt;/p&gt;

&lt;p&gt;I was once told that the person who cares most will write the rules. In my case, I created a coding style guide with some initial guidelines based on code reviews I had done previously. I then elicited some feedback and asked other developers to add their own stylistic preferences if they had any. The key is to have everyone agree on the guidelines to get buy-in. This is a living documentation that can evolve as we decide better practices. But the best purpose of it is to have something to point to to say what we decided as a team how to write code for a particular codebase. It’s also a great resource to onboard new developers to the team.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Sometimes I still just nit pick things.&lt;/p&gt;

&lt;p&gt;I don’t see nit-picking things as a blocker any more. I see it as a way to bestow my opinion where it may be served well — think constructive criticism. Sometimes it can be a learning experience for other developers, but I’ll admit sometimes it may be a hinderance. I have learned not to do it to developers who have expressed that they only want actionable feedback. And If I really cared that much, I would come in behind the developer and clean it up. But I don’t have time to do that, and nobody should — &lt;strong&gt;it undermines that developer and breaks down trust&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;So what about you? Do you have a problem with perfectionism? As a nit-picker or nit-pickee? Let me know how you deal with it? I’m also open to pointers on how I can do better.&lt;/p&gt;

</description>
      <category>codingstyle</category>
      <category>perfectionism</category>
      <category>codereviews</category>
    </item>
    <item>
      <title>Gemfury for Javascript Packages</title>
      <dc:creator>Matthew Jacobs</dc:creator>
      <pubDate>Mon, 25 Feb 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/codingmatty/gemfury-for-javascript-packages-1o67</link>
      <guid>https://dev.to/codingmatty/gemfury-for-javascript-packages-1o67</guid>
      <description>&lt;p&gt;&lt;em&gt;Notice: NPM is the name for 2 disparate tools: a registry that holds javascript packages (npmjs.com), and the CLI to manage javascript packages locally. I talk about both and to distinguish them I use all caps "NPM" for the registry, and all lowercase "npm" for the CLI.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I started a project at work a while back. Specifically, a javascript package for our frontend. It's something that would need to be shared by various projects (I plan to explain the project in a future post). While I could have started the project without worrying about how it would be consumed, I wanted to start thinking about it up front. One of the main known requirements was that it was going to be a private package, at least for now.&lt;/p&gt;

&lt;p&gt;The first option was to depend on Github. It’s something that an npm package allows, and wouldn’t be terribly difficult to work with. In fact, it’s something that has authentication built in when a developer has to have access to clone a repo anyways. But we wanted to find a more robust solution. Something specific to holding packages, separate from the tool used to maintain the code.&lt;/p&gt;

&lt;p&gt;The obvious answer was &lt;a href="https://www.npmjs.com/"&gt;NPM&lt;/a&gt;. It’s something every javascript developer is familiar with. And it’s easy to use. But, the cost to value ratio is pretty steep. This may not be the case for every team, especially for open source projects. But we decided against it for us for now.&lt;/p&gt;

&lt;p&gt;To be honest, I didn’t even know there &lt;em&gt;were&lt;/em&gt; alternatives to NPM. When I brought up the question to my team of possible hosting solutions, I learned of at least a handful of options. Some of which were self-hostable, but others which were just general alternatives to NPM.&lt;/p&gt;

&lt;p&gt;Then our Infrastructure manager mentioned that we had a &lt;a href="https://gemfury.com/"&gt;Gemfury&lt;/a&gt; account. It had a single unused gem in it. We were a ruby shop, so that made sense, but how would that help me for a javascript package? I wasn’t about to wrap my javascript package into a gem just to distribute it. But I took this under advisement.&lt;/p&gt;

&lt;p&gt;After some research, I found that Gemfury actually supports a plethora of languages and registry options. While they sound like they would only be useful for ruby gems, they actually work with python's PIP, php's Composer, and even Debian repositories. It’s like a buffet where you can eat some fried rice, gyros, and Texas barbecue all under one roof.&lt;/p&gt;

&lt;p&gt;Okay, so they have learned how to scale horizontally in the code registry business, but I care about hosting a private javascript package. How do they compare to NPM?&lt;/p&gt;

&lt;h2&gt;
  
  
  Gemfury Features
&lt;/h2&gt;

&lt;p&gt;Here are the top features that sold us on Gemfury:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tokens&lt;/strong&gt; - Gemfury has this concept of “deploy” (i.e. pull) and ”push” tokens. These are one-way, revokable tokens that you can use to allow people or systems to do a single direction action. For instance, if you only want to allow people to install a package, you can provide them with a “deploy” token. Inversely, if you want to setup continuous deployment to publish changes to Gemfury, you can use a “push” token. These are incredibly powerful for intentionality and security. &lt;/p&gt;

&lt;p&gt;There are different ways to use these tokens, but the &lt;a href="https://gemfury.com/help/repository-url"&gt;docs&lt;/a&gt; suggest the tokens go into the url. I will show you below how we avoided that in the case of using npm that seems to be acceptable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Granular User Permissions&lt;/strong&gt; - With an organization set up, you can add collaborators (we will see how to do that below). These collaborators can have 3 tiers of access to an organization:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Download Only&lt;/li&gt;
&lt;li&gt;Upload &amp;amp; Download&lt;/li&gt;
&lt;li&gt;Owner (full access) - this one allows someone to add collaborators and manage tokens for an organization.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By adding collaborators you can provide people with access via their own credentials. This prevents you from having to provide a single access token, or manage multiple tokens for multiple users. By doing this, you can add and remove collaborators without affecting any other person or system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NPM Proxy&lt;/strong&gt; - This is NPM specific, and I can’t speak for other registry options. You can set up npm to &lt;em&gt;only&lt;/em&gt; hit your Gemfury registry to install packages, which may be fine for a very narrow use case. But most projects will need to be able to install from Gemfury &lt;em&gt;first&lt;/em&gt; and then move on to NPM if the package can’t be found. Gemfury provides a way to do this (&lt;a href="https://gemfury.com/help/npm-registry/"&gt;Installing private npm modules&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;This actually allows you to name uploaded packages with the same name as one in NPM, but will effectively shadow that package. This can be useful if you want to avoid updating dependencies in many projects, but you have a fork of a public package on NPM that you would prefer to use.&lt;/p&gt;

&lt;p&gt;You can also namespace your javascript packages and set up only that namespace to search Gemfury. This is what we have done and what I will demonstrate below. But either way, Gemfury can accommodate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mutability&lt;/strong&gt; - For anyone who has been in web development for a while, you may have been affected by, or at least heard of, the &lt;a href="https://www.theregister.co.uk/2016/03/23/npm_left_pad_chaos/"&gt;Left-Pad Fiasco&lt;/a&gt;. The result of that caused NPM to declare all packages to be immutable. No matter who you are or what you want to do, if you publish something to NPM, it will remain there forever. You cannot delete it, and you cannot overwrite it. (You actually have 72 hours to delete it, and can contact support after that)&lt;/p&gt;

&lt;p&gt;This means when you make a mistake, even if no one is being affected by it &lt;em&gt;yet&lt;/em&gt;, you have to release a &lt;strong&gt;new&lt;/strong&gt; version. The remnants of that code will remain forever. But, what if you could just rebuild and override the existing version? Or maybe you want to yank it altogether. This may not be the correct course of action, but for better or for worse Gemfury allows it. And I appreciate that. Yield this power wisely.&lt;/p&gt;




&lt;p&gt;I will offer some drawbacks here as well, but there aren’t many that seem to affect us:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Token Descriptions&lt;/strong&gt; - &lt;del&gt;While you can create all the tokens you want, currently Gemfury does not allow you to add descriptions to them. This makes it very difficult to figure out which one is being used where. I’d suggest keeping a secure file to provide descriptions for keys, but hopefully they will add this ability soon.&lt;/del&gt; &lt;a href="https://www.fury.blog/changelog/2019-02-22-token-description/"&gt;Token Descriptions&lt;/a&gt; have been added as of Feb 22, 2019.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Version Tagging&lt;/strong&gt; - NPM allows you to tag versions as &lt;code&gt;latest&lt;/code&gt;, or &lt;code&gt;beta&lt;/code&gt;, whereas Gemfury currently does not. They do however support full &lt;a href="https://semver.org/"&gt;semver&lt;/a&gt;, which allows for suffixes like &lt;code&gt;-beta.0&lt;/code&gt;, which provides a well enough work around.&lt;/p&gt;

&lt;p&gt;There are some other odd intricacies around authentication methods between using npm and installing gems, but I want to focus on just javascript packages in this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;Now we can get into the nitty gritty.&lt;/p&gt;

&lt;p&gt;Here are some of the conditions that guided our setup process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We have projects that use both &lt;code&gt;npm&lt;/code&gt; and &lt;code&gt;yarn&lt;/code&gt; CLI tools&lt;/li&gt;
&lt;li&gt;We want it to be easy to revoke access to one user - if they leave or lose their laptop, we don't want to have to inconvenience everyone.&lt;/li&gt;
&lt;li&gt;We want to be able to securely install on CI machines, and similar to users, be able to easily revoke those credentials.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now here are the steps:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;These steps assume you want to create an organization account. Read more about organization accounts here: &lt;a href="https://gemfury.com/help/organizations"&gt;Gemfury Organization Accounts Documentation&lt;/a&gt;. You may not &lt;strong&gt;need&lt;/strong&gt; one for a small group. In that case you should be able to just skip step 1, and the remaining steps will be the same with a single user account, including adding collaborators.&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create an organization (&lt;a href="https://manage.fury.io/manage/orgs/new"&gt;Create Organization Link&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Have users create their own Gemfury account (&lt;a href="https://manage.fury.io/users/sign_up"&gt;Signup Link&lt;/a&gt;).

&lt;ul&gt;
&lt;li&gt;They can login via Github, but Gemfury will prompt them to create a new password, which they will need for logging in locally.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Add users to the created organization.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/9ddv2zmnaoer/1gsvkx2mwu91C69meBVPwb/4f120cd1b114336a92e49428ac962262/image.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/9ddv2zmnaoer/1gsvkx2mwu91C69meBVPwb/4f120cd1b114336a92e49428ac962262/image.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These next steps are for local setup:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;We use namespacing to make it esaier to configure, but it's not required. It's as easy as appending &lt;code&gt;@organization/&lt;/code&gt; to the package name in the &lt;code&gt;package.json&lt;/code&gt; file&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add reference to the Gemfury registry in each consuming project's &lt;code&gt;.npmrc&lt;/code&gt; file.

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;yarn&lt;/code&gt; will automatically pick up the project's &lt;code&gt;.npmrc&lt;/code&gt; configuration, but there is a way to only allow yarn to use the configuration via a &lt;code&gt;.yarnrc&lt;/code&gt; file.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ${PROJECT_ROOT}/.npmrc&lt;/span&gt;
&lt;span class="c"&gt;# Make sure to provide the trailing slash '/'&lt;/span&gt;
@namespace:registry&lt;span class="o"&gt;=&lt;/span&gt;https://npm-proxy.fury.io/organization/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Login via &lt;code&gt;npm&lt;/code&gt; CLI with &lt;strong&gt;Gemfury credentials&lt;/strong&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm login &lt;span class="nt"&gt;--registry&lt;/span&gt; https://npm-proxy.fury.io/organzation/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will add or edit the users global &lt;code&gt;.npmrc&lt;/code&gt; file located in their home directory with this line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ~/.npmrc&lt;/span&gt;
//npm-proxy.fury.io/organization/:_authToken&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SECRET_TOKEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Install the dependency via the command line.
&lt;/li&gt;
&lt;/ol&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&lt;/span&gt; @namespace/package
&lt;span class="c"&gt;# OR&lt;/span&gt;
yarn add @namespace/package
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the proper setup, this install step should look in the Gemfury registry first, and then check in NPM if the dependency isn't found.&lt;/p&gt;

&lt;p&gt;Now, once a developer is added as a collaborator (step 3) and logs in to Gemfury via the &lt;code&gt;npm&lt;/code&gt; CLI (step 5) they will be able to install the dependency with no problem.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: We did have some issues wtih &lt;code&gt;yarn&lt;/code&gt; versions 1.6 and 1.7, I strongly recommend using the latest versions to avoid any issues.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Continuous Integration (CI) Setup:&lt;/p&gt;

&lt;p&gt;As mentioned above, &lt;strong&gt;tokens&lt;/strong&gt; are a great way to offer secure one-way actions. The best use case for these tokens are on your CI system, kept in environment variables. This makes them easy to swap out without affecting any one else, or any other system.&lt;/p&gt;

&lt;p&gt;We have 2 different systems that provide different functionality, and thus different setups. Here is how we configured each of them.&lt;/p&gt;

&lt;p&gt;For our CI system that handles the package deployment we needed something to build our package and publish it to Gemfury.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a &lt;strong&gt;push&lt;/strong&gt; token.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/9ddv2zmnaoer/g6HDGOS7vnarbTt5FcNml/4fbc94d9fa48eb90920bb63ff9d598b7/image.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/9ddv2zmnaoer/g6HDGOS7vnarbTt5FcNml/4fbc94d9fa48eb90920bb63ff9d598b7/image.png" alt="image"&gt;&lt;/a&gt; &lt;a href="//images.ctfassets.net/9ddv2zmnaoer/1kPqNJYIAeO6rDkn0VZk3R/e3ee360a86d3e2e4c7dd2b1ebd910534/image.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/9ddv2zmnaoer/1kPqNJYIAeO6rDkn0VZk3R/e3ee360a86d3e2e4c7dd2b1ebd910534/image.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add the generated token to CI environment variables. This step wholly depends on your CI. (For reference, I have named mine &lt;code&gt;FURY_PUSH_TOKEN&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Upload package to Gemfury. There are a few ways to upload packages to Gemfury. Check out &lt;a href="https://gemfury.com/help/upload-packages"&gt;Gemfury Upload docs&lt;/a&gt; for more information.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We chose to generate the assets with &lt;code&gt;npm pack&lt;/code&gt; and then use  &lt;code&gt;curl&lt;/code&gt; to push them up to Gemfury.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm pack
curl &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="nv"&gt;package&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;@&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;namespace-package-&lt;span class="k"&gt;*&lt;/span&gt;.tgz&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; https://&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;FURY_PUSH_TOKEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;@push.fury.io/organization/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: &lt;code&gt;$(echo namespace-package-*.tgz)&lt;/code&gt; just allows us to avoid needing to know the version of the filename, but it does assume there is only 1 tgz file.&lt;/p&gt;

&lt;p&gt;For our CI system that handles the consuming application(s) we just need to authenticate before trying to install.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a &lt;strong&gt;deploy&lt;/strong&gt; token&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/9ddv2zmnaoer/CgVZdX9qBtDzE0Ta4NUUa/562ac4d78a27e63358c619bcd768c7cd/image.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/9ddv2zmnaoer/CgVZdX9qBtDzE0Ta4NUUa/562ac4d78a27e63358c619bcd768c7cd/image.png" alt="image"&gt;&lt;/a&gt; &lt;a href="//images.ctfassets.net/9ddv2zmnaoer/71ZW8i0ZdCC5oSlZyX6958/3bd4e26eb6a846288156b6deff3b16d8/image.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/9ddv2zmnaoer/71ZW8i0ZdCC5oSlZyX6958/3bd4e26eb6a846288156b6deff3b16d8/image.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add the generated token to CI environment variables. This step wholly depends on your CI. (For reference, I have named mine &lt;code&gt;FURY_PULL_TOKEN&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;In your CI scripts authenticate with npm manually before installing dependencies.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"//npm-proxy.fury.io/organization/:_authToken=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;FURY_PULL_TOKEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ~/.npmrc
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that should do it! Feel free to contact me with any questions about our setup. I'd love to help any way I can 🙂&lt;/p&gt;




&lt;p&gt;I hope this helped you to get setup with &lt;a href="https://gemfury.com/"&gt;Gemfury&lt;/a&gt;. It really is a great service, with an intuitive interface, at a reasonable rate for everyone to get started with. &lt;/p&gt;

&lt;p&gt;And while it may not seem intuitive to place javascript packages on a platform originally designed for ruby gems, it really is helpful. It becomes incredibly more powerful when you are with a company that operates in many different languages. Gemfury offers an incredible platform to hold packages of all kinds.&lt;/p&gt;

&lt;p&gt;Let me know your experience.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>gemfury</category>
      <category>npm</category>
    </item>
  </channel>
</rss>
