<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Elliot DeNolf</title>
    <description>The latest articles on Forem by Elliot DeNolf (@denolfe).</description>
    <link>https://forem.com/denolfe</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%2F96510%2Fc56144ff-db61-4bc7-be4b-751e70dde3b2.png</url>
      <title>Forem: Elliot DeNolf</title>
      <link>https://forem.com/denolfe</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/denolfe"/>
    <language>en</language>
    <item>
      <title>White Label the Payload CMS Admin UI</title>
      <dc:creator>Elliot DeNolf</dc:creator>
      <pubDate>Tue, 08 Feb 2022 07:00:00 +0000</pubDate>
      <link>https://forem.com/payloadcms/white-label-the-payload-cms-admin-ui-52hh</link>
      <guid>https://forem.com/payloadcms/white-label-the-payload-cms-admin-ui-52hh</guid>
      <description>&lt;p&gt;Easily make Payload the perfect white labeled headless CMS. With Payload, you get complete control of the look and feel of the Admin Panel.&lt;/p&gt;

&lt;p&gt;Across the Admin UI, the branding from Payload is minimal so that the focus stays where it should be—on your application. By updating the Payload branding, you can create a customized interface and your client (or team, or whoever will access your admin panel) will be greeted with a dynamic interface that is consistent with your application branding.&lt;/p&gt;

&lt;p&gt;In this blog post, you will learn how to rebrand and white label the Payload admin panel for your application by modifying the following elements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Favicon&lt;/li&gt;
&lt;li&gt;Title&lt;/li&gt;
&lt;li&gt;ogImage&lt;/li&gt;
&lt;li&gt;Icon&lt;/li&gt;
&lt;li&gt;Logo&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Get Started
&lt;/h4&gt;

&lt;p&gt;You can use your own Payload app or start a new one for this tutorial. If you haven't started a project yet, you can get set up easily by running &lt;code&gt;npx create-payload-app&lt;/code&gt; in your terminal.&lt;/p&gt;

&lt;p&gt;For more details on how to start an application, including how to do so from scratch, read the &lt;a href="https://payloadcms.com/docs/getting-started/installation" rel="noopener noreferrer"&gt;installation&lt;/a&gt; documentation.&lt;/p&gt;

&lt;h4&gt;
  
  
  Payload Config
&lt;/h4&gt;

&lt;p&gt;Start by navigating to your base Payload config file in which all options for the Admin panel are defined.&lt;/p&gt;

&lt;p&gt;Below is an example config file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;buildConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;payload/config&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;dotenv&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv&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;Page&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./collections/Page&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;Media&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./collections/Media&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&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;buildConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;serverURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PAYLOAD_PUBLIC_SERVER_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;collections&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;Media&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;h4&gt;
  
  
  Admin Option
&lt;/h4&gt;

&lt;p&gt;Next, add the admin option to your payload config file this can start as an empty object.&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;buildConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;serverURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PAYLOAD_PUBLIC_SERVER_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="c1"&gt;// Add this&lt;/span&gt;
  &lt;span class="na"&gt;collections&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;Media&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;h4&gt;
  
  
  Meta
&lt;/h4&gt;

&lt;p&gt;The admin property takes the following sub-properties:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;titleSuffix&lt;/code&gt;: Text that appends the meta/page title displayed in the browser tab—must be a string.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;favicon&lt;/code&gt;: Image that will be displayed as the tab icon.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ogImage&lt;/code&gt;: Image that will appear in the preview when you share links to your admin panel online and through social media.&lt;/p&gt;

&lt;p&gt;Now, let’s add the meta-object and the above properties.&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;buildConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;serverURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PAYLOAD_PUBLIC_SERVER_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Add these properties&lt;/span&gt;
    &lt;span class="na"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;titleSuffix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;- TRBL Design&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;favicon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/assets/favicon.svg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;ogImage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/assets/logo.svg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;collections&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;Media&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;h4&gt;
  
  
  Serving Images
&lt;/h4&gt;

&lt;p&gt;We need to ensure the images are served somewhere they can be downloaded by the browser. We could have saved an absolute URL if the assets&lt;br&gt;
are hosted somewhere. Instead of that we set relative paths to an assets folder for &lt;code&gt;favicon.svg&lt;/code&gt; and &lt;code&gt;logo.svg&lt;/code&gt; above;&lt;br&gt;
 we can serve them from the same express app as Payload is using. Create a directory called &lt;code&gt;assets&lt;/code&gt; and save your images there.&lt;/p&gt;

&lt;p&gt;Then in your &lt;code&gt;server.js&lt;/code&gt; or wherever you have defined express routes, add the line that serves &lt;code&gt;/assets&lt;/code&gt; with &lt;code&gt;express.static&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// Add /assets&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/assets&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;static&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../assets&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;

&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PAYLOAD_SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;mongoURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MONGO_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;express&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;license&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PAYLOAD_LICENSE_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: Anything in that assets folder will be publicly available when your app is hosted online.&lt;/p&gt;

&lt;p&gt;At this point, if we check out our app in the browser—the tab will display your updated favicon and title suffix.&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%2Fpayloadcms.com%2Fimages%2Fblog%2Fwhite-label-admin-panel-branding%2Fmetadata.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%2Fpayloadcms.com%2Fimages%2Fblog%2Fwhite-label-admin-panel-branding%2Fmetadata.png" alt="Browser tabs with favicon and title" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To test that the ogImage has successfully updated, use the browser inspector to view the metadata or test your app through your social media of choice or a web developer tool e.g. &lt;a href="https://cards-dev.twitter.com/validator" rel="noopener noreferrer"&gt;Twitter’s Card Validator&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Custom Components
&lt;/h4&gt;

&lt;p&gt;In addition to metadata, the admin option also takes custom components to override the default configuration.&lt;/p&gt;

&lt;p&gt;The properties we are interested in for rebranding the admin panel are:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;graphics.Logo&lt;/code&gt;: Image component to be displayed as the logo on the Sign Up / Login view.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;graphics.Icon&lt;/code&gt;: Image component displayed above the Nav in the admin panel, often a condensed version of a full logo.&lt;/p&gt;

&lt;p&gt;Now let's set up these custom components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add &lt;code&gt;components&lt;/code&gt; and open an object&lt;/li&gt;
&lt;li&gt;Within components, add the &lt;code&gt;graphics&lt;/code&gt; property and open another object&lt;/li&gt;
&lt;li&gt;Now we can point to the relevant files for the &lt;code&gt;Logo&lt;/code&gt; and &lt;code&gt;Icon&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&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="nx"&gt;Logo&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./graphics/Logo&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;Icon&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./graphics/Icon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&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;buildConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;serverURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PAYLOAD_PUBLIC_SERVER_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="na"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="na"&gt;titleSuffix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;- Custom Title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;favicon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/favicon.svg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;ogImage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/logo.svg&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;// Add components&lt;/span&gt;
     &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="na"&gt;graphics&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="nx"&gt;Logo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nx"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;collections&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;Media&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;These graphics need to be React components that can render whatever you'd like. They could render &lt;code&gt;img&lt;/code&gt; tags or full React SVGs. In this example, we're going to include images, css and other html too.&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="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="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./logo.scss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Logo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;logo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;
      &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/assets/logo.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;TRBL Design Logo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Optionally we can include some styling too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nc"&gt;.logo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;max-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3rem&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&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;Final step, let’s check out the new logo and icons.&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%2Fpayloadcms.com%2Fimages%2Fblog%2Fwhite-label-admin-panel-branding%2Fwhite-label-branded-login.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%2Fpayloadcms.com%2Fimages%2Fblog%2Fwhite-label-admin-panel-branding%2Fwhite-label-branded-login.png" alt="Login page with custom white label branding" width="800" height="400"&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%2Fpayloadcms.com%2F%2Fimages%2Fblog%2Fwhite-label-admin-panel-branding%2Fwhite-label-branded-dashboard.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%2Fpayloadcms.com%2F%2Fimages%2Fblog%2Fwhite-label-admin-panel-branding%2Fwhite-label-branded-dashboard.png" alt="Admin panel with custom white label branding" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Summary
