<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Martin Šošić</title>
    <description>The latest articles on DEV Community by Martin Šošić (@martinsos).</description>
    <link>https://dev.to/martinsos</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%2F334063%2Fe1ab22c6-43b9-46f2-80c7-d19a2a80d25b.jpeg</url>
      <title>DEV Community: Martin Šošić</title>
      <link>https://dev.to/martinsos</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/martinsos"/>
    <language>en</language>
    <item>
      <title>Wow this is amazing work, a look into the future for us at Wasp!</title>
      <dc:creator>Martin Šošić</dc:creator>
      <pubDate>Mon, 17 Nov 2025 17:25:08 +0000</pubDate>
      <link>https://dev.to/martinsos/wow-this-is-amazing-work-a-look-into-the-future-for-us-at-wasp-1a14</link>
      <guid>https://dev.to/martinsos/wow-this-is-amazing-work-a-look-into-the-future-for-us-at-wasp-1a14</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/genyus" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F439783%2F2c7c26a9-07e8-4c2c-a546-160fd0c30ad8.jpeg" alt="genyus"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/genyus/introduction-to-swarm-an-extensible-typescript-code-generation-framework-50p0" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Introduction to Swarm: an extensible Typescript code generation framework&lt;/h2&gt;
      &lt;h3&gt;Gary McPherson ・ Nov 17&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#typescript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#opensource&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#resources&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>typescript</category>
      <category>opensource</category>
      <category>webdev</category>
      <category>resources</category>
    </item>
    <item>
      <title>We are having a fun hackaton, upload design idea as simple as you like!</title>
      <dc:creator>Martin Šošić</dc:creator>
      <pubDate>Wed, 15 Oct 2025 09:37:08 +0000</pubDate>
      <link>https://dev.to/martinsos/we-are-having-a-fun-hackaton-upload-design-idea-as-simple-as-you-like-4nb3</link>
      <guid>https://dev.to/martinsos/we-are-having-a-fun-hackaton-upload-design-idea-as-simple-as-you-like-4nb3</guid>
      <description>&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/wasp/join-wasp-design-ai-thon-pimp-our-website-and-win-trmnl-playdate-and-ob-4-3gj8" class="crayons-story__hidden-navigation-link"&gt;🎨 Join Wasp Design-AI-Thon - Pimp our website and win TRMNL, Playdate, and OB-4! 🤖&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;
          &lt;a class="crayons-logo crayons-logo--l" href="/wasp"&gt;
            &lt;img alt="Wasp logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F3369%2Fc86918f8-76a9-4b01-accf-cc257f9ee56f.png" class="crayons-logo__image"&gt;
          &lt;/a&gt;

          &lt;a href="/matijasos" class="crayons-avatar  crayons-avatar--s absolute -right-2 -bottom-2 border-solid border-2 border-base-inverted  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F331796%2F5d3fd56d-440c-437c-bcda-152c482774b9.jpeg" alt="matijasos profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/matijasos" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Matija Sosic
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Matija Sosic
                
              
              &lt;div id="story-author-preview-content-2920275" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/matijasos" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F331796%2F5d3fd56d-440c-437c-bcda-152c482774b9.jpeg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Matija Sosic&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

            &lt;span&gt;
              &lt;span class="crayons-story__tertiary fw-normal"&gt; for &lt;/span&gt;&lt;a href="/wasp" class="crayons-story__secondary fw-medium"&gt;Wasp&lt;/a&gt;
            &lt;/span&gt;
          &lt;/div&gt;
          &lt;a href="https://dev.to/wasp/join-wasp-design-ai-thon-pimp-our-website-and-win-trmnl-playdate-and-ob-4-3gj8" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Oct 13 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/wasp/join-wasp-design-ai-thon-pimp-our-website-and-win-trmnl-playdate-and-ob-4-3gj8" id="article-link-2920275"&gt;
          🎨 Join Wasp Design-AI-Thon - Pimp our website and win TRMNL, Playdate, and OB-4! 🤖
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/vibecoding"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;vibecoding&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/design"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;design&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/hackathon"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;hackathon&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/wasp/join-wasp-design-ai-thon-pimp-our-website-and-win-trmnl-playdate-and-ob-4-3gj8" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;38&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/wasp/join-wasp-design-ai-thon-pimp-our-website-and-win-trmnl-playdate-and-ob-4-3gj8#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              4&lt;span class="hidden s:inline"&gt; comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;




</description>
      <category>vibecoding</category>
      <category>design</category>
      <category>webdev</category>
      <category>hackathon</category>
    </item>
    <item>
      <title>Why Naming is #1 Skill for Writing Clean Code 🧼🧑‍💻</title>
      <dc:creator>Martin Šošić</dc:creator>
      <pubDate>Wed, 11 Oct 2023 12:46:36 +0000</pubDate>
      <link>https://dev.to/wasp/why-naming-is-1-skill-for-writing-clean-code-4a5p</link>
      <guid>https://dev.to/wasp/why-naming-is-1-skill-for-writing-clean-code-4a5p</guid>
      <description>&lt;p&gt;In stories, you will often find the motif of a powerful demon that &lt;strong&gt;can be controlled only by knowing its true name&lt;/strong&gt;. Once the hero finds out that name, through cunning dialogue or by investigating ancient tomes, they can turn things around and banish the demon!&lt;/p&gt;

&lt;p&gt;I firmly believe writing code is not much different: through finding good names for functions, variables, and other constructs, we truly recognize the essence of the problem we are solving. &lt;strong&gt;The consequence of clarity gained is not just good names but also cleaner code and improved architecture&lt;/strong&gt;.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi9egdxs8uo4256ioir3x.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi9egdxs8uo4256ioir3x.png" alt="The power of correct naming in programming"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I would go as far as to say that &lt;strong&gt;90% of writing clean code is “just” naming things correctly&lt;/strong&gt;.&lt;br&gt;
Sounds simple, but it is really not!&lt;/p&gt;

&lt;p&gt;Let’s take a look at a couple of examples.&lt;/p&gt;
&lt;h1&gt;
  
  
  Example #1
&lt;/h1&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Given first and last name of a person, returns the&lt;/span&gt;
&lt;span class="c1"&gt;// demographic statistics for all matching people.&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;demo&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;avg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])),&lt;/span&gt;
    &lt;span class="nf"&gt;median&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;What is wrong with this code?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The name of the function &lt;code&gt;demo&lt;/code&gt; is &lt;strong&gt;very vague&lt;/strong&gt;: it could stand for “demolish”, or as in “giving a demo/presentation”, … .&lt;/li&gt;
&lt;li&gt;Names &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;b&lt;/code&gt;, and &lt;code&gt;c&lt;/code&gt; are &lt;strong&gt;completely uninformative&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;a&lt;/code&gt; is reused in lambda inside the &lt;code&gt;map&lt;/code&gt;, &lt;strong&gt;shadowing&lt;/strong&gt; the &lt;code&gt;a&lt;/code&gt; that is a function argument, confusing the reader and making it easier to make a mistake when modifying the code in the future and reference the wrong variable.&lt;/li&gt;
&lt;li&gt;The returned object doesn’t have any info about what it contains, instead, &lt;strong&gt;you need to be careful about the order of its elements&lt;/strong&gt; when using it later.&lt;/li&gt;
&lt;li&gt;The name of the field &lt;code&gt;.info&lt;/code&gt; in the result of a call to &lt;code&gt;users()&lt;/code&gt; function gives us &lt;strong&gt;no information as to what it contains&lt;/strong&gt;, which is made further worse by its elements being accessed by their position, also hiding any information about them and making our code prone to silently work wrong if their ordering changes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s fix it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fetchDemographicStatsForFirstAndLastName&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lastName&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchUsersByFirstAndLastName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lastName&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;averageAge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;avg&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="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="na"&gt;medianSalary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;median&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="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;salary&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;What did we do?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The name of the function now exactly reflects what it does, no more no less&lt;/strong&gt;. &lt;code&gt;fetch&lt;/code&gt; in the name even indicates it does some IO (input/output, in this case fetching from the database), which can be good to know since IO is relatively slow/expensive compared to pure code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;We made other names informative enough&lt;/strong&gt;: not too much, not too little.

&lt;ul&gt;
&lt;li&gt;Notice how &lt;strong&gt;we used the name &lt;code&gt;users&lt;/code&gt; for fetched users&lt;/strong&gt;, and not something longer like &lt;code&gt;usersWithSpecifiedFirstAndLastName&lt;/code&gt; or &lt;code&gt;fetchedUsers&lt;/code&gt;: there is no need for a longer name, as this variable is very local, short-lived, and there is enough context around it to make it clear what it is about.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inside lambda, we went with a single-letter name&lt;/strong&gt;, &lt;code&gt;u&lt;/code&gt;, which might seem like bad practice. But, here, it is perfect: this variable is extremely short-lived, and it is clear from context what it stands for. Also, we picked specifically the letter &lt;code&gt;u&lt;/code&gt; for a reason, as it is the first letter of &lt;code&gt;user&lt;/code&gt;, therefore making that connection obvious.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;We named values in the object that we return&lt;/strong&gt;: &lt;code&gt;averageAge&lt;/code&gt; and &lt;code&gt;medianSalary&lt;/code&gt;. Now any code that will use our function won’t need to rely on the ordering of items in the result, and also will be easy and informative to read.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Finally, notice how there is no comment above the function anymore. The thing is, &lt;strong&gt;the comment is not needed anymore&lt;/strong&gt;: it is all clear from the function name and arguments!&lt;/p&gt;

&lt;h1&gt;
  
  
  Example 2
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Find a free machine and use it, or create a new machine&lt;/span&gt;
&lt;span class="c1"&gt;// if needed. Then on that machine, set up the new worker &lt;/span&gt;
&lt;span class="c1"&gt;// with the given Docker image and setup cmd. Finally,&lt;/span&gt;
&lt;span class="c1"&gt;// start executing a job on that worker and return its id.&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getJobId&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;machineType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;machineRegion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;workerDockerImage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;workerSetupCmd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;jobDescription&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;In this example, we are ignoring the implementation details and will focus just on getting the name and arguments right.&lt;/p&gt;

&lt;p&gt;What is wrong with this code?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The function name is hiding a lot of details about what it is doing&lt;/strong&gt;. It doesn’t mention at all that we have to procure the machine or set up the worker, or that function will result in the creation of a job that will continue executing somewhere in the background. Instead, it gives a feeling that we are doing something simple, due to the verb &lt;code&gt;get&lt;/code&gt;: we are just obtaining an id of an already existing job.
Imagine seeing a call to this function somewhere in the code: &lt;code&gt;getJobId(...)&lt;/code&gt; → &lt;strong&gt;you are not expecting it to take long or do all of the stuff that it really does, which is bad&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ok, this sounds easy to fix, let’s give it a better name!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;procureFreeMachineAndSetUpTheDockerWorkerThenStartExecutingTheJob&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;machineType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;machineRegion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;workerDockerImage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;workerSetupCmd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;jobDescription&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;&lt;strong&gt;Uff, that is one long and complicated name&lt;/strong&gt;. But the truth is, that we can’t really make it shorter without losing valuable information about what this function does and what we can expect from it. Therefore, &lt;strong&gt;we are stuck&lt;/strong&gt;, we can’t find a better name! What now?&lt;/p&gt;

&lt;p&gt;The thing is, &lt;strong&gt;you can't give a good name if you don't have clean code behind it&lt;/strong&gt;. So a bad name is not just a naming mishap, but often also an indicator of problematic code behind it, a failure in design. Code so problematic, that you don’t even know what to name it → there is no straightforward name to give to it, because it is not a straightforward code!&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fasuebjrs1mwtnrk2jdw4.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fasuebjrs1mwtnrk2jdw4.png" alt="Bad name is hiding bad code"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In our case, the problem is that this &lt;strong&gt;function is trying to do too much at once&lt;/strong&gt;. A long name and many arguments are indicators of this, although these can be okay in some situations. Stronger indicators are the usage of words “and” and “then” in the name, as well as argument names that can be grouped by prefixes (&lt;code&gt;machine&lt;/code&gt;, &lt;code&gt;worker&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The solution here is to clean up the code by breaking down the function into multiple smaller functions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;procureFreeMachine&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;region&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setUpDockerWorker&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;machineId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dockerImage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setupCmd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;startExecutingJob&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;workerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;jobDescription&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;h1&gt;
  
  
  What is a good name?
&lt;/h1&gt;

&lt;p&gt;But let’s take a step back - what is a bad name, and what is a good name? What does that mean, how do we recognize them?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good name doesn’t misdirect, doesn’t omit, and doesn’t assume&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A good name should give you a good idea about what the variable contains or function does. A good name will tell you all there is to know or will tell you enough to know where to look next. It will not let you guess, or wonder. It will not misguide you. A good name is obvious, and expected. It is consistent. Not overly creative. It will not assume context or knowledge that the reader is not likely to have.&lt;/p&gt;

&lt;p&gt;Also, &lt;strong&gt;context is king:&lt;/strong&gt; you can’t evaluate the name without the context in which it is read. &lt;code&gt;verifyOrganizationChainCredentials&lt;/code&gt; could be a terrible name or a great name. &lt;code&gt;a&lt;/code&gt; could be a great name or a terrible name. It depends on the story, the surroundings, on the problem the code is solving. Names tell a story, and they need to fit together like a story.&lt;/p&gt;