&lt;/h4&gt;

&lt;p&gt;There you have it! With around 10 lines of code you can brand the admin panel, update metadata and add custom components to effectively white label the admin panel for your clients or users.&lt;/p&gt;

&lt;p&gt;Payload gives you the freedom and control to customize other aspects too, like the swapping out the Dashboard or creating custom inputs, to learn more about the admin panel and custom components—check out the pages below.&lt;/p&gt;

&lt;h4&gt;
  
  
  Read More
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://payloadcms.com/docs/admin/overview" rel="noopener noreferrer"&gt;Admin Panel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://payloadcms.com/docs/admin/components" rel="noopener noreferrer"&gt;Custom Components&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Questions or Comments?
&lt;/h4&gt;

&lt;p&gt;Join us on &lt;a href="https://github.com/payloadcms/payload/discussions" rel="noopener noreferrer"&gt;GitHub discussions&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>node</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Build Your Own Role-Based Access Control in Payload</title>
      <dc:creator>Elliot DeNolf</dc:creator>
      <pubDate>Wed, 14 Jul 2021 12:55:02 +0000</pubDate>
      <link>https://forem.com/payloadcms/build-your-own-role-based-access-control-in-payload-5kc</link>
      <guid>https://forem.com/payloadcms/build-your-own-role-based-access-control-in-payload-5kc</guid>
      <description>&lt;p&gt;Payload comes with open-ended &lt;a href="https://payloadcms.com/docs/access-control/overview" rel="noopener noreferrer"&gt;access control&lt;/a&gt;. You can define whatever type of pattern that you can dream up, and best of all—it's all done with simple JavaScript.&lt;/p&gt;

&lt;p&gt;A common pattern is Role-Based Access Control. Here, we'll walk you through how to create your own RBAC pattern on both the collection-level and field-level.&lt;/p&gt;

&lt;p&gt;In more detail, here are the pieces that we will be building:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Users collection with &lt;code&gt;role&lt;/code&gt; field&lt;/li&gt;
&lt;li&gt;Orders collection

&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;beforeChange&lt;/code&gt; hook to save which user created the order to a &lt;code&gt;createdBy&lt;/code&gt; field&lt;/li&gt;
&lt;li&gt;Access Control functions to restrict Admin Panel access to &lt;code&gt;admin&lt;/code&gt; roles or the creator of the order&lt;/li&gt;
&lt;li&gt;admin-only field level access&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  Initialize Project
&lt;/h4&gt;

&lt;p&gt;We'll be using &lt;code&gt;create-payload-app&lt;/code&gt; to build out the initial project.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run &lt;code&gt;npx create-payload-app payload-rbac&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Select &lt;code&gt;javascript&lt;/code&gt; for language&lt;/li&gt;
&lt;li&gt;Select &lt;code&gt;blank&lt;/code&gt; for our template&lt;/li&gt;
&lt;li&gt;Follow all other prompts&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This will give us a simple project with a Payload config and Users collection. The structure of the project will be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├─ payload.config.js
└─ collections/
  └─ Users.js
  └─ Orders.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Modify Users Collection
&lt;/h4&gt;

&lt;p&gt;First, we will add the &lt;code&gt;role&lt;/code&gt; field to our Users collection with 2 options: &lt;code&gt;admin&lt;/code&gt; and &lt;code&gt;user&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;auth&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="na"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;useAsTitle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;role&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;select&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;required&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="na"&gt;defaultValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

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

&lt;/div&gt;



&lt;h4&gt;
  
  
  Create Orders Collection
&lt;/h4&gt;

&lt;p&gt;Next, we will create a new &lt;code&gt;Orders.js&lt;/code&gt; collection in our &lt;code&gt;collections/&lt;/code&gt; directory and scaffold out basic fields and values - including the &lt;code&gt;createdBy&lt;/code&gt; relationship to the user.&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;Orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;orders&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;items&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;array&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;item&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;createdBy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;relationship&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;relationTo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;access&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;update&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;readOnly&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="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sidebar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Boolean&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;createdBy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The Orders collection has an &lt;code&gt;array&lt;/code&gt; field for items and a &lt;code&gt;createdBy&lt;/code&gt; field which is a relationship to our &lt;code&gt;Users&lt;/code&gt; collection. The &lt;code&gt;createdBy&lt;/code&gt; field will feature a strict &lt;code&gt;update&lt;/code&gt; access control function so that it can never be changed.&lt;/p&gt;

&lt;p&gt;Notice we also have a &lt;code&gt;condition&lt;/code&gt; function under the &lt;code&gt;createdBy&lt;/code&gt; field's access. This will hide &lt;code&gt;createdBy&lt;/code&gt; until it has a value.&lt;/p&gt;

&lt;h4&gt;
  
  
  Set the &lt;code&gt;createdBy&lt;/code&gt; Attribute Using a Hook
&lt;/h4&gt;

&lt;p&gt;Next, we'll add a hook that will run before any order is created. This is done by adding a &lt;code&gt;beforeChange&lt;/code&gt; hook to our collection definition.&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;Orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;orders&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;// Collapsed&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;hooks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;beforeChange&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;operation&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;create&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;req&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="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;createdBy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The logic in this hook sets the &lt;code&gt;createdBy&lt;/code&gt; field to be the current user's &lt;code&gt;id&lt;/code&gt; value, only if it is on a &lt;code&gt;create&lt;/code&gt; operation. This will create a relationship between an order and the user who created it.&lt;/p&gt;

&lt;h4&gt;
  
  
  Access Control
&lt;/h4&gt;

&lt;p&gt;Next, the access control for the collection can be defined. Payload's access control is based on functions. An access control function returns either a &lt;code&gt;boolean&lt;/code&gt; value to allow/disallow access &lt;em&gt;or&lt;/em&gt; it returns a query constraint that filters the data.&lt;/p&gt;

&lt;p&gt;We want our function to handle a few scenarios:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A user has the 'admin' role - access &lt;em&gt;all&lt;/em&gt; orders&lt;/li&gt;
&lt;li&gt;A user created the order - allow access to only those orders&lt;/li&gt;
&lt;li&gt;Any other user - disallow access
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isAdminOrCreatedBy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&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="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Scenario #1 - Check if user has the 'admin' role&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;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Scenario #2 - Allow only documents with the current user set to the 'createdBy' field&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="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// Will return access for only documents that were created by the current user&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;createdBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Scenario #3 - Disallow all others&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once defined, this function is added to the &lt;code&gt;access&lt;/code&gt; property of the collection definition:&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;Orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;orders&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;// Collapsed&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;access&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;isAdminOrCreatedBy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;update&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;isAdminOrCreatedBy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;isAdminOrCreatedBy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;hooks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Collapsed&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;With this function added to the &lt;code&gt;read&lt;/code&gt;, &lt;code&gt;update&lt;/code&gt;, and &lt;code&gt;delete&lt;/code&gt; access properties, the function will run whenever these operations are attempted on the collection.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Access functions default to requiring a logged-in user. This is why &lt;code&gt;create&lt;/code&gt; does not need to be defined for this example, since we want any logged in user to be able to create an order.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Put It All Together
&lt;/h4&gt;

&lt;p&gt;The last step is to add the collection to our &lt;code&gt;payload.config.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;buildConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;payload/config&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;Orders&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./collections/Orders&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;Users&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./collections/Users&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;buildConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;serverURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:3000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;collections&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;Orders&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;Let's verify the functionality:&lt;/p&gt;