&lt;h1&gt;
  
  
  Examples of famous bad names
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;JavaScript&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;I was the victim of this bad naming myself: my parents bought me a book about JavaScript while I wanted to learn Java.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;HTTP Authorization header&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;It is named &lt;code&gt;Authorization&lt;/code&gt;, but is used for authentication! And those are not the same: authentication is about identifying yourself, and authorization is about granting permissions. More about it can be found here: &lt;a href="https://stackoverflow.com/questions/30062024/why-is-the-http-header-for-authentication-called-authorization" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/30062024/why-is-the-http-header-for-authentication-called-authorization&lt;/a&gt; .&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Wasp-lang&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;This one is my fault: &lt;a href="https://wasp-lang.dev/" rel="noopener noreferrer"&gt;Wasp&lt;/a&gt; is a full-stack JS web framework that uses a custom config language as only a small part of its codebase, but I put &lt;code&gt;-lang&lt;/code&gt; in the name and scared a lot of people away because they thought it was a whole new general programming language!&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h1&gt;
  
  
  Support us! 🙏⭐️
&lt;/h1&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fid9s6t8rcvfxty40bv2m.gif" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fid9s6t8rcvfxty40bv2m.gif" alt="GH star click"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To help us improve our name at Wasp-lang 😁, &lt;a href="https://github.com/wasp-lang/wasp" rel="noopener noreferrer"&gt;consider giving us a star on Github&lt;/a&gt;! Everything we do at Wasp is open source, and your support helps us make web development easier and motivates us to write more articles like this one.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqgbmn45pia04bxt6zf83.gif" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqgbmn45pia04bxt6zf83.gif" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  How to come up with a good name
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Don’t give a name, find it
&lt;/h2&gt;

&lt;p&gt;The best advice is maybe not to give a name, but instead to &lt;strong&gt;find out&lt;/strong&gt; a name. You shouldn’t be making up an original name, as if you are naming a pet or a child; &lt;strong&gt;you are instead looking for the essence of the thing you are naming, and the name should present itself based on it&lt;/strong&gt;. If you don’t like the name you discovered, it means you don’t like the thing you are naming, and you should change that thing by improving the design of your code (as we did in the example #2).&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsvd322vp7ho9holekwbp.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsvd322vp7ho9holekwbp.png" alt="You shouldn't name your variables the same way you name your pets, and vice versa"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Things to look out for when figuring out a name
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;First, make sure it is not a bad name :)&lt;/strong&gt;. Remember: don’t misdirect, don’t omit, don’t assume.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make it reflect what it represents.&lt;/strong&gt; Find the essence of it, capture it in the name. Name is still ugly? Improve the code. You have also other things to help you here → type signature, and comments. But those come secondary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make it play nicely with the other names around it.&lt;/strong&gt; It should have a clear relation to them - be in the same “world”. It should be similar to similar stuff, opposite to opposite stuff. It should make a story together with other names around it. It should take into account the context it is in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Length follows the scope&lt;/strong&gt;. In general, the shorter-lived the name is, and the smaller its scope is, the shorter the name can/should be, and vice versa. This is why it can be ok to use one-letter variables in short lambda functions. If not sure, go for the longer name.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stick to the terminology you use in the codebase&lt;/strong&gt;. If you so far used the term &lt;code&gt;server&lt;/code&gt;, don’t for no reason start using the term &lt;code&gt;backend&lt;/code&gt; instead. Also, if you use &lt;code&gt;server&lt;/code&gt; as a term, you likely shouldn't go with &lt;code&gt;frontend&lt;/code&gt;: instead, you will likely want to use &lt;code&gt;client&lt;/code&gt;, which is a term more closely related to the &lt;code&gt;server&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stick to the conventions you use in the codebase&lt;/strong&gt;. Examples of some of the conventions that I often use in my codebases:

&lt;ul&gt;
&lt;li&gt;prefix &lt;code&gt;is&lt;/code&gt; when the variable is Bool (e.g. &lt;code&gt;isAuthEnabled&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;prefix &lt;code&gt;ensure&lt;/code&gt; for the functions that are idempotent, that will do something (e.g allocate a resource) only if it hasn’t been set up so far (e.g. &lt;code&gt;ensureServerIsRunning&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The simple technique for figuring out a name every time
&lt;/h2&gt;

&lt;p&gt;If you are ever having trouble coming up with a name, do the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write a comment above the function/variable where you &lt;strong&gt;describe what it is, in human language&lt;/strong&gt;, as if you were describing it to your colleague. It might be one sentence or multiple sentences.
This is the essence of what your function/variable does, what it is.&lt;/li&gt;
&lt;li&gt;Now, you take the role of the sculptor, and you chisel at and &lt;strong&gt;shape that description of your function/variable until you get a name&lt;/strong&gt;, by taking pieces of it away. You stop when you feel that one more hit of your imagined chisel at it would take too much away.&lt;/li&gt;
&lt;li&gt;Is your name still too complex/confusing? If that is so, that means that the code behind is too complex, and should be reorganized! &lt;strong&gt;Go refactor it&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ok, all done&lt;/strong&gt; → you have a nice name!&lt;/li&gt;
&lt;li&gt;That comment above the function/variable? Remove everything from it that is now captured in the name + arguments + type signature. If you can remove the whole comment, great. Sometimes you can’t, because some stuff can’t be captured in the name (e.g. certain assumptions, explanations, examples, …), and that is also okay. But don’t repeat in the comment what you can say in the name instead. &lt;strong&gt;Comments are a necessary evil and are here to capture knowledge that you can’t capture in your names and/or types&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Don’t get overly stuck on always figuring out the perfect name at the get-go → it is okay to do multiple iterations of your code, with both your code and name improving with each iteration.&lt;/p&gt;

&lt;h1&gt;
  
  
  Reviewing code with naming in mind
&lt;/h1&gt;

&lt;p&gt;Once you start thinking a lot about naming, you will see how it will change your code review process: focus shifts from looking at implementation details to looking at names first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When I am doing a code review, there is one predominant thought I will be thinking about: “Is this name clear?”&lt;/strong&gt;. From there, the whole review evolves and results in clean code.&lt;/p&gt;

&lt;p&gt;Inspecting a name is a single point of pressure, that untangles the whole mess behind it. Search for bad names, and you will sooner or later uncover the bad code if there is some.&lt;/p&gt;

&lt;h1&gt;
  
  
  Further reading
&lt;/h1&gt;

&lt;p&gt;If you haven’t yet read it, I would recommend reading the book &lt;strong&gt;Clean Code by Robert Martin&lt;/strong&gt;. It has a great chapter on naming and also goes much further on how to write code that you and others will enjoy reading and maintaining.&lt;/p&gt;

&lt;p&gt;Also, &lt;a href="https://martinfowler.com/bliki/TwoHardThings.html" rel="noopener noreferrer"&gt;A popular joke about naming being hard&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How we built a GPT code agent 🤖 that generates full-stack web apps in React &amp; Node.js, explained simply</title>
      <dc:creator>Martin Šošić</dc:creator>
      <pubDate>Tue, 18 Jul 2023 13:22:18 +0000</pubDate>
      <link>https://dev.to/wasp/how-we-built-a-gpt-code-agent-that-generates-full-stack-web-apps-in-react-nodejs-explained-simply-4f9</link>
      <guid>https://dev.to/wasp/how-we-built-a-gpt-code-agent-that-generates-full-stack-web-apps-in-react-nodejs-explained-simply-4f9</guid>
      <description>&lt;p&gt;We created &lt;a href="https://magic-app-generator.wasp-lang.dev/" rel="noopener noreferrer"&gt;GPT Web App Generator&lt;/a&gt;, which lets you shortly describe the web app you would like to create, and in a matter of minutes, a full-stack codebase, written in React, Node.js, Prisma, and Wasp, will be generated right in front of you, and available to download and run locally!&lt;/p&gt;

&lt;p&gt;We started this as an experiment, to see how well we could use GPT to generate full-stack web apps in &lt;a href="https://wasp-lang.dev/" rel="noopener noreferrer"&gt;Wasp&lt;/a&gt;, the open-source JS web app framework that we are developing. Since we launched, we had more than 3000 apps generated in just a couple of days!&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Falklc7kznxj6vv2cjgra.gif" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Falklc7kznxj6vv2cjgra.gif" alt="How it works"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check out &lt;a href="https://dev.to/wasp/gpt-web-app-generator-let-ai-create-a-full-stack-react-nodejs-codebase-based-on-your-description-2g39"&gt;this blog post&lt;/a&gt; to see GPT Web App Generator in action, including a one-minute demo video, few example apps, and learn a bit more about our plans for the future. Or, try it out yourself at &lt;a href="https://magic-app-generator.wasp-lang.dev/" rel="noopener noreferrer"&gt;https://magic-app-generator.wasp-lang.dev/&lt;/a&gt; !&lt;/p&gt;

&lt;p&gt;In this blog post, &lt;strong&gt;we are going to explore the technical side of creating the GPT Web App Generator&lt;/strong&gt;: techniques we used, how we engineered our prompts, challenges we encountered, and choices we made! (Note from here on we will just refer to it as the “Generator”, or “code agent” when talking about the backend)&lt;/p&gt;

&lt;p&gt;Also, all the code behind the Generator is open source: &lt;a href="https://github.com/wasp-lang/wasp/blob/737ab428edf38f245cd9f8db60b637b723352e55/wasp-ai" rel="noopener noreferrer"&gt;web app&lt;/a&gt;, &lt;a href="https://github.com/wasp-lang/wasp/blob/737ab428edf38f245cd9f8db60b637b723352e55/waspc/src/Wasp/AI" rel="noopener noreferrer"&gt;GPT code agent&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Support us! 🙏⭐️
&lt;/h2&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0y317fbspta55etehxbd.gif" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0y317fbspta55etehxbd.gif" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you wish to express your support for what we are doing, consider &lt;a href="https://github.com/wasp-lang/wasp" rel="noopener noreferrer"&gt;giving us a star on Github&lt;/a&gt;! Everything we do at Wasp is open source, and your support motivates us and helps us to keep making web app development easier and with less boilerplate.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Funu0vq8mtvrbfg8vzaqh.gif" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Funu0vq8mtvrbfg8vzaqh.gif" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How well does it work 🤔?
&lt;/h2&gt;

&lt;p&gt;First, let’s quickly explain what we ended up with and how it performs.&lt;/p&gt;

&lt;p&gt;Input into our Generator is the app name, app description (free form text), and a couple of simple options such as primary app color, temperature, auth method, and GPT model to use.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4360q9ecypmq1tjfb13f.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4360q9ecypmq1tjfb13f.png" alt="Input for generating a Todo app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As an output, Generator spits out the whole JS codebase of a working full-stack web app: frontend, backend, and database. Frontend is React + Tailwind, the backend is NodeJS with Express, and for working with the database we used Prisma. This is all connected together with the Wasp framework.&lt;/p&gt;

&lt;p&gt;You can see an example of generated codebase here: &lt;a href="https://magic-app-generator.wasp-lang.dev/result/07ed440a-3155-4969-b3f5-2031fb1f622f" rel="noopener noreferrer"&gt;https://magic-app-generator.wasp-lang.dev/result/07ed440a-3155-4969-b3f5-2031fb1f622f&lt;/a&gt; .&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjr2felbto78oqsasxr5a.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjr2felbto78oqsasxr5a.png" alt="Result of generating a Todo app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Generator does its best to produce code that works out of the box → you can download it to your machine and run it. For simpler apps, such as &lt;a href="https://magic-app-generator.wasp-lang.dev/result/07ed440a-3155-4969-b3f5-2031fb1f622f" rel="noopener noreferrer"&gt;TodoApp&lt;/a&gt; or &lt;a href="https://magic-app-generator.wasp-lang.dev/result/3bb5dca2-f134-4f96-89d6-0812deab6e0c" rel="noopener noreferrer"&gt;MyPlants&lt;/a&gt;, it often generates code with no mistakes, and you can run them out of the box.&lt;/p&gt;

&lt;p&gt;Here is what a generated TodoApp looks like:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F73ba77ualycnfx39tx4o.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F73ba77ualycnfx39tx4o.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For a bit more complex apps, like a blog with posts and comments, it still generates a reasonable codebase but there are some mistakes to be expected here and there. For even more complex apps, it usually doesn’t follow up completely, but stops at some level of complexity and fills in the rest with TODOs or omits functionality, so it is kind of like a simplified model of what was asked for. Overall, it is optimized for producing CRUD business web apps.&lt;/p&gt;

&lt;p&gt;This makes it a great tool for kick-starting your next web app project with a solid prototype, or to even generate working, simple apps on the fly!&lt;/p&gt;

&lt;h2&gt;
  
  
  How does it work ⚙️?
&lt;/h2&gt;

&lt;p&gt;When we set out to build the Generator, we gave ourselves the following goals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;we must be able to build it in a couple of weeks&lt;/li&gt;
&lt;li&gt;it has to be relatively easy to maintain in the future&lt;/li&gt;
&lt;li&gt;it needs to generate the app quickly and cheaply (a couple of minutes, &amp;lt; $1)&lt;/li&gt;
&lt;li&gt;generated apps should have as few mistakes as possible&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Therefore, to keep it simple, we don’t do any LLM-level engineering or fine-tuning, instead, &lt;strong&gt;we just use OpenAI API (specifically GPT3.5 and GPT4) to generate different parts of the app while giving it the right context at every moment (pieces of docs, examples, guidelines, …)&lt;/strong&gt;. To ensure the coherence and quality of the generated app, we don’t give our code agent too much freedom but instead heavily guide it, step by step, through generating the app.&lt;/p&gt;

&lt;p&gt;As &lt;strong&gt;step zero&lt;/strong&gt;, we generate some code files deterministically, without GPT, just based on the options that the user chose (primary color, auth method): those include some config files for the project, some basic global CSS, and some auth logic. You can see this logic here (we call those “skeleton” files): &lt;a href="https://github.com/wasp-lang/wasp/blob/737ab428edf38f245cd9f8db60b637b723352e55/waspc/src/Wasp/AI/GenerateNewProject/Skeleton.hs" rel="noopener noreferrer"&gt;code on Github&lt;/a&gt; .&lt;/p&gt;

&lt;p&gt;Then, the code agent takes over!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The code agent does its work in 3 main phases&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Planning 📝&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Generating 🏭&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fixing 🔧&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Since GPT4 is quite slower and significantly more expensive than GPT3.5 (also has a lower rate limit regarding the number of tokens per minute, and also the number of requests per minute), we use GPT4 only for the planning, since that is the crucial step, and then after that, we use GPT3.5 for the rest.&lt;/p&gt;

&lt;p&gt;As for &lt;strong&gt;cost per app&lt;/strong&gt; 💸: one app typically consumes from 25k to 60k tokens, which comes to about &lt;strong&gt;$0.1 to $0.2&lt;/strong&gt; per app, when we use a mix of GPT4 and GPT3.5. If we run it just with GPT4, then the cost is 10x, which is from &lt;strong&gt;$1 to $2&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  🎶 Intermezzo: short explanation of OpenAI Chat Completions API
&lt;/h3&gt;

&lt;p&gt;OpenAI API offers different services, but we used only one of them: “chat completions”.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API itself is actually very simple: you send over a conversation, and you get a response from the GPT.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The conversation is just a list of messages, where each message has content and a role, where the role specifies who “said” that content → was it “user” (you), or “assistant” (GPT).&lt;/p&gt;

&lt;p&gt;The important thing to note is that &lt;strong&gt;there is no concept of state/memory: every API call is completely standalone&lt;/strong&gt;, and the only thing that GPT knows about is the conversation you provide it with at that moment!&lt;/p&gt;

&lt;p&gt;If you are wondering how ChatGPT (the web app that uses GPT in the background) works with no memory → well, each time you write a message, the whole conversation so far is resent again! There are some additional smart mechanisms in play here, but that is really it at its core.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://platform.openai.com/docs/guides/gpt/chat-completions-api" rel="noopener noreferrer"&gt;Official guide&lt;/a&gt;, &lt;a href="https://platform.openai.com/docs/api-reference/chat/create" rel="noopener noreferrer"&gt;official API reference&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step #1: Planning 📝
&lt;/h3&gt;

&lt;p&gt;A Wasp app consists of Entities (Prisma data models), Operations (NodeJS Queries and Actions), and Pages (React).&lt;/p&gt;

&lt;p&gt;Once given an app description and title, &lt;strong&gt;the code agent first generates a Plan&lt;/strong&gt;: it is a list of Entities, Operations (Queries and Actions), and Pages that comprise the app. So kind of like an initial draft of the app. It doesn’t generate the code yet → instead, it comes up with their names and some other details, including a short description of what they should behave like.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp7bx7gj4b2z3zjcfkqhd.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp7bx7gj4b2z3zjcfkqhd.png" alt="Planning step logs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is done via a single API request toward GPT, where the prompt consists of the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Short &lt;strong&gt;info about the Wasp&lt;/strong&gt; framework + an example of some Wasp code.&lt;/li&gt;
&lt;li&gt;We &lt;strong&gt;explain that we want to generate the Plan&lt;/strong&gt;, explain what it is, and how it is represented as &lt;strong&gt;JSON&lt;/strong&gt;, by describing its schema.&lt;/li&gt;
&lt;li&gt;We provide some &lt;strong&gt;examples of the Plan&lt;/strong&gt;, represented as JSON.&lt;/li&gt;
&lt;li&gt;Some &lt;strong&gt;rules and guidelines&lt;/strong&gt; we want it to follow (e.g. “plan should have at least 1 page”, “make sure to generate a User entity”).&lt;/li&gt;
&lt;li&gt;Instructions to return the Plan only as a &lt;strong&gt;valid JSON response&lt;/strong&gt;, and no other text.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;App name and description&lt;/strong&gt; (as provided by the user).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can see how we generate such a prompt in the code &lt;a href="https://github.com/wasp-lang/wasp/blob/737ab428edf38f245cd9f8db60b637b723352e55/waspc/src/Wasp/AI/GenerateNewProject/Plan.hs#L61" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Also, here is a &lt;a href="https://gist.github.com/Martinsos/6924f3c56351426f92b468b1545819de" rel="noopener noreferrer"&gt;link to a gist&lt;/a&gt; with an actual instance of this prompt for a TodoApp.&lt;/p&gt;

&lt;p&gt;GPT then responds with a JSON (hopefully), that we parse, and we have ourselves a Plan! We will use this Plan in the following steps, to drive our generation of other parts of the app. Note that GPT sometimes adds text to the JSON response or returns invalid JSON, so we built in some simple approaches to overcome these issues, which we explain in detail later.&lt;/p&gt;

&lt;h3&gt;
  
  
  🎶 Intermezzo: Common prompt design
&lt;/h3&gt;

&lt;p&gt;The prompt design we just described above for generating a Plan is actually very similar for other steps (e.g. the Generation and Fixing steps along with their respective sub-steps), so let’s cover those commonalities.&lt;/p&gt;

&lt;p&gt;All of the prompts we use more or less adhere to the same basic structure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;General context&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Short info about what Wasp framework is.&lt;/li&gt;
&lt;li&gt;Doc snippets (with code examples if needed) about whatever we are generating right now (e.g. examples of NodeJS code, or examples of React code).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Project context&lt;/strong&gt;: stuff we generated in the previous steps that is relevant to the current step.&lt;/li&gt;

&lt;li&gt;Instructions on &lt;strong&gt;what we want to generate right now&lt;/strong&gt; + JSON schema for it + example of such JSON response.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Rules and guidelines&lt;/strong&gt;: this is a good place to warn it about common mistakes it makes, or give it some additional advice, and emphasize what needs to happen and what must not happen.&lt;/li&gt;

&lt;li&gt;Instructions to &lt;strong&gt;respond only with a valid JSON&lt;/strong&gt;, and no other text.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Original user prompt&lt;/strong&gt;: app name and description (as provided by the user).&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;We put the original user prompt at the end because then we can tell GPT in the system message after it sees the start of the original user prompt (we have a special header for it), that it needs to treat everything after it as an app description and not as instructions on what to do → this way we attempt to &lt;strong&gt;defend from the potential prompt injection&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step #2: Generating 🏭
&lt;/h3&gt;

&lt;p&gt;After producing the Plan, Generator goes step by step through the Plan and asks GPT to generate each web app piece, while providing it with docs, examples, and guidelines. Each time a web app piece is generated, Generator fits it into the whole app. This is where most of our work comes in: &lt;strong&gt;equipping GPT with the right information at the right moment&lt;/strong&gt;.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi1b0ydopsjtllbh7uy6k.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi1b0ydopsjtllbh7uy6k.png" alt="Generation step logs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In our case, we do it for all the Operations in the Plan (Actions and Queries: NodeJs code), and also for all the Pages in the Plan (React code), with one prompt for each. So if we have 2 queries, 3 actions, and 2 pages, that will be 2+3+2 = 7 GPT prompts/requests. Prompts are designed as explained previously.&lt;/p&gt;

&lt;p&gt;Code on Github: &lt;a href="https://github.com/wasp-lang/wasp/blob/737ab428edf38f245cd9f8db60b637b723352e55/waspc/src/Wasp/AI/GenerateNewProject/Operation.hs" rel="noopener noreferrer"&gt;generating an Operation&lt;/a&gt;, &lt;a href="https://www.notion.so/Postavi-da-mijenjamo-verziju-cabala-stalno-https-github-com-wasp-lang-wasp-issues-892-fb4f0edb0a024951ad236f82030008a5?pvs=21" rel="noopener noreferrer"&gt;generating a Page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When generating Operations, we provide GPT with the info about the previously generated Entities, while when generating Pages, we provide GPT with the info about previously generated Entities and Operations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step #3: Fixing 🔧
&lt;/h3&gt;

&lt;p&gt;Finally, the Generator tries its best to fix any mistakes that GPT might have introduced previously. &lt;strong&gt;GPT loves fixing stuff it previously generated&lt;/strong&gt; → if you first ask it to generate some code, and then just tell it to fix it, it will often improve it!&lt;/p&gt;

&lt;p&gt;To enhance this process further, we don’t just ask it to fix previous code, but also provide it with instructions on what to keep an eye out for, like common types of mistakes that we noticed it often does, and also point it to any specific mistakes we were able to detect on our own.&lt;/p&gt;

&lt;p&gt;Regarding detecting mistakes to report to GPT, &lt;strong&gt;ideally, you would have a full REPL going on&lt;/strong&gt; → that means running the generated code through an interpreter/compiler, then sending it for repairs, and so on until all is fixed.&lt;/p&gt;

&lt;p&gt;In our case, running the whole project through the TypeScript compiler was not feasible for us with the time limits we put on ourselves, but &lt;strong&gt;we used some simpler static analysis tools&lt;/strong&gt; like Wasp’s compiler (for the .wasp file) and &lt;code&gt;prisma format&lt;/code&gt; for Prisma model schemas, and sent those to GPT to fix them. We also wrote some &lt;strong&gt;simple heuristics of our own that are able to detect some of the common mistakes&lt;/strong&gt;.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu2xt55hvl3n0k4129ha9.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu2xt55hvl3n0k4129ha9.png" alt="Fixing step logs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/wasp-lang/wasp/blob/737ab428edf38f245cd9f8db60b637b723352e55/waspc/src/Wasp/AI/GenerateNewProject/PageComponentFile.hs#L122" rel="noopener noreferrer"&gt;Our code (&amp;amp; prompt) for fixing a Page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/wasp-lang/wasp/blob/737ab428edf38f245cd9f8db60b637b723352e55/waspc/src/Wasp/AI/GenerateNewProject/OperationsJsFile.hs#L26" rel="noopener noreferrer"&gt;Our code (&amp;amp; prompt) for fixing Operations&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the prompt, we would usually repeat the same guidelines we provided previously in the Generation step, while also adding a couple of additional pointers to common mistakes, and that usually helps, it fixes stuff it missed before. But, often not everything, instead something will still get through. &lt;strong&gt;Some things we just couldn’t get it to fix consistently&lt;/strong&gt;, for example, Wasp-specific JS imports, no matter how much we emphasized what it needed to do with them, it would just keep messing them up. Even GPT4 wasn’t perfect in this situation. For such situations, when possible, we ended up writing &lt;strong&gt;our own heuristics that would fix those mistakes&lt;/strong&gt; (&lt;a href="https://github.com/wasp-lang/wasp/blob/737ab428edf38f245cd9f8db60b637b723352e55/waspc/src/Wasp/AI/GenerateNewProject/PageComponentFile.hs#L49" rel="noopener noreferrer"&gt;fixing JS imports&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Things we tried/learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Explanations 💬
&lt;/h3&gt;

&lt;p&gt;We tried telling GPT to explain what it did while fixing mistakes: which mistakes it will fix, and which mistakes it fixed, since we read that that can help, but &lt;strong&gt;we didn’t see visible improvement in its performance&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing 🧪
&lt;/h3&gt;

&lt;p&gt;Testing the performance of your code agent is hard.&lt;/p&gt;

&lt;p&gt;In our case, it takes a couple of minutes for our code agent to generate a new app, and you need to run tests directly with the OpenAI API. Also, &lt;strong&gt;since results are non-deterministic, it can be pretty hard to say if output was affected by the changes&lt;/strong&gt; you did or not.&lt;/p&gt;

&lt;p&gt;Finally, evaluating the output itself can be hard (especially in our case when it is a whole full-stack web app).&lt;/p&gt;

&lt;p&gt;Ideally, we would have set up a system where we can run only parts of the whole generation process, and we could automatically run a specific part a number of times for each of different sets of parameters (which would include different prompts, but also parameters like type of model (gpt4 vs gpt3.5), temperature and similar), in order to compare performance for each of those parameter sets.&lt;/p&gt;

&lt;p&gt;Evaluation performance would also ideally be automated, e.g. we would count the mistakes during compilation and/or evaluate the quality of app design → but this is also quite hard.&lt;/p&gt;

&lt;p&gt;We, unfortunately, didn’t have time to set up such a system, so &lt;strong&gt;we were mostly doing testing manually, which is quite subjective and vulnerable to randomness&lt;/strong&gt;, and is effective only for changes that have quite a big impact, while you can’t really detect those that are minor optimizations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Context vs smarts 🧠
&lt;/h3&gt;

&lt;p&gt;When we started working on the Generator, we thought the size of GPT’s context would be the main issue. However, we didn’t have any issues with context at the end → most of what we wanted to specify would fit into 2k to max 4k tokens, while GPT3.5 has context up to 16k!&lt;/p&gt;

&lt;p&gt;Instead, &lt;strong&gt;we had bigger problems with its “smarts”&lt;/strong&gt; → meaning that GPT would not follow the rules we very explicitly told it to follow, or would do things we explicitly forbid it from doing. GPT4 proved to be better at following rules than GPT3.5, but even GPT4 would keep doing some mistakes over and over and forgetting about specific rules (even though there was more than enough context). The “fixing” step did help with this: we would repeat the rules there and GPT would pick up more of them, but often still not all of them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handling JSON as a response 📋
&lt;/h3&gt;

&lt;p&gt;As mentioned earlier in this article, in all our interactions with GPT, we always ask it to return the response as JSON, for which we specify the schema and give some examples.&lt;/p&gt;

&lt;p&gt;However, GPT still doesn’t always follow that rule, and will sometimes add some text around the JSON, or will make a mistake in formatting JSON.&lt;/p&gt;

&lt;p&gt;The way we handled this is with two simple fixes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Upon receiving JSON, we would remove all the characters from the start until we hit &lt;code&gt;{&lt;/code&gt;, and also all chars from the end until we hit &lt;code&gt;}&lt;/code&gt;. Simple heuristic, but it works very well for removing redundant text around the JSON in practice since GPT will normally not have any &lt;code&gt;{&lt;/code&gt; or &lt;code&gt;}&lt;/code&gt; in that text.&lt;/li&gt;
&lt;li&gt;If we fail to parse JSON, we send it again for repairs, to GPT. We include the previous prompt and its last answer (that contains invalid JSON) and add instructions to fix it + JSON parse errors we got. We repeat this a couple of times until it gets it right (or until we give up).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In practice, &lt;strong&gt;these two methods took care of invalid JSON in 99% of the cases for us&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;NOTE: While we were implementing our code agent, OpenAI released new functionality for GPT, “functions”, which is basically a mechanism to have GPT respond with a structured JSON, following the schema of your description. So it would likely make more sense to do this with “functions”, but we already had this working well so we just stuck with it.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Handling interruptions in the service 🚧
&lt;/h3&gt;

&lt;p&gt;We were calling OpenAI API directly, so we noticed quickly that often it would return 503 - service unavailable - especially during peak hours (e.g. 3 pm CET).&lt;/p&gt;

&lt;p&gt;Therefore, it is recommended to have some kind of retry mechanism, ideally with exponential backoff, that makes your code agent redundant to such random interruptions in the service, and also to potential rate limiting. &lt;strong&gt;We went with the retry mechanism with exponential backoff and it worked great&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Temperature 🌡️
&lt;/h3&gt;

&lt;p&gt;Temperature determines how creative GPT is, but the more creative it gets, the less “stable” it is. It hallucinates more and also has a harder time following rules.&lt;br&gt;
A temperature is a number from 0 to 2, with a default value of 1.&lt;/p&gt;

&lt;p&gt;We experimented with different values and found the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;≥ 1.5&lt;/strong&gt; would every so and so start giving quite silly results with random strings in it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;≥ 1.0, &amp;lt; 1.5&lt;/strong&gt; was okish but was introducing a bit too many mistakes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;≥ 0.7, &amp;lt; 1.0&lt;/strong&gt; was optimal → creative enough, while still not having many mistakes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;≤ 0.7&lt;/strong&gt; seemed to perform similarly to a bit higher values, but with a bit less creativity maybe.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That said, I don’t think we tested values below 0.7 enough, and that is something we could certainly work on more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We ended up using 0.7 as our default value, except for prompts that do fixing, for those we used a lower value of 0.5&lt;/strong&gt; because it seemed like GPT was changing stuff too much while fixing at 0.7 (being too creative). Our logic was: let it be creative when writing the first version of the code, then have it be a bit more conventional while fixing it. Again, we haven’t tested all this enough, so this is certainly something I would like us to explore more. &lt;/p&gt;

&lt;h2&gt;
  
  
  Future 🔮
&lt;/h2&gt;

&lt;p&gt;While we ended up being impressed with the performance of what we managed to build in such a short time, we were also left wanting to try so many different ideas on how to improve it further. There are many avenues left to be explored in this ecosystem that is developing so rapidly, that it is hard to reach the point where you feel like you explored all the options and found the optimal solution.&lt;/p&gt;

&lt;p&gt;Some of the ideas that would be exciting to try in the future:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;We put quite a few limitations regarding the code that our code agent generates, to make sure it works well enough: we don’t allow it to create helper files, to include npm dependencies, no TypeScript, no advanced Wasp features, … . &lt;strong&gt;We would love to lift the limitations&lt;/strong&gt;, therefore allowing the creation of more complex and powerful apps.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Instead of our code agent doing everything in one shot, we could &lt;strong&gt;allow the user to interact with it&lt;/strong&gt; after the first version of the app is generated: to provide additional prompts, for example, to fix something, to add some feature to the app, to do something differently, …. The hardest thing here would be figuring out which context to provide to the GPT at which moment and designing the experience appropriately, but I am certain it is doable, and it would take the Generator to the next level of usability.&lt;br&gt;
Another option is to allow intervention in between initial generation steps → for example, after the plan is generated, to allow the user to adjust it by providing additional instructions to the GPT.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Find an open-source &lt;strong&gt;LLM that fits the purpose and fine-tune / pre-train it for our purpose&lt;/strong&gt;. If we could teach it more about Wasp and the technologies we use, so we don’t have to include it in every prompt, we could save quite some context + have the LLM be more focused on the rules and guidelines we are specifying in the prompt. We could also host it ourselves and have more control over the costs and rate limits.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Take a &lt;strong&gt;different approach to the code agent: let it be more free&lt;/strong&gt;. Instead of guiding it so carefully, we could teach it about all the different things it is allowed to ask for (ask for docs, ask for examples, ask to generate a certain piece of the app, ask to see a certain already generated piece of the app, …) and would let it guide itself more freely. It could constantly generate a plan, execute it, update the plan, and so on until it reaches the state of equilibrium. This approach potentially promises more flexibility and would likely be able to generate apps of greater complexity, but it also requires quite more tokens and a powerful LLM to drive it → I believe this approach will become more feasible as LLMs become more capable.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Support us! ⭐️
&lt;/h2&gt;

&lt;p&gt;If you wish to express your support for what we are doing, consider giving us a &lt;a href="https://github.com/wasp-lang/wasp" rel="noopener noreferrer"&gt;star on Github&lt;/a&gt;! Everything we do at Wasp is open source, and your support motivates us and helps us to keep making web app development easier and with less boilerplate.&lt;/p&gt;

&lt;p&gt;Also, if you have any ideas on how we could improve our code agent, or maybe we can help you somehow -&amp;gt; feel free to join our &lt;a href="https://discord.gg/rzdnErX" rel="noopener noreferrer"&gt;Discord server&lt;/a&gt; and let's chat!&lt;/p&gt;

</description>
      <category>chatgpt</category>
      <category>react</category>
      <category>webdev</category>
      <category>ai</category>
    </item>
    <item>
      <title>Wasp Beta brings major IDE improvements</title>
      <dc:creator>Martin Šošić</dc:creator>
      <pubDate>Fri, 02 Dec 2022 14:13:09 +0000</pubDate>
      <link>https://dev.to/wasp/wasp-beta-brings-major-ide-improvements-2h7a</link>
      <guid>https://dev.to/wasp/wasp-beta-brings-major-ide-improvements-2h7a</guid>
      <description>&lt;p&gt;With the Beta release (0.7), Wasp brings its IDE game to a whole new level!&lt;/p&gt;

&lt;p&gt;So far Wasp didn’t have much beyond basic syntax highlighting in VSCode, but now it has:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Wasp language server&lt;/strong&gt;, that brings the following to your .wasp files:

&lt;ul&gt;
&lt;li&gt;live error reporting in your editor&lt;/li&gt;
&lt;li&gt;autocompletion (basic for now)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VSCode Wasp language extension&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;snippets (for page, query, action, entity)&lt;/li&gt;
&lt;li&gt;improved syntax highlighting for .wasp files&lt;/li&gt;
&lt;li&gt;integration with the above-mentioned language server&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Support for popular &lt;strong&gt;IDEs to fully support Javascript and Typescript files&lt;/strong&gt; in the Wasp project.&lt;/li&gt;
&lt;/ol&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8pscj5sqhe0mmdb7xn0r.gif" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8pscj5sqhe0mmdb7xn0r.gif"&gt;&lt;/a&gt;Wasp IDE support in action in VSCode: syntax highlighting, snippets, live error reporting.&lt;/p&gt;

&lt;p&gt;Wasp IDE support in action in VSCode: syntax highlighting, snippets, live error reporting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wasp Language Server
&lt;/h2&gt;

&lt;p&gt;Wasp Language Server (WLS) is the “brain” behind smart IDE features like live error reporting and autocompletion - so if it seems like IDE actually understands your code to some degree, well that is the language server!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;TIP&lt;br&gt;
For curious, check out the source code of WLS on Github: &lt;a href="https://github.com/wasp-lang/wasp/tree/main/waspc/waspls/src/Wasp/LSP" rel="noopener noreferrer"&gt;https://github.com/wasp-lang/wasp/tree/main/waspc/waspls/src/Wasp/LSP&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;h3&gt;
  
  
  Live error/warning reporting
&lt;/h3&gt;

&lt;p&gt;WLS compiles wasp code for you as you work on it and shows you any errors directly in the editor, via red squiggly lines.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpdb3f48lsh96v000uooe.gif" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpdb3f48lsh96v000uooe.gif" alt="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pdb3f48lsh96v000uooe.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Autocompletion
&lt;/h3&gt;

&lt;p&gt;WLS understands at which part of code you are right now and offers appropriate completions for it.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy0rmflyhdl9mq3yw38b9.gif" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy0rmflyhdl9mq3yw38b9.gif" alt="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y0rmflyhdl9mq3yw38b9.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NOTE&lt;br&gt;
Right now WLS is pretty naive here, and mostly focuses on offering available expressions when it realizes you need an expression. This is helpful but just a start, and it will get much smarter in future versions!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Bit of history: why are Language Servers cool
&lt;/h2&gt;

&lt;p&gt;Years ago, there was no standardized way to write something like Language Server for your language, instead, each language was doing something of its own, and then each editor/IDE would also implement its own layer of logic for using it, and that was a loooot of work that needed to be done for each editor!&lt;/p&gt;

&lt;p&gt;Luckily, Microsoft then came up with &lt;a href="https://microsoft.github.io/language-server-protocol/" rel="noopener noreferrer"&gt;Language Server Protocol&lt;/a&gt; - a standardized way of communicating between the “smart” part, implemented by language creators, and the editor/IDE part (language extension) that is using it. This enabled each editor to implement this logic for interacting with language servers only once, and then it can be used for any language server!&lt;/p&gt;

&lt;p&gt;This is great for us, language creators, because it means that once we implement a language server for our language, most of the work is done, and the work we need to do per each editor is manageable.&lt;/p&gt;

&lt;p&gt;Right now WLS is used only by the &lt;a href="https://marketplace.visualstudio.com/items?itemName=wasp-lang.wasp" rel="noopener noreferrer"&gt;VSCode Wasp language extension&lt;/a&gt;, but thanks to the nature of the Language Server Protocol, it should be relatively easy to add support for other editors too! Check this &lt;a href="https://github.com/wasp-lang/wasp/issues/864" rel="noopener noreferrer"&gt;GH issue&lt;/a&gt; if you are interested in helping.&lt;/p&gt;

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

&lt;p&gt;The best thing: there is nothing you, as a Wasp user, have to do to set up WLS! It already comes bundled with your installation of &lt;code&gt;wasp&lt;/code&gt; → so if you can run &lt;code&gt;wasp&lt;/code&gt; projects on your machine, you already have WLS, and it is always of the correct version needed for your current &lt;code&gt;wasp&lt;/code&gt; installation. The only thing you need to ensure is you have &lt;code&gt;wasp&lt;/code&gt; version ≥ 0.6, and a relatively fresh VSCode Wasp language extension.&lt;/p&gt;

&lt;p&gt;An easy way to check that your version of &lt;code&gt;wasp&lt;/code&gt; has WLS packaged into it is to run it and look at its usage instructions: it should mention &lt;code&gt;waspls&lt;/code&gt; as one of the commands.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fegdxhxjnuuehr8iqv3ti.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fegdxhxjnuuehr8iqv3ti.png" alt="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/egdxhxjnuuehr8iqv3ti.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wasp VSCode extension
&lt;/h2&gt;

&lt;p&gt;If we would call Wasp Language Server (WLS) the “backend”, then &lt;a href="https://marketplace.visualstudio.com/items?itemName=wasp-lang.wasp" rel="noopener noreferrer"&gt;VSCode Wasp language extension&lt;/a&gt; would be “frontend” → it takes care of everything to ensure you have a nice experience working with Wasp in VSCode, while delegating the hardest work to the WLS.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc6voeggiaxsmzsupk752.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc6voeggiaxsmzsupk752.png" alt="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c6voeggiaxsmzsupk752.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;TIP&lt;br&gt;
For curious, you can check out its source code here, core of it is just one file: &lt;a href="https://github.com/wasp-lang/vscode-wasp/blob/main/src/extension.ts" rel="noopener noreferrer"&gt;https://github.com/wasp-lang/vscode-wasp/blob/main/src/extension.ts&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;h3&gt;
  
  
  Syntax highlighting
&lt;/h3&gt;

&lt;p&gt;Nothing unexpected here: it recognizes different parts of Wasp syntax, like type, value, identifier, comment, string, … and colors them appropriately.&lt;/p&gt;

&lt;p&gt;If you are curious how is this implemented, check &lt;a href="https://github.com/wasp-lang/vscode-wasp/blob/main/syntaxes/wasp.tmLanguage.yaml" rel="noopener noreferrer"&gt;https://github.com/wasp-lang/vscode-wasp/blob/main/syntaxes/wasp.tmLanguage.yaml&lt;/a&gt; → the whole syntax of Wasp is described via this “mysterious” old TextMate format, since that is the way to do it in VSCode.&lt;/p&gt;

&lt;h3&gt;
  
  
  Snippets
&lt;/h3&gt;

&lt;p&gt;Wasp allows you to quickly generate a snippet of code for a new &lt;code&gt;page&lt;/code&gt;, &lt;code&gt;query&lt;/code&gt;, &lt;code&gt;action&lt;/code&gt;, or &lt;code&gt;entity&lt;/code&gt;!&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2x9c9vwtsmonf4x7lbrj.gif" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2x9c9vwtsmonf4x7lbrj.gif" alt="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2x9c9vwtsmonf4x7lbrj.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check out our snippet definitions here: &lt;a href="https://github.com/wasp-lang/vscode-wasp/blob/main/snippets/wasp.json" rel="noopener noreferrer"&gt;https://github.com/wasp-lang/vscode-wasp/blob/main/snippets/wasp.json&lt;/a&gt; . It is actually really easy, in VSCode, to define them and add new ones.&lt;/p&gt;

&lt;h3&gt;
  
  
  Live error reporting + autocompletion
&lt;/h3&gt;

&lt;p&gt;This is done by delegating the work to WLS, as described above!&lt;/p&gt;

&lt;h2&gt;
  
  
  IDE support for Javascript / Typescript in Wasp project
&lt;/h2&gt;

&lt;p&gt;Due to how unique Wasp is in its approach, getting an IDE to provide all the usual features for Javascript / Typescript wasn’t completely working, and instead, the IDE would get somewhat confused with the context in which files are and would for example not be able to offer “go to definition” for some values, or would not know how to follow the import path.&lt;/p&gt;

&lt;p&gt;With Wasp Beta this is now resolved! We resolved this by somewhat changing the structure of the Wasp project and also adding tsconfig.json files that provide IDE with the information needed to correctly analyze the JS/TS source files.&lt;/p&gt;

&lt;p&gt;To learn more about Typescript support in Wasp Beta, check &lt;a href="https://wasp-lang.dev/blog/2022/11/29/typescript-feature-announcement" rel="noopener noreferrer"&gt;this blog post&lt;/a&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  What does the future hold?
&lt;/h2&gt;

&lt;p&gt;While Wasp Beta greatly improved IDE support for Wasp, there are still quite a few things we want to improve on:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Smarter autocompletion via WLS.

&lt;ul&gt;
&lt;li&gt;Right now it suggests any expression when you need an expression. In the future, we want it to know exactly what is the type of needed expression, and suggest only expressions of that type! So if I am in route ... { to:  }, then I want to see only pages among the suggested completions, not queries or actions or something else.&lt;/li&gt;
&lt;li&gt;Further, we would also like it to autocomplete on dictionary fields → so if I am in route ... {  }, it should offer me path and to as completions, as those are only valid fields in the route dictionary.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Extensions for other editors besides VSCode. Now that we have Wasp Language Server, these shouldn’t be too hard to implement! This is also a great task for potential contributors: check this &lt;a href="https://github.com/wasp-lang/wasp/issues/864" rel="noopener noreferrer"&gt;GH issue&lt;/a&gt; if you are interested.&lt;/li&gt;
&lt;li&gt;Implement Wasp code formatter. We could make it a part of WLS, and then have the editor extension call it on save.&lt;/li&gt;
&lt;li&gt;Improve support for PSL (Prisma Schema Language) in .wasp files.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If any of these sound interesting, feel free to join us on our &lt;a href="https://github.com/wasp-lang/wasp" rel="noopener noreferrer"&gt;Github&lt;/a&gt;, or join the discussion on &lt;a href="https://discord.gg/rzdnErX" rel="noopener noreferrer"&gt;Discord&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>vscode</category>
    </item>
    <item>
      <title>Permissions (access control) in web apps</title>
      <dc:creator>Martin Šošić</dc:creator>
      <pubDate>Wed, 30 Nov 2022 19:47:37 +0000</pubDate>
      <link>https://dev.to/wasp/permissions-access-control-in-web-apps-j6b</link>
      <guid>https://dev.to/wasp/permissions-access-control-in-web-apps-j6b</guid>
      <description>&lt;p&gt;At Wasp, we are working on a config language / DSL for building web apps that integrates with React &amp;amp; Node.js.&lt;br&gt;&lt;br&gt;
This requires us to deeply understand different parts of what constitutes a web app, in order to be able to model them in our DSL.&lt;/p&gt;

&lt;p&gt;Recently our focus was on access control, and I decided to capture the learnings in this blog post, to help others quickly get up to speed on how to do access control in web apps.&lt;br&gt;&lt;br&gt;
So, if you are new to access control in web apps, or have been doing it for some time but want to get a better idea of standard practices, read along!&lt;/p&gt;

&lt;h4&gt;
  
  
  Quick overview of what this blog post covers:
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Permissions, yay! Wait, what are they though? (quick overview of basic terms)&lt;/li&gt;
&lt;li&gt;Where do we check permissions in a web app: frontend vs backend vs db&lt;/li&gt;
&lt;li&gt;Common approaches (RBAC, ABAC, …)&lt;/li&gt;
&lt;li&gt;OWASP recommendations&lt;/li&gt;
&lt;li&gt;Implementing access control in practice&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  1. Permissions, yay! Wait, what are they though?
&lt;/h2&gt;

&lt;p&gt;Unless your web app is mostly about static content or is a form of art, it will likely have a notion of users and user accounts.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6zbnj2o1v478wqzohy1b.gif" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6zbnj2o1v478wqzohy1b.gif"&gt;&lt;/a&gt;&lt;br&gt;This dolphin doesn't need users
  &lt;/p&gt;

&lt;p&gt;In such a case, you will need to know which user has permissions to do what -&amp;gt; who can access which resources, and who can execute which operations.&lt;/p&gt;

&lt;p&gt;Some common examples of permissions in action:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User can access only their own user account.&lt;/li&gt;
&lt;li&gt;If the user is an admin, they can ban other users’ accounts.&lt;/li&gt;
&lt;li&gt;User can read other users’ articles, but can't modify them.&lt;/li&gt;
&lt;li&gt;The title and description of the article behind the paywall are publicly accessible, but the content is not.&lt;/li&gt;
&lt;li&gt;User can send an email invitation to up to 10 future users per day.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Aha, you mean access control! Sorry, authorization! Hmm, authentication?
&lt;/h3&gt;

&lt;p&gt;There are different terms out there (authentication, authorization, access control, permissions) that are often confused for each other, so let's quickly clarify what each one of them stands for.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffcozzwdppxxyupf4p9jc.jpg" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffcozzwdppxxyupf4p9jc.jpg"&gt;&lt;/a&gt;&lt;br&gt;They all look the same!
  &lt;/p&gt;

&lt;h3&gt;
  
  
  1) Authentication (or as cool kids would say: authN)
&lt;/h3&gt;

&lt;p&gt;Act of verifying the user's identity.&lt;br&gt;&lt;br&gt;
Answers the question "&lt;strong&gt;Who are they?&lt;/strong&gt;"&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A: Knock Knock&lt;br&gt;&lt;br&gt;
B: Who is it?&lt;br&gt;&lt;br&gt;
A: User!&lt;br&gt;&lt;br&gt;
B: User who?&lt;br&gt;&lt;br&gt;
A: Authorization: Basic myusername:mypassword&lt;/p&gt;

&lt;p&gt;-&amp;gt; yes, you noticed correctly, this is an example of common authentication method but HTTP header is called "Authorization"! Weird! But it all makes sense if you squint hard enough: (&lt;a href="https://stackoverflow.com/questions/30062024/why-is-the-http-header-for-authentication-called-authorization" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/30062024/why-is-the-http-header-for-authentication-called-authorization&lt;/a&gt;).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2) Authorization (or as cool kids would say: authZ)
&lt;/h3&gt;