&lt;p&gt;Start up the project by running &lt;code&gt;npm run dev&lt;/code&gt; or &lt;code&gt;yarn dev&lt;/code&gt; and navigate to &lt;code&gt;http://localhost:3000/admin&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Create your initial user with the &lt;code&gt;admin&lt;/code&gt; role.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpayloadcms.com%2Fimages%2Fblog%2Frbac%2F1-login.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%2Fpayloadcms.com%2Fimages%2Fblog%2Frbac%2F1-login.png" alt="Payload login" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Create an Order with the &lt;code&gt;admin&lt;/code&gt; user.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpayloadcms.com%2Fimages%2Fblog%2Frbac%2F2-create-order-1.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%2Fpayloadcms.com%2Fimages%2Fblog%2Frbac%2F2-create-order-1.png" alt="Create Order as admin" width="800" height="400"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpayloadcms.com%2Fimages%2Fblog%2Frbac%2F3-save-order-as-admin.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%2Fpayloadcms.com%2Fimages%2Fblog%2Frbac%2F3-save-order-as-admin.png" alt="Save Order as admin" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Create an additional user with the &lt;code&gt;user&lt;/code&gt; role by navigating to the Users collection, selecting Create New, entering an email/password, then saving.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpayloadcms.com%2Fimages%2Fblog%2Frbac%2F4-create-normal-user.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%2Fpayloadcms.com%2Fimages%2Fblog%2Frbac%2F4-create-normal-user.png" alt="Create Normal User" width="800" height="400"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpayloadcms.com%2Fimages%2Fblog%2Frbac%2F5-create-normal-user-after.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%2Fpayloadcms.com%2Fimages%2Fblog%2Frbac%2F5-create-normal-user-after.png" alt="Save Normal User" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Log out of your &lt;code&gt;admin&lt;/code&gt; user by selecting the icon in the bottom left, then log in with the second user.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpayloadcms.com%2Fimages%2Fblog%2Frbac%2F6-normal-user-login.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%2Fpayloadcms.com%2Fimages%2Fblog%2Frbac%2F6-normal-user-login.png" alt="Normal User Login" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You'll notice if we go to the Orders collection, no Orders will be shown. This indicates that the access control is working properly.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpayloadcms.com%2Fimages%2Fblog%2Frbac%2F7-view-orders-1.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%2Fpayloadcms.com%2Fimages%2Fblog%2Frbac%2F7-view-orders-1.png" alt="View Orders as Normal User" width="800" height="400"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpayloadcms.com%2Fimages%2Fblog%2Frbac%2F8-no-orders-shown.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%2Fpayloadcms.com%2Fimages%2Fblog%2Frbac%2F8-no-orders-shown.png" alt="No Orders Show" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Create another Order. Note that the current user will be saved to &lt;code&gt;Created By&lt;/code&gt; in the sidebar.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpayloadcms.com%2Fimages%2Fblog%2Frbac%2F9-create-order-as-normal-user.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%2Fpayloadcms.com%2Fimages%2Fblog%2Frbac%2F9-create-order-as-normal-user.png" alt="Create Order as Normal User" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Navigate back to Orders list on the dashboard. There will only be the single order created by the current user.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpayloadcms.com%2Fimages%2Fblog%2Frbac%2F10-view-single-order-as-normal-user.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%2Fpayloadcms.com%2Fimages%2Fblog%2Frbac%2F10-view-single-order-as-normal-user.png" alt="View Single Order As Normal User" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Log out, then back in with your &lt;code&gt;admin&lt;/code&gt; user. You should be able to see the original Order as well as the Order created by the second user.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpayloadcms.com%2Fimages%2Fblog%2Frbac%2F11-viewing-all-orders-as-admin.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%2Fpayloadcms.com%2Fimages%2Fblog%2Frbac%2F11-viewing-all-orders-as-admin.png" alt="View All Orders As Admin" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Field Level Access Control
&lt;/h4&gt;

&lt;p&gt;With everything working at the collection level, we can carry the concepts further and see how they can be applied at the field level. Suppose we wanted to add a &lt;code&gt;paymentID&lt;/code&gt; field only for Admin users. Create an &lt;code&gt;isAdmin&lt;/code&gt; function that checks the role as we did earlier.&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;isAdmin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&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="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin&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;Add a new field to Orders and set &lt;code&gt;create&lt;/code&gt;, &lt;code&gt;read&lt;/code&gt; or &lt;code&gt;update&lt;/code&gt; access calls to use the isAdmin function.&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;Orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;orders&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;// Collapsed&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;paymentId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;access&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;isAdmin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;isAdmin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;update&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;isAdmin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="c1"&gt;// Collapsed&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The new paymentID field is not available to the users even on one's own Order. Field level access controls allow for greater granularity over document level access for Collections and Globals. This shows how easy it is to manage exact permissions throughout the admin UI, GraphQL and REST endpoints; it even works when querying relationships to keep data secure.&lt;/p&gt;

&lt;h4&gt;
  
  
  What Other Improvements Can Be Made?
&lt;/h4&gt;

&lt;p&gt;Now that we have a basic example working. What are some ways that this could be improved?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ideally, we'd want to use both the hook and the access control function across multiple collections in our application. Since it's just JavaScript, we can extract each of these functions into their own file for re-use.&lt;/li&gt;
&lt;li&gt;Add additional roles, such as an &lt;code&gt;editor&lt;/code&gt; role which allows reading and editing, &lt;em&gt;but disallows creating&lt;/em&gt;. This all can be customized specifically to your needs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Questions or Comments? Join us on GitHub Discussions
&lt;/h4&gt;

&lt;p&gt;I hope you enjoyed the introduction to doing role-based access control with Payload!&lt;/p&gt;

&lt;p&gt;Come join the Payload &lt;a href="https://github.com/payloadcms/payload/discussions" rel="noopener noreferrer"&gt;discussions&lt;/a&gt; on GitHub.&lt;/p&gt;

&lt;h4&gt;
  
  
  Further Reading
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/payloadcms/blog-rbac" rel="noopener noreferrer"&gt;Source Code for this post&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Documentation

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://payloadcms.com/docs/access-control/overview" rel="noopener noreferrer"&gt;Access Control&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://payloadcms.com/docs/hooks/overview" rel="noopener noreferrer"&gt;Hooks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://payloadcms.com/docs/configuration/collections" rel="noopener noreferrer"&gt;Collections&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Build .NET 5 Applications with GitHub Actions</title>
      <dc:creator>Elliot DeNolf</dc:creator>
      <pubDate>Wed, 23 Dec 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/denolfe/build-net-5-applications-with-github-actions-4hcd</link>
      <guid>https://forem.com/denolfe/build-net-5-applications-with-github-actions-4hcd</guid>
      <description>&lt;p&gt;This article will go through setting up GitHub Actions to build and test a .NET 5 application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Basic Application
&lt;/h2&gt;

&lt;p&gt;First, let’s build out our basic application and test suite using the dotnet CLI. Create a new directory for your project and go through the following commands:&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;MyApp MyApp.Tests
&lt;span class="nb"&gt;cd &lt;/span&gt;MyApp
dotnet new webapi
&lt;span class="nb"&gt;cd&lt;/span&gt; ../MyApp.Tests
dotnet new xunit
&lt;span class="nb"&gt;cd&lt;/span&gt; ..
dotnet new sln
dotnet sln add &lt;span class="k"&gt;**&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.csproj
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ve now built out a solution with a web API and test suite to use in our Workflow.&lt;/p&gt;