&lt;p&gt;Process of determining access rights that user has.&lt;br&gt;&lt;br&gt;
Answers the question "&lt;strong&gt;Are they allowed to do this?&lt;/strong&gt;"&lt;/p&gt;

&lt;p&gt;Normally you will want the user to be authenticated at this point already, so you have information about them based on which you will decide if they are allowed to do something.&lt;/p&gt;

&lt;h3&gt;
  
  
  3) Access Control
&lt;/h3&gt;

&lt;p&gt;A higher-level term (compared to authN and authZ) that encompasses the whole process of ensuring that only allowed parties can access specific resources (controlling access to resources -&amp;gt; access control).&lt;/p&gt;

&lt;p&gt;Often consists of authentication and/or authorization as its steps.&lt;/p&gt;

&lt;p&gt;Also often used in the wild interchangeably with just "authorization".&lt;br&gt;
Reference (OWASP): &lt;a href="https://www.cgisecurity.com/owasp/html/ch08.html" rel="noopener noreferrer"&gt;https://www.cgisecurity.com/owasp/html/ch08.html&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4) Permission(s)
&lt;/h3&gt;

&lt;p&gt;A more general/informal term, closest in meaning to "authorization" when used in the context of computer science.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Permission&lt;/strong&gt; to &lt;strong&gt;access&lt;/strong&gt; a &lt;strong&gt;resource&lt;/strong&gt; is called &lt;strong&gt;authorization&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  All together
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgvs7a0tl52d3wpp0q3pn.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgvs7a0tl52d3wpp0q3pn.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s see these terms used in a sentence by observing the following imagined pull request (PR):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Title: Added &lt;strong&gt;access control&lt;/strong&gt; to the app.  &lt;/p&gt;

&lt;p&gt;Description:&lt;br&gt;&lt;br&gt;
I implemented a way for users to &lt;strong&gt;authenticate&lt;/strong&gt; via email and password or via Google.&lt;br&gt;&lt;br&gt;
On the server-side, I added &lt;strong&gt;permission checks&lt;/strong&gt; to most of our REST API handlers, to ensure an &lt;strong&gt;authenticated&lt;/strong&gt; user is &lt;strong&gt;authorized&lt;/strong&gt; to execute them.&lt;br&gt;&lt;br&gt;
If the user is not &lt;strong&gt;authorized&lt;/strong&gt;, we throw an HTTP error 403.&lt;br&gt;&lt;br&gt;
There are also some public parts of REST API where the user doesn’t have to be &lt;strong&gt;authenticated&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  2. Where do we check permissions in a web app: frontend vs backend vs database
&lt;/h2&gt;

&lt;p&gt;We explained a bunch of terms, let's see now how access permission checks are actually done in practice!&lt;/p&gt;

&lt;p&gt;In a typical web app, you will have a frontend, backend (server), and database.&lt;/p&gt;

&lt;p&gt;The frontend will be issuing commands to the server, which then executes operations and possibly modifies the database (on their behalf). Since &lt;strong&gt;users don’t have direct access to the database&lt;/strong&gt;, and since the &lt;strong&gt;frontend is inherently not secure&lt;/strong&gt;, that leaves the &lt;strong&gt;server as the central place where all the crucial access control needs to happen&lt;/strong&gt;.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fylm0sci3j4adsx3aqoqt.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fylm0sci3j4adsx3aqoqt.png"&gt;&lt;/a&gt;&lt;br&gt;A real-life photo of frontend, server, and database performing access control.
  &lt;/p&gt;

&lt;h3&gt;
  
  
  Frontend (browser)
&lt;/h3&gt;

&lt;p&gt;By frontend we mean web client -&amp;gt; code (e.g. JS) that executes in the browser.&lt;/p&gt;

&lt;p&gt;The frontend is here to help users issue commands towards the server via which users can access and/or modify the resources of our web app (which are most often stored in the database).&lt;/p&gt;

&lt;p&gt;Since users can manipulate the frontend code as they wish, we can't really do any permissions checks in the frontend code, we can't trust it!&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhv44sbz2qbuclxsfs8yu.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhv44sbz2qbuclxsfs8yu.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Any permission checks we do on the frontend, we will need to repeat on the server in any case.&lt;br&gt;&lt;br&gt;
If that is so, should we at all check permissions on the frontend, and what is the purpose of that?&lt;br&gt;&lt;br&gt;
&lt;strong&gt;The main reason for doing any permissions checks on the frontend is ergonomics/user experience&lt;/strong&gt; -&amp;gt; by having UI focus only on resources they can change, we make it easier for users to understand what they can do in our web app and make sure they don't waste time on trying to describe complex operations that server will then not be able to execute.&lt;/p&gt;

&lt;p&gt;So, for example, our frontend code can hide/omit certain fields in the UI form if the user shouldn't be able to access them, it can prevent opening certain pages, or hide/omit certain buttons if they trigger operations that the user is not allowed to perform.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frgppz76ct4y90qt93tr8.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frgppz76ct4y90qt93tr8.png"&gt;&lt;/a&gt;&lt;br&gt;Example of using casl.js in React to show button only if the user has permission.
  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Takeaway: Permission checks on frontend are not there for security, but only for ergonomics / improving user experience.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Backend (server)
&lt;/h3&gt;

&lt;p&gt;The server is &lt;strong&gt;a crucial place to implement access control&lt;/strong&gt;. It exposes an API that the frontend (browser) and/or other services consume. While doing that, they authenticate with the server, so that server knows who they are, and then they ask (i.e. via REST API or GraphQL API) the server to execute certain operations (i.e. creating, updating, or fetching something). It is the server’s job to figure out if they are allowed (authorized) to perform those operations (on specified resources / with provided arguments) and to reject them if they are not.&lt;/p&gt;

&lt;p&gt;At its core, permissions checks on the server are here to &lt;strong&gt;check for each API endpoint&lt;/strong&gt; if the caller is allowed to execute it. Often they are executed at the very start of the API endpoint logic, but often they are also intertwined with the rest of the endpoint handler logic.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0o9rqjfhhz7gczix1lq4.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0o9rqjfhhz7gczix1lq4.png"&gt;&lt;/a&gt;&lt;br&gt;Example of doing permission check at the start of API endpoint (is user authenticated) and then also doing another check as part of the database query (is user owner of the article they are trying to delete).
  &lt;/p&gt;

&lt;p&gt;Besides defining checks at API/operation level, they are also often defined at the &lt;strong&gt;data/model level&lt;/strong&gt;. This means that they are tied to specific data models (normally from the database), as part of data access logic (ORM), and are defining who can access specific field(s), or even the whole data model.&lt;/p&gt;

&lt;p&gt;Example of attaching permission checks to the data model directly in the GraphQL schema (&lt;a href="https://www.prisma.io/blog/graphql-directive-permissions-authorization-made-easy-54c076b5368e" rel="noopener noreferrer"&gt;from this blog post&lt;/a&gt;):&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F24epvlvl7g83n6ovou5j.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F24epvlvl7g83n6ovou5j.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For a more sophisticated RBAC approach, with an additional layer of indirection (permissions), read on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Database
&lt;/h3&gt;