&lt;p&gt;Before we move forward, here is our simplified project structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
|-- MyApp
| |-- MyApp.csproj
| |-- Program.cs
| `-- Startup.cs
|-- MyApp.Tests
| |-- MyApp.Tests.csproj
| `-- UnitTest1.cs
`-- MyApp.sln
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create Workflow
&lt;/h2&gt;

&lt;p&gt;GitHub Actions is driven by having workflow definitions in the &lt;code&gt;.github/workflows&lt;/code&gt; directory. Create these 2 directories and add a file named &lt;code&gt;ci.yml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The first thing we will add to our &lt;code&gt;ci.yml&lt;/code&gt; will be a name and an &lt;code&gt;on&lt;/code&gt; trigger. This simply gives our workflow a name and also defines when the workflow should run - when we push to the repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.NET 5 CI&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we define our &lt;code&gt;jobs&lt;/code&gt; section. We will only be adding a single job that will have multiple steps. Add the following to our &lt;code&gt;ci.yml&lt;/code&gt; which will simply define our job and what OS it will run on.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.NET 5 Application&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first step of our job will be to configure .NET with our desired version - .NET 5 in our case&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.NET 5 Application&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup dotnet&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-dotnet@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;dotnet-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;5.0.101'&lt;/span&gt; &lt;span class="c1"&gt;# Check for latest at link below&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;NOTE: The &lt;code&gt;dotnet-version&lt;/code&gt; used is the latest from the &lt;a href="https://dotnet.microsoft.com/download/dotnet/5.0"&gt;.NET 5 Download page&lt;/a&gt;. Be sure to check the latest version available and swap that in. Wildcarding the version like &lt;code&gt;5.x&lt;/code&gt; should be supported in the near future.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now that .NET is configured and makes the &lt;code&gt;dotnet&lt;/code&gt; cli available to our workflow, we will add steps for &lt;code&gt;dotnet build&lt;/code&gt; and &lt;code&gt;dotnet test&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let’s add those. The complete &lt;code&gt;ci.yml&lt;/code&gt; should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.NET 5 CI&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.NET 5 Application&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup dotnet&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-dotnet@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;dotnet-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;5.0.101'&lt;/span&gt; &lt;span class="c1"&gt;# Check for latest at link at .NET 5 download page&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dotnet build&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dotnet test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we add commit this file, push, and navigate to the Actions section of our repository, we will see a new workflow run that goes through all of the steps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Links
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://github.com/denolfe/blog-code-samples/tree/master/posts/build-net-5-github-actions"&gt;Source code for this example&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/free-pro-team@latest/actions"&gt;GitHub Actions Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dotnet.microsoft.com/download/dotnet/5.0"&gt;.NET 5 Download page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/dotnet/core/tools/?tabs=netcore2x"&gt;Dotnet CLI documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>dotnet</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Bootstrap your Dotfiles with dotbot</title>
      <dc:creator>Elliot DeNolf</dc:creator>
      <pubDate>Mon, 13 Jul 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/denolfe/bootstrap-your-dotfiles-with-dotbot-4j7f</link>
      <guid>https://forem.com/denolfe/bootstrap-your-dotfiles-with-dotbot-4j7f</guid>
      <description>&lt;p&gt;A customized set of dotfiles can vastly increase your command-line productivity and happiness. Having your dotfiles in a repo allows you to take your configuration anywhere. In this tutorial, we'll be setting up a dotfiles repository and bootstrapping it using &lt;a href="https://github.com/anishathalye/dotbot/"&gt;dotbot&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Dotbot?
&lt;/h2&gt;

&lt;p&gt;While it could be tempting for some to script your dotfiles configuration and installation yourself, I would advise against going this route. I previously went this route, but I would constantly run into edge-cases leading to constant modification of the scripts. With a framework, most of the use-cases have been thought of, so it is very low friction in comparison.&lt;/p&gt;

&lt;p&gt;On investigating a number of tools out there, dotbot's features set it apart from the others:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Single configuration file&lt;/li&gt;
&lt;li&gt;Single command to install on a new machine via symbolic links&lt;/li&gt;
&lt;li&gt;Can be added as a git submodule&lt;/li&gt;
&lt;li&gt;Python is the only dependency (standard for almost all distros)&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;The first step is to get a git repository started and add dotbot as a submodule&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;# Create project directory&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;mkdir &lt;/span&gt;dotfiles
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;dotfiles

&lt;span class="c"&gt;# Initialize Repository&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; git init
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; git submodule add https://github.com/anishathalye/dotbot
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;cp &lt;/span&gt;dotbot/tools/git-submodule/install &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;touch &lt;/span&gt;install.config.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So now we have a few things set up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New git repository&lt;/li&gt;
&lt;li&gt;Dotbot added as a submodule&lt;/li&gt;
&lt;li&gt;Dotbot's &lt;code&gt;install&lt;/code&gt; script copied to the project root&lt;/li&gt;
&lt;li&gt;A blank configuration file&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;p&gt;Next, we'll start modifying our config file. Here is a starting point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;defaults&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;link&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;relink&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;clean&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;~'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;link&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;~/.bashrc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bashrc&lt;/span&gt;
    &lt;span class="s"&gt;~/.zshrc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zshrc&lt;/span&gt;
    &lt;span class="s"&gt;~/.vimrc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vimrc&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;git submodule update --init --recursive&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Installing submodules&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's go through each section to see what it does&lt;/p&gt;

&lt;h3&gt;
  
  
  Defaults
&lt;/h3&gt;

&lt;p&gt;Defaults controls what action will be taken for everything in the &lt;code&gt;link&lt;/code&gt; section. &lt;code&gt;relink&lt;/code&gt; removes the old target if it is a symlink. There are additional options that may be worth looking at in the documentation&lt;/p&gt;

&lt;h3&gt;
  
  
  Clean
&lt;/h3&gt;

&lt;p&gt;This simply defines what directory should be inspected for dead links. Dead links are automatically removed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Link
&lt;/h3&gt;

&lt;p&gt;This is where most of your modifications will take place. Here we define where we want the symlink to be once linked, and what file should be linked there. In the above example, we have 3 files that commonly contain customizations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Shell
&lt;/h3&gt;

&lt;p&gt;This section contains any raw shell commands that you'd like to run upon running your install script. In this case, it installs any submodules.&lt;/p&gt;

&lt;h2&gt;
  
  
  Move files into Repository
&lt;/h2&gt;

&lt;p&gt;Next, we move the files we want to link into our repository. Assuming you want the 3 files specified from above, we can run the following commands to move them in.&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; &lt;span class="nb"&gt;cp&lt;/span&gt; ~/.vimrc ./vimrc
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;cp&lt;/span&gt; ~/.zshrc ./zshrc
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;cp&lt;/span&gt; ~/.bashrc ./bashrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Run Install Script
&lt;/h3&gt;

&lt;p&gt;We can test out if everything works properly by running &lt;code&gt;./install&lt;/code&gt; within our repository. If all is configured properly, you should see something like the following:&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; ./install
All targets have been cleaned
Creating &lt;span class="nb"&gt;link&lt;/span&gt; ~/.bashrc -&amp;gt; ~/.dotfiles/bashrc
Creating &lt;span class="nb"&gt;link&lt;/span&gt; ~/.zshrc -&amp;gt; ~/.dotfiles/zshrc
Creating &lt;span class="nb"&gt;link&lt;/span&gt; ~/.vimrc -&amp;gt; ~/.dotfiles/vimrc
All links have been &lt;span class="nb"&gt;set &lt;/span&gt;up
Installing submodules &lt;span class="o"&gt;[&lt;/span&gt;git submodule update &lt;span class="nt"&gt;--init&lt;/span&gt; &lt;span class="nt"&gt;--recursive&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
All commands have been executed

&lt;span class="o"&gt;==&amp;gt;&lt;/span&gt; All tasks executed successfully
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you are satisfied with how you dotfiles install, be sure to commit your changes and push to a remote repository.&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; git add &lt;span class="nt"&gt;--all&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; git remote add origin git@github.com:username/dotfiles.git
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Use on Multiple Machines
&lt;/h3&gt;

&lt;p&gt;Now that you have a basic dotfiles repository set up, you can push this to a public repository in order to use on multiple machines. On any machine, you can now simply run the following commands to install your dotfiles:&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; git clone git@github.com:username/dotfiles.git &lt;span class="nt"&gt;--recursive&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;dotfiles &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any new changes can be retrieved from the repository and installed using the following commands:&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; git pull
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ./install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;You can now easily maintain your dotfiles in a git repository and share them between your environments.&lt;/p&gt;

&lt;p&gt;Here is my &lt;a href="https://github.com/denolfe/dotfiles"&gt;personal dotfiles&lt;/a&gt; repository that uses dotbot. There are many other places to draw dotfiles inspiration from such as &lt;a href="http://dotfiles.github.io/"&gt;GitHub Does Dotfiles&lt;/a&gt; and &lt;a href="https://github.com/anishathalye/dotbot/wiki/Users"&gt;other dotbot users&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>bash</category>
      <category>scripting</category>
      <category>linux</category>
    </item>
    <item>
      <title>Easily Rerun EC2 UserData</title>
      <dc:creator>Elliot DeNolf</dc:creator>
      <pubDate>Mon, 29 Jun 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/denolfe/easily-rerun-ec2-userdata-3k18</link>
      <guid>https://forem.com/denolfe/easily-rerun-ec2-userdata-3k18</guid>
      <description>&lt;p&gt;EC2 UserData code blocks only run when an EC2 starts up for the first time by default. Rerunning this code can be useful for troubleshooting purposes. However, the way to do this is not very straight forward. Let's go through how to view, verify, and execute your EC2's UserData.&lt;/p&gt;

&lt;p&gt;First, we must log into our EC2 using SSH and our .pem file.&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; ssh &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"my-cert.pem"&lt;/span&gt; ec2-user@my.machine.ip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;This article will not go into the details of how to SSH into a machine, you can learn how to do this from the &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AccessingInstancesLinux.html"&gt;AWS Documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Next, we must elevate to the root user.&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; &lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An EC2's UserData can be accessed at the url: &lt;code&gt;http://instance-data/latest/user-data&lt;/code&gt;, so we can use curl to redirect this to a file in order to inspect 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="o"&gt;&amp;gt;&lt;/span&gt; curl http://instance-data/latest/user-data &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; user-data.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can then inspect the file using &lt;code&gt;cat&lt;/code&gt; or &lt;code&gt;vim&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;cat&lt;/span&gt; ./user-data.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can then modify the permissions and execute 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="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;chmod&lt;/span&gt; +x user-data.sh
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ./user-data.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Other Variations
&lt;/h3&gt;

&lt;p&gt;We can run the script in one single command if we don't want to inspect it first by piping it directly.&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; curl http://instance-data/latest/user-data | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another option is if you'd like to see each line written to STDOUT as it runs, we can enable this by adding &lt;code&gt;set -ex&lt;/code&gt; to the top of our &lt;code&gt;user-data.sh&lt;/code&gt; script before executing it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Links
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html"&gt;AWS: Retrieving Instance Metadata&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AccessingInstancesLinux.html"&gt;AWS: Accessing Linux Instances&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html"&gt;Bash set command&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>scripting</category>
      <category>bash</category>
    </item>
    <item>
      <title>Polyglot Version Management with asdf</title>
      <dc:creator>Elliot DeNolf</dc:creator>
      <pubDate>Thu, 25 Jun 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/denolfe/polyglot-version-management-with-asdf-4nif</link>
      <guid>https://forem.com/denolfe/polyglot-version-management-with-asdf-4nif</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Originally published on my blog at &lt;a href="https://elliotdenolf.com/posts/polyglot-version-management-with-asdf"&gt;elliotdenolf.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Dabbling in multiple programming languages can be a lot of fun. However, because each language tends to have its own version manager, remembering each version manager's commands can get a little cumbersome. This is why the idea of having a single version manager for &lt;em&gt;all&lt;/em&gt; of your languages is very intriguing. &lt;a href="https://asdf-vm.com/"&gt;Asdf&lt;/a&gt; is a version manager that aims to do that. It supports a large number of languages, all with only one set of commands to remember.&lt;/p&gt;

&lt;p&gt;Let's get it installed, add a language plugin, then activate the desired language version.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;Installation is performed using either Homebrew on macOS or Git on Linux.&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;# macOS&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; brew &lt;span class="nb"&gt;install &lt;/span&gt;asdf
&lt;span class="c"&gt;# Linux&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; git clone https://github.com/asdf-vm/asdf.git ~/.asdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You must then source the script by adding the following line to your &lt;code&gt;~/.zshrc&lt;/code&gt;, &lt;code&gt;~/.bashrc&lt;/code&gt;, or &lt;code&gt;~/.bash_profile&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;.&lt;/span&gt; ~/.asdf/asdf.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;See the &lt;a href="https://asdf-vm.com/"&gt;latest documentation&lt;/a&gt; if more detail is needed.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;Asdf has the concept of "plugins" for each supported language. We need to install a plugin for any language we want to use.&lt;/p&gt;

&lt;p&gt;First, let's list all available plugins using &lt;code&gt;asdf plugin-list-all&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; asdf plugin-list-all
golang          https://github.com/kennyp/asdf-golang.git
nodejs          https://github.com/asdf-vm/asdf-nodejs.git
python          https://github.com/danhper/asdf-python.git
ruby            https://github.com/asdf-vm/asdf-ruby.git
&lt;span class="c"&gt;# many more...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From the list, let's add the Node.js plugin.&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; asdf plugin-add nodejs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the plugin is added, we can list all versions available to us.&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; asdf list-all nodejs
12.16.0
12.16.1
12.16.2
12.16.3
12.17.0
12.18.0
12.18.1
&lt;span class="c"&gt;# many more...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any version from the list can be installed, we'll install the latest LTS.&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; asdf &lt;span class="nb"&gt;install &lt;/span&gt;nodejs 12.18.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once installed, we declare this version as the one we'd like to use globally on our system.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; asdf global nodejs 12.18.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you need to run a specific version &lt;em&gt;only&lt;/em&gt; for a single project, we can do that using the &lt;code&gt;asdf local&lt;/code&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;node-10-project
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; asdf &lt;span class="nb"&gt;local &lt;/span&gt;nodejs 10.21.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a file named &lt;code&gt;.tool-versions&lt;/code&gt; that specifies which version should be used within the current project. Asdf will automatically detect and use that version if it is installed.&lt;/p&gt;

&lt;p&gt;The above steps can be repeated for any language we'd like.&lt;/p&gt;

&lt;p&gt;All plugins and versions installed can be viewed with the &lt;code&gt;asdf list&lt;/code&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; asdf list
golang
  1.14
  1.15beta1
nodejs
  10.20.0
  12.18.1
python
  3.8.2
  3.10-dev
ruby
  2.7.0
  2.8.0-dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Asdf is a very powerful tool that can replace aany number of version managers. I'd highly recommend using it if you use more than a couple languages on your system and have a need to switch between versions. Head on over to their &lt;a href="https://asdf-vm.com/"&gt;documentation&lt;/a&gt; if you'd like to learn more.&lt;/p&gt;

&lt;h3&gt;
  
  
  Links
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://asdf-vm.com/"&gt;asdf Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>shell</category>
      <category>linux</category>
      <category>bash</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>My Tech Podcasts: Summer 2020 Edition</title>
      <dc:creator>Elliot DeNolf</dc:creator>
      <pubDate>Tue, 23 Jun 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/denolfe/my-tech-podcasts-summer-2020-edition-1ni6</link>
      <guid>https://forem.com/denolfe/my-tech-podcasts-summer-2020-edition-1ni6</guid>
      <description>&lt;p&gt;Tech podcasts are a great way to stay on top of everything going on in the software development industry. I’ve been listening to podcasts for many years, and my list evolves over time. Here is my current list for Summer 2020:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://softwareengineeringdaily.com/"&gt;Software Engineering Daily&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;This podcast is in the 1 on 1 interview format, and the guests are usually either very prominent figures in the community or someone who has been working on cutting edge technology. This podcast also serves as a way to know what is trending in the enterprise software community.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.indiehackers.com/podcast"&gt;Indie Hackers&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;This podcast goes into the entrepreneurial aspect of software development which can be refreshing. It’s very cool to see how each creator markets and monetizes their software product or service.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://changelog.com/podcast"&gt;The ChangeLog&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;This podcast is similar to Software Engineering Daily in a lot of ways, but they focus more on Open Source instead of enterprise technologies.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.fullstackradio.com/"&gt;Full Stack Radio&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;This is a very web app and JavaScript focused podcast. It involves a lot of interviews about the latest JavaScript frameworks with their creators or advocates.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://syntax.fm/"&gt;Syntax.fm&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;I really like listening to Wes and Scott. They have a great rapport which makes this podcast easy listening. Some of the topics tend to be a little too web development focused or too beginner-friendly for my taste. However, I do like looking at the title of each podcast to see if it’s worth tuning in.&lt;/p&gt;

</description>
      <category>podcast</category>
    </item>
    <item>
      <title>Backup and Restore Visual Studio Code Extensions from the Command Line</title>
      <dc:creator>Elliot DeNolf</dc:creator>
      <pubDate>Mon, 22 Jun 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/denolfe/backup-and-restore-visual-studio-code-extensions-from-the-command-line-2dcc</link>
      <guid>https://forem.com/denolfe/backup-and-restore-visual-studio-code-extensions-from-the-command-line-2dcc</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Originally posted on my blog at &lt;a href="https://elliotdenolf.com/posts/backup-restore-vs-code-extensions"&gt;elliotdenolf.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I maintain my own &lt;a href="https://github.com/denolfe/dotfiles"&gt;dotfiles&lt;/a&gt; to be able to have identical setups between my work and personal machines. My Visual Studio Code configuration is one piece of those. While there is an &lt;a href="https://marketplace.visualstudio.com/items?itemName=Shan.code-settings-sync"&gt;extension&lt;/a&gt; out there that does extension syncing, I didn't find them ideal for my use-case as it also did a settings sync.&lt;/p&gt;

&lt;h2&gt;
  
  
  Backup Extensions
&lt;/h2&gt;

&lt;p&gt;Let's output our list of extensions to a file.&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; code &lt;span class="nt"&gt;--list-extensions&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; extensions.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The file content will be similar to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;donjayamanne.githistory
eamodio.gitlens
johnpapa.vscode-peacock
ms-azuretools.vscode-docker
yzhang.markdown-all-in-one
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Restore Extensions
&lt;/h2&gt;

&lt;p&gt;Once we have our file listing our extensions, we can restore our extensions.&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; &lt;span class="nb"&gt;cat &lt;/span&gt;extensions.txt | xargs &lt;span class="nt"&gt;-L&lt;/span&gt; 1 code &lt;span class="nt"&gt;--install-extension&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This line performs the &lt;code&gt;code --install-extension&lt;/code&gt; command for every line in your file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use with a Script
&lt;/h2&gt;

&lt;p&gt;To make these commands easier to use, we can use a script that wraps the &lt;code&gt;code&lt;/code&gt; command. The following script will add &lt;code&gt;code save-ext&lt;/code&gt; and &lt;code&gt;code install-ext&lt;/code&gt; as commands. Any other standard command will simply be passed through.&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;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; code &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;code&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in
    &lt;/span&gt;save-ext&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Saving code extensions..."&lt;/span&gt;
      code &lt;span class="nt"&gt;--list-extensions&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ~/.dotfiles/vscode/extensions.txt
      &lt;span class="p"&gt;;;&lt;/span&gt;
    install-ext&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Installing code extensions..."&lt;/span&gt;
      &lt;span class="nb"&gt;cat&lt;/span&gt; ~/.dotfiles/vscode/extensions.txt | xargs &lt;span class="nt"&gt;-L&lt;/span&gt; 1 code &lt;span class="nt"&gt;--install-extension&lt;/span&gt;
      &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;command &lt;/span&gt;code &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&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;esac&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To use this script, modify the &lt;code&gt;extensions.txt&lt;/code&gt; file path and also add the script to your PATH.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;code save-ext&lt;/code&gt; will output your extensions to file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;code install-ext&lt;/code&gt; will install them from file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'd recommend saving this file to your personal dotfiles to share between your dev environments. &lt;a href="https://github.com/denolfe/dotfiles"&gt;Here are mine&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Links
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/denolfe/dotfiles"&gt;My Dotfiles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/denolfe/dotfiles/blob/master/functions/code.override.sh"&gt;VS Code override script&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vscode-docs.readthedocs.io/en/stable/extensions/install-extension/"&gt;VS Code Docs: Install Extensions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>productivity</category>
      <category>vscode</category>
      <category>tools</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>C# Scripting using dotnet-script</title>
      <dc:creator>Elliot DeNolf</dc:creator>
      <pubDate>Mon, 15 Jun 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/denolfe/c-scripting-using-dotnet-script-1h06</link>
      <guid>https://forem.com/denolfe/c-scripting-using-dotnet-script-1h06</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Originally posted on my blog at &lt;a href="https://elliotdenolf.com/posts/csharp-scripting-using-dotnet-script/"&gt;elliotdenolf.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;C# is an extremely powerful language but for small, simple tasks it tends to be a bit overkill because of the boilerplate required. dotnet-script is a project that aims to alleviate that friction. It allows C# code to be run as a single script file. No Main method, no .csproj, and transparent compilation.&lt;/p&gt;

&lt;p&gt;Let's create a simple "Hello World" script as well as a more complex script that uses a NuGet package.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;To install dotnet-script, we use the dotnet cli to install it globally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; dotnet tool &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; dotnet-script
&lt;span class="c"&gt;# Optionally include specific version&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; dotnet tool &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; dotnet-script &lt;span class="nt"&gt;--version&lt;/span&gt; 0.53.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The latest version will be listed at &lt;a href="https://www.nuget.org/packages/dotnet-script/"&gt;nuget.org&lt;/a&gt; if you wish to use a specific one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Script
&lt;/h2&gt;

&lt;p&gt;dotnet-script comes with an init command to generate a simple script. Let's create a directory and run that.&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; &lt;span class="nb"&gt;mkdir &lt;/span&gt;dotnet-script-test
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;dotnet-script-test
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; dotnet script init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The init script will generate 2 files: main.csx and omnisharp.json&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;omnisharp.json contains script settings like target framework and allowing nuget references&lt;/li&gt;
&lt;li&gt;main.csx is scaffolded to contain a simple, functioning Hello World example&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The contents of our &lt;code&gt;main.csx&lt;/code&gt; will be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;!/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="n"&gt;dotnet&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;script&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello world!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, test that your script is functioning by running &lt;code&gt;./main.csx&lt;/code&gt; on your command line. You should see the &lt;code&gt;Hello World!&lt;/code&gt; output. Windows machines may need to run the script using &lt;code&gt;dotnet script main.csx&lt;/code&gt; since the "shebang" line may not be included.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use NuGet Package
&lt;/h2&gt;

&lt;p&gt;From here, we can make something a bit more complex. Let's pull in a nuget package and use it.&lt;/p&gt;

&lt;p&gt;In a dotnet script file, nuget packages are pulled in using the &lt;code&gt;#r&lt;/code&gt; directive. In this example, we'll pull in the Bogus nuget package in order to generate some realistic first and last names. If we browse nuget.org to find the &lt;a href="https://www.nuget.org/packages/Bogus"&gt;Bogus&lt;/a&gt; package, the latest version is &lt;code&gt;29.0.2&lt;/code&gt; at the time of writing. Below is how we pull in the package and add a &lt;code&gt;using&lt;/code&gt; statement to make use of it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;!/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="n"&gt;dotnet&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;script&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="s"&gt;"nuget: Bogus, 29.0.2"&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Bogus&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From here, let's create a simple &lt;code&gt;Person&lt;/code&gt; class with FirstName and LastName properties.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;FirstName&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;LastName&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&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;Let's create a person with a random name and output them using the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;peopleGenerator&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Faker&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RuleFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FirstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FirstName&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RuleFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LastName&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;peopleGenerator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Generate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Hello, &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FirstName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&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 this with &lt;code&gt;./main.csx&lt;/code&gt; on the command line. You should see something similar to this output.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ./main.csx
Hello, Darian Bartell
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Overall, I think using dotnet-script is an easy way to have small, concise C# scripts without the overhead of compiling them into command-line applications and having to maintain .csproj files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Links
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://github.com/denolfe/blog-code-samples/tree/master/posts/csharp-scripting-using-dotnet-script"&gt;Source code for this example&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/filipw/dotnet-script"&gt;dotnet-script&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/dotnet/core/tools/global-tools"&gt;.NET Core Tools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nuget.org/packages/Bogus"&gt;Bogus&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>development</category>
    </item>
    <item>
      <title>Chromebook For Development - 1 Year In</title>
      <dc:creator>Elliot DeNolf</dc:creator>
      <pubDate>Thu, 25 Jul 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/denolfe/chromebook-for-development-1-year-in-11bj</link>
      <guid>https://forem.com/denolfe/chromebook-for-development-1-year-in-11bj</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Originally posted on my blog at &lt;a href="https://elliotdenolf.com/posts/chromebook-for-development-1-year-in/"&gt;https://elliotdenolf.com/posts/chromebook-for-development-1-year-in/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With the advent of "Linux on Chromebooks", using a Chromebook for development has become enticing in many ways. They are lightweight, fast to boot, have great battery life, and require no maintenance. When I heard that Linux applications would now be supported out-of-the-box, starting with the Pixelbook, I jumped at the opportunity. I've been doing development on my Pixelbook since June 2018. Below, I summarize my experience so far. &lt;/p&gt;

&lt;h2&gt;
  
  
  Enabling Linux
&lt;/h2&gt;

&lt;p&gt;Anyone that has installed a Linux distro on a laptop knows that you can often run into issues with driver support. Using a Chromebook completely removes any issues related to doing this. It is a simple toggle in settings, wait for the download, then restart. There is now an application called "Terminal" that gives you access to a full Debian version of Linux.&lt;/p&gt;

&lt;h2&gt;
  
  
  Main applications
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Konsole - The default "Terminal" application works fine for simple use-cases. However, most developers will want additional features like tabs, color schemes, etc. There are a handful of options out there. I found Konsole's mix of features to be the sweet spot for me.&lt;/li&gt;
&lt;li&gt;Terminal tools - I have not had any issues running any terminal tool. Anything that Debian will run, runs as expected. I work extensively with the following:

&lt;ul&gt;
&lt;li&gt;Node and TypeScript (tsc)&lt;/li&gt;
&lt;li&gt;.NET Core (dotnet)&lt;/li&gt;
&lt;li&gt;Crystal&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Docker. Though this is terminal-based, I thought it deserved a specific call-out. Initially, I had issues with getting the Docker daemon working properly. I'm happy to say those issues were ironed out late-Summer of 2018. Since then, I've had no issues running a few containers at a time and doing normal Docker tasks.&lt;/li&gt;
&lt;li&gt;VS Code - This is the jackknife of the editor world. It can be used to edit anything, and there is almost always is an extension for the functionality you are looking for. I was concerned at first about the performance because it is an electron-based application, but I have not run into any issues.&lt;/li&gt;
&lt;li&gt;Other applications I make use of are WebStorm, Postman, Slack, Robo3t&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;It is worth noting that my Pixelbook runs an i5 with 8gb of RAM, which is considered very high specs compared to most other Chromebooks in the market.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;No OS-level modifications - Chrome OS does not allow custom global shortcuts or using a different launcher. This was one area that I struggled with since I typically make heavy use of tools like Alfred, Karabiner, and AutoHotkey. These applications provide an extra level of control and automation that just cannot be rivaled on a Chromebook. The OS is very locked down, and not having access to tools like these is a side-effect of that.&lt;/li&gt;
&lt;li&gt;Desktop Environment - Chrome OS provides a shortcut and gesture to give you an overview of all open windows. It also provides basic splitting of windows left and right. However, the shortcut to split windows is not recognized when a Linux application is in focus, so I have to use the mouse to snap the window to the side to split it. "Virtual Desks" are scheduled to roll out to the Stable channel within the next few weeks. A feature I am very excited about and is a move in the right direction for the desktop environment in Chrome OS.&lt;/li&gt;
&lt;li&gt;Graphics applications. GPU support for Linux is just being rolled out. However, until now any applications that would take advantage of a graphics card are very anemic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Overall, if your development workflow uses a Linux-based OS, a terminal, and (optionally) an editor, I feel you may be genuinely surprised how well a Chromebook will work as a development machine.&lt;/p&gt;

</description>
      <category>development</category>
      <category>chromebook</category>
      <category>chromeos</category>
      <category>tools</category>
    </item>
    <item>
      <title>Build .NET Core Applications using GitLab CI</title>
      <dc:creator>Elliot DeNolf</dc:creator>
      <pubDate>Mon, 01 Apr 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/denolfe/build-net-core-applications-using-gitlab-ci-448c</link>
      <guid>https://forem.com/denolfe/build-net-core-applications-using-gitlab-ci-448c</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Originally posted on my blog at &lt;a href="https://elliotdenolf.com/posts/build-net-core-applications-using-gitlab-ci" rel="noopener noreferrer"&gt;https://elliotdenolf.com/posts/build-net-core-applications-using-gitlab-ci&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Updated for .NET 5&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;GitLab CI has the ability to utilize any docker container in order to build and deploy an application. This makes it an extremely flexible tool. This article will go through building out a GitLab CI pipeline for a .NET Core application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Basic Application
&lt;/h2&gt;

&lt;p&gt;First, let’s build out our basic application and test suite using the dotnet CLI. Create a new directory for your project and go through the following commands:&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;MyApp MyApp.Tests
&lt;span class="nb"&gt;cd &lt;/span&gt;MyApp
dotnet new webapi
&lt;span class="nb"&gt;cd&lt;/span&gt; ../MyApp.Tests
dotnet new nunit
&lt;span class="nb"&gt;cd&lt;/span&gt; ..
dotnet new sln
dotnet sln add &lt;span class="k"&gt;**&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.csproj
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ve now built out a basic application and test suite to use in our pipeline.&lt;/p&gt;

&lt;p&gt;Before we move forward, here is our simplified project structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
|-- MyApp
| |-- MyApp.csproj
| |-- Program.cs
| `-- Startup.cs
|-- MyApp.Tests
| |-- MyApp.Tests.csproj
| `-- UnitTest1.cs
`-- MyApp.sln
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create Pipeline
&lt;/h2&gt;