&lt;p&gt;Usually, users don’t have direct access to the database at all, instead, they affect it via the server. &lt;strong&gt;In such a case, there is no need to do specific database access control&lt;/strong&gt; besides normal constraints that you will have in your DB to ensure data model integrity, like uniqueness, validations, and similar. That said, in some situations, you might want to do it, but we will not get into that in this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Common approaches (RBAC, ABAC, …)
&lt;/h2&gt;

&lt;p&gt;The most common approaches to access control are &lt;strong&gt;RBAC&lt;/strong&gt; (Role-Based Access Control) and &lt;strong&gt;ABAC&lt;/strong&gt; (Attribute-Based Access Control), with RBAC having the strong lead (but ABAC is picking up).&lt;/p&gt;

&lt;p&gt;Although less popular, we will also shortly mention ReBAC as an “in-between” option.&lt;/p&gt;

&lt;h3&gt;
  
  
  RBAC - Role-based access control
&lt;/h3&gt;

&lt;p&gt;Roles rule :D! In &lt;a href="https://en.wikipedia.org/wiki/Role-based_access_control" rel="noopener noreferrer"&gt;RBAC&lt;/a&gt;, roles are the central concept. Some example roles might be &lt;code&gt;admin&lt;/code&gt;, &lt;code&gt;guest&lt;/code&gt;, &lt;code&gt;writer&lt;/code&gt;, &lt;code&gt;moderator&lt;/code&gt;, …. When determining if a certain user has access, &lt;strong&gt;we check their roles and determine their access rights based on it&lt;/strong&gt;. For example, &lt;code&gt;admin&lt;/code&gt; can delete other users, articles, and projects, but &lt;code&gt;guest&lt;/code&gt; can’t modify any resources, only read articles.&lt;/p&gt;

&lt;p&gt;Pro advice (thanks Karan!): While we could be checking the user’s roles directly in the permission checks, it is even better (and recommended by OWASP) to add a layer of indirection → permissions. &lt;strong&gt;So roles are attached to users, permissions are attached to roles, and permission checks check permissions&lt;/strong&gt; (who would expect that :)!?).&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fow4j0l0hmb42fwx4519e.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fow4j0l0hmb42fwx4519e.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example, a user might have role &lt;code&gt;admin&lt;/code&gt;, and role &lt;code&gt;admin&lt;/code&gt; has permissions &lt;code&gt;updateArticle&lt;/code&gt; and &lt;code&gt;deleteArticle&lt;/code&gt; attached to it. Then, when determining if a user can delete the article, we first fetch his role, then we fetch the permissions attached to that role, and finally check if &lt;code&gt;deleteArticle&lt;/code&gt; is present among those → if so, they can continue with the deletion!&lt;/p&gt;

&lt;p&gt;This way, if we decide a certain role should have more or fewer permissions, we just add or remove the permission in question to the role, and that is it! We don’t have to go through every permission check and update its logic (which we would have to do if we were checking directly against roles).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RBAC is popular because it is relatively simple and it reflects the basic business domain pretty well&lt;/strong&gt; - often we are thinking in the terms of roles in the real world, so it is easy to grasp and understand. There are plenty of solutions and frameworks out there that implement RBAC.&lt;/p&gt;

&lt;p&gt;While a good match for many common use cases, there is a drawback to RBAC - when access control becomes complex (which usually happens as the web app evolves and grows big), RBAC sometimes fails in offering needed granularity in an elegant way, resulting in unwieldy and overly-complex access control logic.&lt;/p&gt;

&lt;h3&gt;
  
  
  ABAC - Attribute-based access control
&lt;/h3&gt;

&lt;p&gt;In &lt;a href="https://en.wikipedia.org/wiki/Attribute-based_access_control" rel="noopener noreferrer"&gt;ABAC&lt;/a&gt;, key idea is that you define &lt;strong&gt;a bunch of access control rules&lt;/strong&gt; where each rule takes different “attributes” as input. When you need to check if a user is authorized to do smth, you run the rules and &lt;strong&gt;if all the rules pass, it is a go&lt;/strong&gt;, but if a single rule fails, it is a no go.&lt;/p&gt;

&lt;p&gt;Rule attributes can be anything, but usually, they fall into 4 categories:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Subject&lt;/strong&gt;: information about a user (i.e. user’s id or name)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action&lt;/strong&gt;: operation they want to perform (i.e. reading an Article)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Object&lt;/strong&gt;: resources they want to operate on (i.e. an Article),&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment/context:&lt;/strong&gt; i.e. current time of the day or number of previous requests that the user did in the last hour.&lt;/li&gt;
&lt;/ol&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6pv1bkeu5vlsqv8vsxgd.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6pv1bkeu5vlsqv8vsxgd.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s observe the example from before where we wanted to know if user is allowed to delete an article.&lt;br&gt;&lt;br&gt;
In ABAC, we could define an action “deleteArticle”, and then define a rule that takes user(subject), action, object, and additional context. That rule would check if action is “deleteArticle” → if so, it would evaluate if user is allowed to delete the article specified as an object, by checking some properties of user, maybe even role, or by checking if user is owner of that article.&lt;br&gt;&lt;br&gt;
Then, when user actually issues a command to delete an article, we would ask our access control system to run it against all the rules it has, while giving it the (user, “deleteArticle”, article, context) tuple → most of the rules would say all is ok since they are not concerned with “deleteArticle” action, but the ones that are (like the one we defined above) must all pass in order to actually allow the access.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ABAC is very flexible and general as an approach&lt;/strong&gt;, and you could easily implement RBAC (and many other approaches) in ABAC (by checking the user’s role as one of the attributes) → therefore it is more general/expressive than RBAC.&lt;/p&gt;

&lt;p&gt;However, ABAC is more complex to implement, and it is also more expensive performance-wise, due to needing to check multiple rules each time that access control check is being performed.&lt;/p&gt;

&lt;h3&gt;
  
  
  ReBAC - Relationship-based access control
&lt;/h3&gt;

&lt;p&gt;Roles (RBAC) can be lacking when you need to grant access based on relationship-related questions like “is this user owner of this article” or “does this user belong to this workspace”.&lt;/p&gt;

&lt;p&gt;While ABAC can easily handle this, you could also consider it a bit too powerful if all you need to describe are relationships → and this is where ReBAC comes in.&lt;/p&gt;

&lt;p&gt;While there are different ways one could go about implementing ReBAC, the simplest one is to build on top of RBAC by introducing a concept of “relationship” rules to your access control logic and then checking those alongside the roles. So RBAC with a dash of ABAC (focused on relationships).&lt;/p&gt;

&lt;h2&gt;
  
  
  4. OWASP recommendations
&lt;/h2&gt;

&lt;p&gt;When looking online for “official”/standardized recommendations on how to do access control in web apps, you will most likely find resources produced by OWASP.&lt;/p&gt;

&lt;p&gt;Definition of OWASP: The Open Web Application Security Project® (OWASP) is a nonprofit foundation that works to improve the security of software.&lt;/p&gt;

&lt;p&gt;I found that they have quite a few resources on how to do access control in web apps, the most interesting being the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://owasp.org/www-pdf-archive/ASDC12-Access_Control_Designs_and_Pitfalls.pdf" rel="noopener noreferrer"&gt;OWasp presentation about how to do ACL in Web App&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Authorization_Cheat_Sheet.md" rel="noopener noreferrer"&gt;OWasp cheat sheet on how to do ACL in Web App.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From their materials I extracted a couple of main points that made the most sense to me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Centralize the access control logic so it is easy to review.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deny access by default.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prefer ABAC over RBAC.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh5j5y440i9wkva057flx.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh5j5y440i9wkva057flx.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Implementing access control in practice
&lt;/h2&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frzw73lfhx96dc45bqgp7.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frzw73lfhx96dc45bqgp7.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s a Reddit &lt;a href="https://www.reddit.com/r/webdev/comments/vhaglx/what_do_you_use_for_access_control_permission/" rel="noopener noreferrer"&gt;poll I did on r/webdev&lt;/a&gt;.&lt;br&gt;&lt;br&gt;
An interesting finding is that even though the sample is pretty small, it is clear that devs prefer RBAC over OWASP-recommended ABAC.&lt;br&gt;&lt;br&gt;
I believe this is due to 2 main reasons: RBAC is simpler + there are more libraries/frameworks out there supporting RBAC than ABAC (again, due to it being simpler).&lt;br&gt;&lt;br&gt;
It does seem that ABAC is picking up recently though, so it would be interesting to repeat this poll in the future and see what changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Organic development
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc7att18c1g826n2hecl9.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc7att18c1g826n2hecl9.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Often, we add permission checks to our web app one by one, as needed. For example, if we are using NodeJS with ExpressJS for our server and writing middleware that handles HTTP API requests, we will add a bit of logic into that middleware that does some checks to ensure a user can actually perform that action. Or maybe we will embed “checks” into our database queries so that we query only what the user is allowed to access. Often a combination.&lt;/p&gt;

&lt;p&gt;What can be dangerous with such an organic approach is the complexity that arises as the codebase grows - if we don’t put enough effort into centralizing and structuring our access control logic, it can become very hard to reason about it and to do consistent updates to it, leading to mistakes and vulnerabilities.&lt;/p&gt;

&lt;p&gt;Imagine having to modify the web app so that user can now only read their own articles and articles of their friends, while before they were allowed to read any article. If there is only one place where we can make this update, we will have a nice time, but if there are a bunch of places and we need to hunt those down first and then make sure they are all updated in the same way, we are in for a lot of trouble and lot of space to make mistakes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using an existing solution
&lt;/h3&gt;

&lt;p&gt;Instead of figuring out on our own how to structure the access control code, often it is a better choice to use an existing access control solution! Besides not having to figure and implement everything on your own, another big advantage is that these solutions are battle-tested, which is very important for the code dealing with the security of your web app.&lt;/p&gt;

&lt;p&gt;We can roughly divide these solutions into frameworks and (external) providers, where frameworks are embedded into your web app and shipped together with it, while providers are externally hosted and usually paid services.&lt;/p&gt;

&lt;p&gt;A couple of popular solutions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://casbin.org/" rel="noopener noreferrer"&gt;https://casbin.org/&lt;/a&gt; (multiple approaches, multiple languages, provider)

&lt;ol&gt;
&lt;li&gt;Open source authZ library that has support for many access control models (ACL, RBAC, ABAC, …) and many languages (Go, Java, Node.js, JS, Rust, …). While somewhat complex, it is also powerful and flexible. They also have their Casdoor platform, which is authN and authZ provider.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://casl.js.org/v5/en/" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://casl.js.org/v5/en/" rel="noopener noreferrer"&gt;https://casl.js.org/v5/en/&lt;/a&gt; (ABAC, Javascript)

&lt;ol&gt;
&lt;li&gt;Open source JS/TS library for ABAC. CASL gives you a nice way to define the ABAC rules in your web / NodeJS code, and then also check them and call them. It has a bunch of integrations with popular solutions like React, Angular, Prisma, Mongoose, … .&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/CanCanCommunity/cancancan" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://github.com/CanCanCommunity/cancancan" rel="noopener noreferrer"&gt;https://github.com/CanCanCommunity/cancancan&lt;/a&gt; (Ruby on Rails ABAC)

&lt;ol&gt;
&lt;li&gt;Same like casl.js, but for Ruby on Rails! Casl.js was actually inspired and modeled by cancancan.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/varvet/pundit" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://github.com/varvet/pundit" rel="noopener noreferrer"&gt;https://github.com/varvet/pundit&lt;/a&gt;

&lt;ol&gt;
&lt;li&gt;Popular open-source Ruby library focused around the notion of policies, giving you the freedom to implement your own approach based on that.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://spring.io/projects/spring-security" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://spring.io/projects/spring-security" rel="noopener noreferrer"&gt;https://spring.io/projects/spring-security&lt;/a&gt; 

&lt;ol&gt;
&lt;li&gt;Open source authN and authZ framework for Spring (Java). &lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/dfunckt/django-rules" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://github.com/dfunckt/django-rules" rel="noopener noreferrer"&gt;https://github.com/dfunckt/django-rules&lt;/a&gt;

&lt;ol&gt;
&lt;li&gt;A generic, approachable open source framework for building rule-based systems in Django (Python).&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://auth0.com/" rel="noopener noreferrer"&gt;Auth0&lt;/a&gt; (provider)

&lt;ol&gt;
&lt;li&gt;Auth0 has been around for some time and is probably the most popular authN provider out there. While authN is their main offering (they give you SDKs for authentication + they store user profiles and let you manage them through their SaaS), they also allow you to define authZ to some degree, via RBAC and policies.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://www.osohq.com/" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://www.osohq.com/" rel="noopener noreferrer"&gt;https://www.osohq.com/&lt;/a&gt; (provider, DSL)

&lt;ol&gt;
&lt;li&gt;OSO is an authZ provider, unique in a way that they have a specialized language for authorization (DSL, called Polar) in which you define your authorization rules. They come with support for common approaches (e.g. RBAC, ABAC, ReBAC) but also support custom ones. Then, you can use their open source library embedded in your application, or use their managed cloud offering.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://warrant.dev/" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://warrant.dev/" rel="noopener noreferrer"&gt;https://warrant.dev/&lt;/a&gt; (Provider)

&lt;ol&gt;
&lt;li&gt;Relatively new authZ provider, they have a dashboard where you can manage your rules in a central location and then use them from multiple languages via their SDKs, even on the client to perform UI checks. Rules can also be managed programmatically via SDK.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://authzed.com/" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://authzed.com/" rel="noopener noreferrer"&gt;https://authzed.com/&lt;/a&gt; (Provider)