&lt;p&gt;GitLab CI is driven by having a &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; file at the root of the project. Create this file now. The first thing we will put into this file is our base image&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mcr.microsoft.com/dotnet/sdk:5.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the official dotnet core image from Microsoft. It includes the latest .NET Core SDK, which provides everything needed to build out an application. A container running this image will be where our scripts will run and application code cloned into. The version can also be changed to build previous dotnet versions.&lt;/p&gt;

&lt;p&gt;Next, we define our stages. We will be building out 3 distinct stages: build, test, and release. Those are added by adding this block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;stages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;release&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that our stages are set up, we can add build steps and associate them with our defined stages. We just happen to want a single step for each of the build and test stages.&lt;/p&gt;

&lt;p&gt;Let’s add the build step that will build via the dotnet CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;dotnet build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The top-level value can be named anything you’d like. If you only have one step per stage, keeping them the same name keeps things simple. However, the &lt;code&gt;stage&lt;/code&gt; value must specify one of the listed stages from above. This is how GitLab CI knows what stage to run this step on.&lt;/p&gt;

&lt;p&gt;If the build stage succeeds, we want our unit tests to run. The following block will run the test command via the dotnet CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;dotnet test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The release step will be more complex than the previous because we will be using the dotnet publish command along with capturing the generated artifacts. This is the release block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;release&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;release&lt;/span&gt;
  &lt;span class="na"&gt;only&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;
  &lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;publish/&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;dotnet publish -c Release -o ../publish MyApp/MyApp.csproj&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s break down each piece of this step.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;only&lt;/code&gt; section specifies that this step will &lt;em&gt;only&lt;/em&gt; run on our master branch. This is to avoid generating artifacts for feature branches&lt;/li&gt;
&lt;li&gt;The artifacts path points to our intended output directory&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;dotnet publish&lt;/code&gt; command outputs into a &lt;code&gt;publish&lt;/code&gt; directory at our project root. Make note of the &lt;code&gt;../&lt;/code&gt; before publish as the dotnet publish command outputs relative to the &lt;code&gt;csproj&lt;/code&gt; file that is specified, so we want the artifacts to be output one directory up at the root.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our full &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; file should now look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;image &lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mcr.microsoft.com/dotnet/sdk:5.0&lt;/span&gt;

&lt;span class="na"&gt;stages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;release&lt;/span&gt;

&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;dotnet build&lt;/span&gt;

&lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;dotnet test&lt;/span&gt;

&lt;span class="na"&gt;release&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;release&lt;/span&gt;
  &lt;span class="na"&gt;only&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;
  &lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;publish/&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;dotnet publish -c Release -o ../publish MyApp/MyApp.csproj&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we commit this code and navigate to CI/CD -&amp;gt; Pipelines in our GitLab repository, we will be able to see a pipeline that ran for the commit. If successful, each step will have a green check and artifacts will be available for download. If there was a failure, it will be a red X that can be clicked on to see more detail.&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%2Felliotdenolf.com%2Fmedia%2Fpipeline-1.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%2Felliotdenolf.com%2Fmedia%2Fpipeline-1.png" alt="GitLab CI Pipeline"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Additional steps can be added beyond what I’ve provided, but they would differ greatly based upon your environment. This application could easily be put into a Docker image and pushed either to the GitLab image registry or your Kubernetes cluster. However, that would warrant an entire article on its own. You now have a basic CI pipeline for your .NET Core application!&lt;/p&gt;