&lt;ol&gt;
&lt;li&gt;AuthZed brings a specialized SpiceDB permissions database which they use as a centralized place for storing and managing rules. Then, you can use their SDKs to query, store, and validate application permissions.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;h2&gt;
  
  
  Summary (TLDR)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Authentication&lt;/strong&gt; (authN) answers “who are they”, &lt;strong&gt;authorization&lt;/strong&gt; (authZ) answers “are they allowed to”, while &lt;strong&gt;access control&lt;/strong&gt; is the overarching term for the whole process of performing authN and authZ.&lt;/li&gt;
&lt;li&gt;Doing access control on the frontend is just for show (for improving UX) and you can’t rely on it. &lt;strong&gt;Any and all real access control needs to be done on the server&lt;/strong&gt; (possibly a bit in the db, but normally not needed).&lt;/li&gt;
&lt;li&gt;While it is ok to start with a simple access control approach at the beginning, you should be ready to switch to a more advanced approach once the complexity grows. The most popular approaches for doing access control are &lt;strong&gt;RBAC&lt;/strong&gt; (role-based) and &lt;strong&gt;ABAC&lt;/strong&gt; (attribute-based). RBAC is easier to get going with, but ABAC is more powerful.&lt;/li&gt;
&lt;li&gt;You should make sure your access control has &lt;strong&gt;as little duplication as possible and is centralized&lt;/strong&gt;, in order to reduce the chance of introducing bugs.&lt;/li&gt;
&lt;li&gt;It is usually smart to &lt;strong&gt;use existing solutions&lt;/strong&gt;, like access control frameworks or external providers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Access control in Wasp
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://wasp-lang.dev" rel="noopener noreferrer"&gt;Wasp&lt;/a&gt;, we don’t yet have special support for access control, although we are planning to add it in the future. As it seems at the moment, we will probably go for ABAC, and we would love to provide a way to define access rules both at the Operations level and at Entity (data model) level. Due to Wasp’s mission to provide a highly integrated full-stack experience, we are excited about the possibilities this offers to provide an access control solution that is integrated tightly with the whole web app, through the whole stack!&lt;/p&gt;

&lt;p&gt;You can check out our discussion about this in our &lt;a href="https://github.com/wasp-lang/wasp/issues/584" rel="noopener noreferrer"&gt;“Support for Permissions” RFC&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thanks to the reviewers
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://twitter.com/karankajla" rel="noopener noreferrer"&gt;Karan Kajla&lt;/a&gt; (pro advice on RBAC!), &lt;a href="https://twitter.com/grahamneray" rel="noopener noreferrer"&gt;Graham Neray&lt;/a&gt; (great general advice + pointed out ReBAC), &lt;a href="https://twitter.com/lawjolla" rel="noopener noreferrer"&gt;Dennis Walsh&lt;/a&gt; (awesome suggestions how to have article read better), &lt;a href="https://github.com/shayneczyzewski" rel="noopener noreferrer"&gt;Shayne Czyzewski&lt;/a&gt;, &lt;a href="https://twitter.com/matijasosic" rel="noopener noreferrer"&gt;Matija Sosic&lt;/a&gt;, thank you for taking the time to review this article and make it better! Your suggestions, corrections, and ideas were invaluable.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>security</category>
      <category>programming</category>
    </item>
    <item>
      <title>How to implement a Discord bot (in NodeJS) that requires new members to introduce themselves</title>
      <dc:creator>Martin Šošić</dc:creator>
      <pubDate>Tue, 04 May 2021 16:30:37 +0000</pubDate>
      <link>https://dev.to/martinsos/how-to-implement-a-discord-bot-in-nodejs-that-requires-new-members-to-introduce-themselves-926</link>
      <guid>https://dev.to/martinsos/how-to-implement-a-discord-bot-in-nodejs-that-requires-new-members-to-introduce-themselves-926</guid>
      <description>&lt;p&gt;At &lt;a href="https://wasp-lang.dev" rel="noopener noreferrer"&gt;Wasp&lt;/a&gt;, we have a Discord server for our community, where we talk with people interested in and using Wasp - Waspeteers!&lt;/p&gt;

&lt;p&gt;In the beginning, we knew everybody in the community by their name, but as it started growing, we had a lot of people joining that never wrote anything, and the community started feeling less homey, less intimate.&lt;/p&gt;

&lt;p&gt;This was when we decided to make it required for the new members to introduce themselves to gain access to the community.&lt;br&gt;
We knew that with this kind of barrier we would probably lose some potential new Waspeteers, but those that would go through it would be more engaged and better integrated.&lt;/p&gt;

&lt;p&gt;We found no other way to accomplish this automatically but to implement our own Discord bot.&lt;br&gt;
In this post I will describe in detail how we did it.&lt;/p&gt;


&lt;h2&gt;
  
  
  High-level approach
&lt;/h2&gt;

&lt;p&gt;We want the following: when a new user comes to our Discord server, they should be able to access only "public" channels, like &lt;code&gt;rules&lt;/code&gt;, &lt;code&gt;contributing&lt;/code&gt;, and most importantly, &lt;code&gt;introductions&lt;/code&gt;, where they could introduce themselves.&lt;/p&gt;

&lt;p&gt;Once they introduced themselves in the &lt;code&gt;introductions&lt;/code&gt; channel, they would get access to the rest of the channels.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1wugan62xc12w5uo6xn0.png" class="article-body-image-wrapper"&gt;&lt;img alt="Channels user can see when Guest vs when full member." src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1wugan62xc12w5uo6xn0.png"&gt;&lt;/a&gt;Left: what Guest sees; Right: what Waspeteer sees.
    
  
&lt;/p&gt;

&lt;p&gt;In Discord, access control is performed via roles. There are two ways to accomplish what we need:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Adding a role that grants access&lt;/strong&gt;. When they join, they have no roles. Once they introduce themselves, they are granted a role (e.g. &lt;code&gt;Member&lt;/code&gt; or &lt;code&gt;Waspeteer&lt;/code&gt;) that is required to access the rest of the server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Removing a role that forbids access&lt;/strong&gt;. When they join, they are automatically assigned a role &lt;code&gt;Guest&lt;/code&gt;, for which we configured the non-public channels to deny access. Once they introduce themselves, the role &lt;code&gt;Guest&lt;/code&gt; gets removed and they gain access to the rest of the server.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We decided to go with the second approach since it means we don't have to assign all the existing members with a new role. From now on, we will be talking about how to get this second approach working.&lt;/p&gt;

&lt;p&gt;To get this going, we need to do the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create role &lt;code&gt;Guest&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Ensure that the &lt;code&gt;Guest&lt;/code&gt; role has permissions to access only "public" channels.
One convenient way to go about this is to disable "View Channels" permission for the role &lt;code&gt;Guest&lt;/code&gt; at the level of Category, so it propagates to all the channels in it, instead of doing it for every single channel.
Once you are done, use the "See server as a role" feature of Discord to confirm that you set the permissions as you wanted.&lt;/li&gt;
&lt;li&gt;Automatically inform new members that they must introduce themselves in the &lt;code&gt;introductions&lt;/code&gt; channel with &lt;code&gt;!intro &amp;lt;text_about_me&amp;gt;&lt;/code&gt; to gain access to the rest of the server.
This can be done via Discord's "Welcome Screen" feature or via one of the many existing Discord bots out there offering this functionality.&lt;/li&gt;
&lt;li&gt;Automatically assign the &lt;code&gt;Guest&lt;/code&gt; role to a new member when they join the server.&lt;/li&gt;
&lt;li&gt;Automatically remove the &lt;code&gt;Guest&lt;/code&gt; role when a member introduces themselves in the public &lt;code&gt;introductions&lt;/code&gt; channel.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Points #1, #2 and #3 are relatively straight-forward.&lt;/p&gt;

&lt;p&gt;For the #4 (automatic assignment of the role when a new member joins the server), since Discord doesn't support this directly, you will need a bot to do it.&lt;br&gt;
Luckily, many bots allow you to auto-assign roles when new members join, and I ended up using &lt;a href="https://mee6.xyz/" rel="noopener noreferrer"&gt;MEE6&lt;/a&gt; for this.&lt;br&gt;
I set it up so that when a new member joins, they are immediately assigned a &lt;code&gt;Guest&lt;/code&gt; role.&lt;/p&gt;

&lt;p&gt;However, for the #5 (remove or assign the role on a message in a specific channel), the situation is more complicated - I couldn't find a single bot out there that supports this!&lt;br&gt;
The closest I got was with &lt;a href="https://carl.gg/" rel="noopener noreferrer"&gt;Carl Bot&lt;/a&gt; and its "tags" feature, which allows you to write custom code, but in the end, it turned out to be too restrictive to accomplish this.&lt;br&gt;
Therefore, I ended up implementing our own bot (Wasp Bot) that does this.&lt;/p&gt;
&lt;h2&gt;
  
  
  Implementing a Discord Bot (NodeJS)
&lt;/h2&gt;

&lt;p&gt;I decided to implement a bot in NodeJS since it is easy to get started quickly and there is a good Discord library.&lt;/p&gt;

&lt;p&gt;I will describe how to create it step by step below, but &lt;a href="https://github.com/wasp-lang/wasp-bot/tree/4b3858202622c7635aeb6f1d71d9ba9781eea6eb" rel="noopener noreferrer"&gt;here is the final code of the bot&lt;/a&gt; if you want to skip ahead.&lt;/p&gt;
&lt;h3&gt;
  
  
  Defining bot on Discord and adding it to your server.
&lt;/h3&gt;

&lt;p&gt;Before we even start implementing the bot, we will tell Discord about it first, in order to obtain the neccessary credentials that we will use in our code, and we will add the bot to our server.&lt;br&gt;
There are many tutorials already on how to do this, so I will keep it short.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to Discord Developer Portal, create a new Application -&amp;gt; I named it &lt;code&gt;Wasp&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Go to the "Bot" part of Application "Settings" and add a new bot. I named it &lt;code&gt;WaspBot&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;On the "Bot" page of your freshly created bot, there is a "TOKEN" part -&amp;gt; create a mental note about it, we will need this later when running our bot.&lt;/li&gt;
&lt;li&gt;Go to the "OAuth2" part of Application "Settings". Here we will define which permissions our bot will have.
This is done by checking the permissions we want to give it and then following the URL that will be generated based on our choices.

&lt;ul&gt;
&lt;li&gt;Check the &lt;code&gt;bot&lt;/code&gt; under the "SCOPES" section.&lt;/li&gt;
&lt;li&gt;Scroll down further to find the "BOT PERMISSIONS" section. There, check the &lt;code&gt;Manage Roles&lt;/code&gt;, &lt;code&gt;View Channels&lt;/code&gt;, &lt;code&gt;Read Message History&lt;/code&gt;, and &lt;code&gt;Send Messages&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Under the "SCOPES" section above, you will see a URL. Copy it into the browser and follow the steps to add the bot to your server.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Creating a basic bot in NodeJS
&lt;/h3&gt;

&lt;p&gt;In a directory where your code will be, create a new npm project with &lt;code&gt;npm init&lt;/code&gt; -&amp;gt; set the entry point to &lt;code&gt;bot.js&lt;/code&gt; instead of &lt;code&gt;index.js&lt;/code&gt;. This will result in a &lt;code&gt;package.json&lt;/code&gt; file being generated.&lt;/p&gt;

&lt;p&gt;We will need one important dependency, &lt;code&gt;discord.js&lt;/code&gt;, to make it easy to work with Discord's API.&lt;br&gt;
Add it with &lt;code&gt;npm install -S discord.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, create &lt;code&gt;bot.js&lt;/code&gt; file next to &lt;code&gt;package.json&lt;/code&gt; with following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Discord&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;discord.js&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;BOT_TOKEN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BOT_TOKEN&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Discord&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;bot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;BOT_TOKEN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;bot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ready&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Logged in as: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;bot&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;tag&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is it! Run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DISCORD_BOT=&amp;lt;TOKEN_OF_YOUR_DISCORD_BOT&amp;gt; node bot.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and you should see output about successful login, in my case it was &lt;code&gt;Logged in as: WaspBot#1234&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Detecting a valid introduction from a member
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: For the following part, I was using &lt;a href="https://discord.js.org/#/" rel="noopener noreferrer"&gt;Discord.js&lt;/a&gt; docs to figure out how to do it, so if you need more details on a specific step, check them out.&lt;/p&gt;