&lt;h3&gt;
  
  
  Links
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://github.com/denolfe/blog-code-samples/tree/master/posts/build-net-core-using-gitlab-ci" rel="noopener noreferrer"&gt;Source code for this example&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.gitlab.com/ee/ci/" rel="noopener noreferrer"&gt;GitLab CI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/dotnet/core/tools/?tabs=netcore2x" rel="noopener noreferrer"&gt;Dotnet CLI documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>gitlab</category>
      <category>devops</category>
    </item>
    <item>
      <title>Cucumber.js with TypeScript</title>
      <dc:creator>Elliot DeNolf</dc:creator>
      <pubDate>Fri, 29 Mar 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/denolfe/cucumber-js-with-typescript-44cg</link>
      <guid>https://forem.com/denolfe/cucumber-js-with-typescript-44cg</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Originally posted on my blog at &lt;a href="https://elliotdenolf.com/posts/cucumberjs-with-typescript"&gt;https://elliotdenolf.com/posts/cucumberjs-with-typescript&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Cucumber.js is the JavaScript implementation of &lt;a href="https://cucumber.io/"&gt;Cucumber&lt;/a&gt;. The main benefit of writing automated tests for Cucumber is that they are written in plain English, so any non-technical person can read the scenarios and know what is being tested. This is extremely powerful in larger organizations because it allows developers, testers, and business stakeholders to better communicate and collaborate.&lt;/p&gt;

&lt;p&gt;This post will go through setting up a basic Cucumber.js suite using TypeScript and &lt;a href="https://www.npmjs.com/package/cucumber-tsflow"&gt;cucumber-tsflow&lt;/a&gt;. Cucumber-tsflow is a package that will allow us to take advantage of TypeScript’s &lt;a href="https://www.typescriptlang.org/docs/handbook/decorators.html"&gt;decorators&lt;/a&gt;, which make for clearer step definition code.&lt;/p&gt;

&lt;p&gt;The first step will be installing our dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; cucumber cucumber-tsflow cucumber-pretty ts-node typescript chai
npm i &lt;span class="nt"&gt;-D&lt;/span&gt; @types/cucumber @types/chai
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;experimentalDecorators&lt;/code&gt; must also be set to &lt;code&gt;true&lt;/code&gt; in your &lt;code&gt;tsconfig.json&lt;/code&gt; in order for the decorators to compile properly.&lt;/p&gt;

&lt;p&gt;The two main components for cucumber tests are feature files and step definitions. Let’s start out by creating a &lt;code&gt;features&lt;/code&gt; directory then creating a file named &lt;code&gt;bank-account.feature&lt;/code&gt; inside it. Our example will be testing the basic functionality of a bank account.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# features/bank-account.feature
Feature: Bank Account

  Scenario: Stores money
    Given A bank account with starting balance of $100
    When $100 is deposited
    Then The bank account balance should be $200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This defines a single scenario for depositing money into a bank account. Next, we will create a directory named &lt;code&gt;step-definitions&lt;/code&gt; and create a file named &lt;code&gt;bank-account.steps.ts&lt;/code&gt; within it.&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;binding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;given&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;when&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cucumber-tsflow&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;assert&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chai&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="nd"&gt;binding&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;BankAccountSteps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;accountBalance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;given&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/A bank account with starting balance of &lt;/span&gt;&lt;span class="se"&gt;\$(\d&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;givenAnAccountWithStartingBalance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&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="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;accountBalance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;amount&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="nd"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\$(\d&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt; is deposited/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;deposit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&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="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;accountBalance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Number&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;accountBalance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&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="nd"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/The bank account balance should be &lt;/span&gt;&lt;span class="se"&gt;\$(\d&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;accountBalanceShouldEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expectedAmount&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="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;equal&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;accountBalance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expectedAmount&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;We are utilizing the &lt;a href="https://www.npmjs.com/package/cucumber-tsflow"&gt;cucumber-tsflow&lt;/a&gt; package which exposes some very useful &lt;a href="https://www.typescriptlang.org/docs/handbook/decorators.html"&gt;decorators&lt;/a&gt; for our &lt;code&gt;Given&lt;/code&gt;, &lt;code&gt;When&lt;/code&gt;, and &lt;code&gt;Then&lt;/code&gt; steps. The code within each step is fairly simple. The &lt;code&gt;Given&lt;/code&gt; step initializes the &lt;code&gt;accountBalance&lt;/code&gt;, the &lt;code&gt;When&lt;/code&gt; step adds to the balance, and the &lt;code&gt;Then&lt;/code&gt; step asserts its value.&lt;/p&gt;

&lt;p&gt;Some specific things to note: this file exports a single class which has the &lt;code&gt;@binding()&lt;/code&gt; decorator on it which is required for cucumber-tsflow to pick up the steps. Each step definition must also have a &lt;code&gt;@given&lt;/code&gt;, &lt;code&gt;@when&lt;/code&gt; or &lt;code&gt;@then&lt;/code&gt; decorator on it. These decorators take a regular expression as a parameter which is how the lines in the feature file map to the code. Also, make note that there are capture groups in the expressions to capture values from the text and are subsequently passed as parameters to the function.&lt;/p&gt;

&lt;p&gt;Cucumber is run using the &lt;code&gt;cucumber-js&lt;/code&gt; command with a series of command-line switches. However, this can optionally be put into a &lt;code&gt;cucumber.js&lt;/code&gt; file at the root of the project. Create a &lt;code&gt;cucumber.js&lt;/code&gt; file at the root of the project with the following contents:&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;// cucumber.js&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;common&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;features/**/*.feature&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Specify our feature files&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--require-module ts-node/register&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Load TypeScript module&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--require step-definitions/**/*.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Load step definitions&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--format progress-bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Load custom formatter&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--format node_modules/cucumber-pretty&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// Load custom formatter&lt;/span&gt;
&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;common&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Putting the configuration in this file allows us to simply pass the profile name to &lt;code&gt;cucumber-js&lt;/code&gt; (&lt;code&gt;default&lt;/code&gt; in our case) instead of a long list of arguments. This file is building out all of the command line arguments, joining them, then exporting them under a named property. Let’s add an npm script to our &lt;code&gt;package.json&lt;/code&gt;, so we can easily run it.&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="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;package.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./node_modules/.bin/cucumber-js -p default"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The structure of your project should now 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;.
|-- cucumber.js
|-- features
| `-- bank-account.feature
|-- package.json
|-- step-definitions
| `-- bank-account.steps.ts
`-- tsconfig.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when we run &lt;code&gt;npm test&lt;/code&gt;, &lt;code&gt;cucumber-js&lt;/code&gt; inside of our &lt;code&gt;node_modules&lt;/code&gt; will be executed with the &lt;code&gt;-p default&lt;/code&gt; switch denoting the default profile exported from our &lt;code&gt;cucumber.js&lt;/code&gt; file we created earlier.&lt;/p&gt;

&lt;p&gt;The output should be something similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Feature: Bank Account

  Scenario: Stores money
    Given A bank account with starting balance of $100
    When $100 is deposited
    Then The bank account balance should be $200

1 scenario (1 passed)
3 steps (3 passed)
0m00.004s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it! You’re up and going with Cucumber and TypeScript!&lt;/p&gt;

&lt;h3&gt;
  
  
  Links
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://github.com/denolfe/blog-code-samples/tree/master/posts/cucumber-with-typescript"&gt;Source code for this example&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cucumber.io/"&gt;Cucumber&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/cucumber-tsflow"&gt;cucumber-tsflow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.cucumber.io/gherkin/reference/"&gt;Feature file syntax reference (Gherkin)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>testing</category>
      <category>typescript</category>
      <category>node</category>
      <category>cucumber</category>
    </item>
  </channel>
</rss>