&lt;p&gt;Now is the moment to define exactly how we want the introduction process to go.&lt;br&gt;
So, let's say that the correct way for new members to introduce themselves is by sending a message to the &lt;code&gt;introductions&lt;/code&gt; channel that starts with &lt;code&gt;!intro&lt;/code&gt; and follows with at least 20 characters of text (to ensure the introduction is not too short).&lt;br&gt;
&lt;code&gt;!intro&lt;/code&gt; makes it easy for our bot to know when to act (in Discord, bot commands often start with &lt;code&gt;!&amp;lt;something&amp;gt;&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Let's add the needed code to &lt;code&gt;bot.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="p"&gt;...&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;INTRODUCTIONS_CHANNEL_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;YOU_WILL_HAVE_TO_FIND_THIS_ON_DISCORD_SERVER&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="nx"&gt;bot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;msg&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;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;!intro &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;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;channel&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="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;INTRODUCTIONS_CHANNEL_ID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;introductionsChannelName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;guild&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;channels&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;INTRODUCTIONS_CHANNEL_ID&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`Please use !intro command in the &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;introductionsChannelName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; channel!`&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;introMsg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;!intro &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;trim&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;minMsgLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&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;introMsg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;minMsgLength&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`Please write introduction at least &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;minMsgLength&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; characters long!`&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Yay successful introduction!`&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;One thing to notice is that you will have to obtain the ID of the &lt;code&gt;introductions&lt;/code&gt; channel and paste it in your code where I put the placeholder above.&lt;br&gt;
You can find out this ID by going to your Discord server in the Discord app, right-clicking on the &lt;code&gt;introductions&lt;/code&gt; channel, and clicking on &lt;code&gt;Copy ID&lt;/code&gt;. For this to work, you will first have to enable the "Developer Mode" (under "User Settings" &amp;gt; "Advanced").&lt;/p&gt;
&lt;h3&gt;
  
  
  Removing the "Guest" role upon successful introduction
&lt;/h3&gt;

&lt;p&gt;What is missing is removing the &lt;code&gt;Guest&lt;/code&gt; role upon successful introduction, so let's do that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;INTRODUCTIONS_CHANNEL_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;YOU_WILL_HAVE_TO_FIND_THIS_ON_DISCORD_SERVER&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;GUEST_ROLE_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;YOU_WILL_HAVE_TO_FIND_THIS_ON_DISCORD_SERVER&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="nx"&gt;bot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;msg&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;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;!intro &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;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;channel&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="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;INTRODUCTIONS_CHANNEL_ID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;introductionsChannelName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;guild&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;channels&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;INTRODUCTIONS_CHANNEL_ID&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`Please use !intro command in the &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;introductionsChannelName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; channel!`&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;introMsg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;!intro &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;trim&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;minMsgLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&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;introMsg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;minMsgLength&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`Please write introduction at least &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;minMsgLength&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; characters long!`&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;member&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;guild&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;member&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;member&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;GUEST_ROLE_ID&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;member&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;GUEST_ROLE_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;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Nice getting to know you! You are no longer a guest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
          &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; and have full access, welcome!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Error: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;Same as with the ID of the &lt;code&gt;introductions&lt;/code&gt; channel, now you will also need to find out the ID of the &lt;code&gt;Guest&lt;/code&gt; role (which you should have created at some point).&lt;br&gt;
You can do it by finding it in the server settings, under the list of roles, right-clicking on it, and then "Copy ID".&lt;/p&gt;

&lt;p&gt;This is it! You can now run the bot with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;DISCORD_BOT&lt;/span&gt;&lt;span class="o"&gt;=&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TOKEN_OF_YOUR_DISCORD_BOT&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="nx"&gt;bot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and if you assign yourself a &lt;code&gt;Guest&lt;/code&gt; role on the Discord server and then type &lt;code&gt;!intro Hi this is my introduction, I am happy to be here.&lt;/code&gt; in the &lt;code&gt;introductions&lt;/code&gt; channel, you should see yourself getting full access together with an appropriate message from your bot.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploying the bot
&lt;/h3&gt;

&lt;p&gt;While there are many ways to deploy the Discord bot, I will shortly describe how we did it via Heroku.&lt;/p&gt;

&lt;p&gt;We created a Heroku app &lt;code&gt;wasp-discord-bot&lt;/code&gt; and set up the "Automatic deploys" feature on Heroku to automatically deploy every push to the &lt;code&gt;production&lt;/code&gt; branch (our bot is on Github).&lt;/p&gt;

&lt;p&gt;On Heroku, we set the environment variable &lt;code&gt;DISCORD_BOT&lt;/code&gt; to the token of our bot.&lt;/p&gt;

&lt;p&gt;Finally, we added &lt;code&gt;Procfile&lt;/code&gt; file to our project:&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;worker&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node bot.js&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is it! On every push to the &lt;code&gt;production&lt;/code&gt; branch, our bot gets deployed.&lt;/p&gt;

</description>
      <category>node</category>
      <category>discord</category>
    </item>
    <item>
      <title>Wasp - language for developing full-stack Javascript web apps with no boilerplate</title>
      <dc:creator>Martin Šošić</dc:creator>
      <pubDate>Thu, 04 Mar 2021 16:32:18 +0000</pubDate>
      <link>https://dev.to/martinsos/wasp-language-for-developing-full-stack-javascript-web-apps-with-no-boilerplate-3nc8</link>
      <guid>https://dev.to/martinsos/wasp-language-for-developing-full-stack-javascript-web-apps-with-no-boilerplate-3nc8</guid>
      <description>&lt;p&gt;For the last year and a half, my twin brother and I have been working on &lt;a href="https://wasp-lang.dev" rel="noopener noreferrer"&gt;Wasp&lt;/a&gt;: a new programming language for developing full-stack web apps with less code.&lt;/p&gt;

&lt;p&gt;Wasp is a &lt;strong&gt;simple declarative language&lt;/strong&gt; that makes developing web apps easy while still allowing you to use the latest technologies like &lt;strong&gt;React, Node.js, and Prisma&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this post, I will share with you why we believe Wasp could be a big thing for web development, how it works, where we are right now and what is the plan for the future!&lt;/p&gt;

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

&lt;p&gt;You know how to use React, know your way around HTML/CSS/…, know how to write business logic on the backend (e.g. in Node), but when you want to build an actual web app and deploy it for others to use, you drown in all the details and extra work - responsive UI, proper error handling, security, building, deployment, authentication, managing server state on the client, managing database, different environments, ....&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fubv7itjzqtgw4md2dkdz.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fubv7itjzqtgw4md2dkdz.png" alt="Iceberg of web dev"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Jose Aguinaga described in a fun way the unexpected complexity of web app development in his blog post &lt;a href="https://hackernoon.com/how-it-feels-to-learn-javascript-in-2016-d3a717dd577f" rel="noopener noreferrer"&gt;"How it feels to learn JavaScript in 2016"&lt;/a&gt;, which still feels relevant 4 years later. &lt;/p&gt;

&lt;p&gt;We are building Wasp because even though we are both experienced developers and have worked on multiple complex web apps in various technologies (JQuery -&amp;gt; Backbone -&amp;gt; Angular -&amp;gt; React, own scripts / makefile -&amp;gt; Grunt -&amp;gt; Gulp -&amp;gt; Webpack, PHP -&amp;gt; Java -&amp;gt; Node.js, …), we still feel &lt;strong&gt;building web apps is harder than it should be&lt;/strong&gt;, due to a lot of boilerplate and repetitive work involved in the process.  &lt;/p&gt;

&lt;p&gt;The main insight for us was that while the tech stack keeps advancing rapidly, the core requirements of the apps are mostly remaining the same (auth, routing, data model CRUD, ACL, …).&lt;/p&gt;

&lt;p&gt;That is why almost 2 years ago we started thinking about &lt;strong&gt;separating web app specification&lt;/strong&gt; (what it should do) &lt;strong&gt;from its implementation&lt;/strong&gt; (how it should do it).&lt;br&gt;&lt;br&gt;
This led us to the idea of extracting common web app features and concepts into a special specification language (Wasp), while the implementation details are still described via a modern stack (right now React, Node.js, Prisma).&lt;/p&gt;

&lt;p&gt;Our vision with Wasp is to create &lt;strong&gt;a powerful but simple language where you can describe your web app as humanly as possible&lt;/strong&gt;.&lt;br&gt;
We want to make the top of that iceberg on the image above as pleasant as possible while making the bottom part much smaller.&lt;br&gt;&lt;br&gt;
In such language, with just a few words, you can specify pages and their routes, specify which type of authentication you want, define basic entities / data models, describe basic data flow, choose where you want to deploy, implement specific details in React/Node, and let Wasp take care of connecting it all, building it and deploying it.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9h6ak7p8yo8qw9wshbik.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9h6ak7p8yo8qw9wshbik.png" alt="Wasp example code"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check &lt;a href="https://github.com/wasp-lang/wasp/blob/master/examples/tutorials/TodoApp/main.wasp" rel="noopener noreferrer"&gt;here&lt;/a&gt; for the complete example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a language (DSL), aren’t frameworks solving this already?
&lt;/h2&gt;

&lt;p&gt;Frameworks (like e.g. Ruby on Rails or Meteor) are a big inspiration to us.&lt;br&gt;
However, we want to take things one step further - by designing a language specialized for the domain of web apps (a &lt;a href="https://en.wikipedia.org/wiki/Domain-specific_language" rel="noopener noreferrer"&gt;DSL&lt;/a&gt;) we can get rid of a lot of boilerplate and provide a cleaner &amp;amp; simpler developer experience.&lt;/p&gt;

&lt;p&gt;On the other hand, we are not trying to replace everything with Wasp nor think that would make sense - just the opposite, Wasp acts as a “glue” between your React and Node.js code, saving you from the grunt work while allowing you to keep the flexibility of writing your own code.&lt;br&gt;
&lt;strong&gt;The majority of the code is still being written in React and Node.js, with Wasp serving as the backbone of your whole application.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Another benefit of a DSL is that it allows Wasp to &lt;strong&gt;understand the web app’s requirements during the build time and reason about it&lt;/strong&gt; before generating the final code, and this is what we are especially excited about.&lt;/p&gt;

&lt;p&gt;For example, when generating code to be deployed to production, it could pick the most appropriate architecture based on its understanding of the web app and deploy it to serverless or another type of architecture (or even a combination).&lt;br&gt;
Another example would be reusing your data model logic through all the parts of the stack while defining it just once in Wasp.&lt;/p&gt;

&lt;p&gt;DSL opens the potential for optimizations, static analysis, extensibility, and unparalleled ergonomics.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does it work?
&lt;/h2&gt;

&lt;p&gt;Wasp compiler compiles the .wasp/React/Node.js source code into just React/Node.js target code.&lt;br&gt;&lt;br&gt;
Currently, Wasp supports only Javascript, but we plan to add Typescript soon.&lt;br&gt;&lt;br&gt;
Technical note: Wasp compiler is implemented in Haskell.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx1jpeccayn2r2fpjk9yk.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx1jpeccayn2r2fpjk9yk.png" alt="Wasp compilation diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While right now only React and Node.js are supported, we plan to support multiple other technologies in the future.&lt;/p&gt;

&lt;p&gt;Generated code is human readable and can easily be inspected and even ejected if Wasp becomes too limiting.&lt;br&gt;
If not ejecting, there is no need for you to ever look at the generated code - it is generated by Wasp in the background.&lt;/p&gt;

&lt;p&gt;Wasp is used via &lt;code&gt;wasp&lt;/code&gt; CLI - to run wasp project in development, all you need to do is run &lt;code&gt;wasp start&lt;/code&gt;.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frdy6dl01zad4qs4vuyl7.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frdy6dl01zad4qs4vuyl7.png" alt="Wasp CLI output"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where is Wasp now and where is it going?
&lt;/h2&gt;

&lt;p&gt;Our big vision is to move as much of the web app domain knowledge as possible into the Wasp language itself, giving Wasp more power and flexibility.&lt;/p&gt;

&lt;p&gt;Ultimately, since Wasp would have such a deep understanding of the web app's requirements, we could generate a visual editor on top of it - allowing non-developers to participate in development alongside developers.&lt;/p&gt;

&lt;p&gt;Also, Wasp wouldn't be tied to the specific technology but rather support multiple technologies (React/Angular/..., Node/Go/...**.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wasp is currently in Alpha&lt;/strong&gt; and some features are still rough or missing, there are things we haven’t solved yet and others that will probably change as we progress, but &lt;strong&gt;you can try it out and build and deploy web apps&lt;/strong&gt;!&lt;/p&gt;

&lt;h3&gt;
  
  
  What Wasp currently supports:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;✅ full-stack auth (email &amp;amp; password)&lt;/li&gt;
&lt;li&gt;✅ pages &amp;amp; routing&lt;/li&gt;
&lt;li&gt;✅ blurs the line between client &amp;amp; server - define your server actions and queries and call them directly in your client code (RPC)!&lt;/li&gt;
&lt;li&gt;✅ smart caching of server actions and queries (automatic cache invalidation)&lt;/li&gt;
&lt;li&gt;✅ entity (data model) definition with Prisma.io&lt;/li&gt;
&lt;li&gt;✅ ACL on frontend&lt;/li&gt;
&lt;li&gt;✅ importing NPM dependencies &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What is coming:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;⏳ ACL on backend&lt;/li&gt;
&lt;li&gt;⏳ one-click deployment&lt;/li&gt;
&lt;li&gt;⏳ more auth methods (Google, Linkedin, ...**&lt;/li&gt;
&lt;li&gt;⏳ tighter integration of entities with other features&lt;/li&gt;
&lt;li&gt;⏳ themes and layouts&lt;/li&gt;
&lt;li&gt;⏳ support for explicitly defined server API&lt;/li&gt;
&lt;li&gt;⏳ inline JS - the ability to mix JS code with Wasp code!&lt;/li&gt;
&lt;li&gt;⏳ Typescript support&lt;/li&gt;
&lt;li&gt;⏳ server-side rendering&lt;/li&gt;
&lt;li&gt;⏳ Visual Editor&lt;/li&gt;
&lt;li&gt;⏳ support for different languages on the backend&lt;/li&gt;
&lt;li&gt;⏳ richer wasp language with better tooling &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;You can check out our repo&lt;/strong&gt; at &lt;a href="https://github.com/wasp-lang/wasp" rel="noopener noreferrer"&gt;https://github.com/wasp-lang/wasp&lt;/a&gt; and &lt;strong&gt;give it a try&lt;/strong&gt; at &lt;a href="https://wasp-lang.dev/docs" rel="noopener noreferrer"&gt;https://wasp-lang.dev/docs&lt;/a&gt; -&amp;gt; we are always looking for feedback and suggestions on how to shape Wasp!&lt;/p&gt;

&lt;p&gt;We also have a &lt;strong&gt;community&lt;/strong&gt; on &lt;a href="https://discord.com/invite/rzdnErX" rel="noopener noreferrer"&gt;Discord&lt;/a&gt;, where we chat about Wasp-related stuff - join us to see what we are up to, share your opinions or get help with your Wasp project.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>javascript</category>
      <category>wasp</category>
    </item>
  </channel>
</rss>
