<?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: Mihovil Ilakovac</title>
    <description>The latest articles on DEV Community by Mihovil Ilakovac (@infomiho).</description>
    <link>https://dev.to/infomiho</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%2F32926%2F49786321-b17d-4b26-9c21-ba64c896224b.jpeg</url>
      <title>DEV Community: Mihovil Ilakovac</title>
      <link>https://dev.to/infomiho</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/infomiho"/>
    <language>en</language>
    <item>
      <title>[Boost]</title>
      <dc:creator>Mihovil Ilakovac</dc:creator>
      <pubDate>Thu, 26 Mar 2026 11:38:09 +0000</pubDate>
      <link>https://dev.to/infomiho/-426d</link>
      <guid>https://dev.to/infomiho/-426d</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/wasp/whats-the-best-way-to-vibe-code-a-saas-in-2026-9fh" class="crayons-story__hidden-navigation-link"&gt;What's the Best Way to Vibe Code a SaaS in 2026?&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="/vincanger" 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%2F982343%2F99b7a039-13b3-4eb5-820b-da74b180ddf6.jpeg" alt="vincanger profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/vincanger" class="crayons-story__secondary fw-medium m:hidden"&gt;
              vincanger
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                vincanger
                
              
              &lt;div id="story-author-preview-content-3407834" 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="/vincanger" 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%2F982343%2F99b7a039-13b3-4eb5-820b-da74b180ddf6.jpeg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;vincanger&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/whats-the-best-way-to-vibe-code-a-saas-in-2026-9fh" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 26&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/whats-the-best-way-to-vibe-code-a-saas-in-2026-9fh" id="article-link-3407834"&gt;
          What's the Best Way to Vibe Code a SaaS in 2026?
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&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/sass"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sass&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/tooling"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;tooling&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/whats-the-best-way-to-vibe-code-a-saas-in-2026-9fh" 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/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.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;33&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/whats-the-best-way-to-vibe-code-a-saas-in-2026-9fh#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              7&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;
            15 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>webdev</category>
      <category>sass</category>
      <category>ai</category>
      <category>tooling</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Mihovil Ilakovac</dc:creator>
      <pubDate>Mon, 13 Oct 2025 13:47:17 +0000</pubDate>
      <link>https://dev.to/infomiho/-3gig</link>
      <guid>https://dev.to/infomiho/-3gig</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>[Boost]</title>
      <dc:creator>Mihovil Ilakovac</dc:creator>
      <pubDate>Tue, 29 Apr 2025 12:02:40 +0000</pubDate>
      <link>https://dev.to/infomiho/-l2k</link>
      <guid>https://dev.to/infomiho/-l2k</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/wasp/from-0-to-10k-how-open-saas-became-the-free-boilerplate-devs-love-45hb" class="crayons-story__hidden-navigation-link"&gt;From 0 to 10K ⭐: How Open SaaS became the Free Boilerplate Devs Love&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="/vincanger" 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%2F982343%2F99b7a039-13b3-4eb5-820b-da74b180ddf6.jpeg" alt="vincanger profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/vincanger" class="crayons-story__secondary fw-medium m:hidden"&gt;
              vincanger
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                vincanger
                
              
              &lt;div id="story-author-preview-content-2429632" 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="/vincanger" 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%2F982343%2F99b7a039-13b3-4eb5-820b-da74b180ddf6.jpeg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;vincanger&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/from-0-to-10k-how-open-saas-became-the-free-boilerplate-devs-love-45hb" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Apr 29 '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/from-0-to-10k-how-open-saas-became-the-free-boilerplate-devs-love-45hb" id="article-link-2429632"&gt;
          From 0 to 10K ⭐: How Open SaaS became the Free Boilerplate Devs Love
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&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/from-0-to-10k-how-open-saas-became-the-free-boilerplate-devs-love-45hb" 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/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&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/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;223&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/from-0-to-10k-how-open-saas-became-the-free-boilerplate-devs-love-45hb#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              37&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;
            7 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>developers</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Go go Vinny!</title>
      <dc:creator>Mihovil Ilakovac</dc:creator>
      <pubDate>Wed, 16 Apr 2025 09:00:45 +0000</pubDate>
      <link>https://dev.to/infomiho/go-go-vinny-n36</link>
      <guid>https://dev.to/infomiho/go-go-vinny-n36</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/wasp/a-structured-workflow-for-vibe-coding-full-stack-apps-352l" class="crayons-story__hidden-navigation-link"&gt;A Structured Workflow for "Vibe Coding" Full-Stack Apps&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="/vincanger" 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%2F982343%2F99b7a039-13b3-4eb5-820b-da74b180ddf6.jpeg" alt="vincanger profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/vincanger" class="crayons-story__secondary fw-medium m:hidden"&gt;
              vincanger
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                vincanger
                
              
              &lt;div id="story-author-preview-content-2409125" 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="/vincanger" 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%2F982343%2F99b7a039-13b3-4eb5-820b-da74b180ddf6.jpeg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;vincanger&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/a-structured-workflow-for-vibe-coding-full-stack-apps-352l" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Apr 16 '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/a-structured-workflow-for-vibe-coding-full-stack-apps-352l" id="article-link-2409125"&gt;
          A Structured Workflow for "Vibe Coding" Full-Stack Apps
        &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/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/fullstack"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;fullstack&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;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/wasp/a-structured-workflow-for-vibe-coding-full-stack-apps-352l" 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/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&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/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;425&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/a-structured-workflow-for-vibe-coding-full-stack-apps-352l#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              47&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;
            10 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>ai</category>
      <category>fullstack</category>
      <category>webdev</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Mihovil Ilakovac</dc:creator>
      <pubDate>Tue, 04 Mar 2025 19:37:58 +0000</pubDate>
      <link>https://dev.to/infomiho/-2dfn</link>
      <guid>https://dev.to/infomiho/-2dfn</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/wasp" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__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%2Forganization%2Fprofile_image%2F3369%2Fc86918f8-76a9-4b01-accf-cc257f9ee56f.png" alt="Wasp" width="151" height="151"&gt;
      &lt;div class="ltag__link__user__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%2F207293%2F1864d1f9-7cd9-4b83-a5b5-b02813ec1aa0.jpg" alt="" width="512" height="512"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/wasp/meet-marko-saric-co-founder-of-privacy-friendly-plausible-analytics-3nm8" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Meet Marko Saric, Co-founder of Privacy-friendly Plausible Analytics&lt;/h2&gt;
      &lt;h3&gt;Milica Maksimovic for Wasp ・ Mar 4&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#opensource&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#buildinpublic&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#privacy&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>opensource</category>
      <category>buildinpublic</category>
      <category>privacy</category>
      <category>webdev</category>
    </item>
    <item>
      <title>OG image generator, high quality output</title>
      <dc:creator>Mihovil Ilakovac</dc:creator>
      <pubDate>Thu, 27 Feb 2025 11:49:20 +0000</pubDate>
      <link>https://dev.to/infomiho/og-image-generator-high-quality-output-262h</link>
      <guid>https://dev.to/infomiho/og-image-generator-high-quality-output-262h</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/wasp" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__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%2Forganization%2Fprofile_image%2F3369%2Fc86918f8-76a9-4b01-accf-cc257f9ee56f.png" alt="Wasp" width="151" height="151"&gt;
      &lt;div class="ltag__link__user__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%2F982343%2F99b7a039-13b3-4eb5-820b-da74b180ddf6.jpeg" alt="" width="460" height="460"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/wasp/i-built-my-saas-app-fast-now-its-marketing-time-657" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;I built my SaaS app fast. Now it's marketing time.&lt;/h2&gt;
      &lt;h3&gt;vincanger for Wasp ・ Feb 26&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#ai&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#react&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>webdev</category>
      <category>ai</category>
      <category>javascript</category>
      <category>react</category>
    </item>
    <item>
      <title>Vinny built CoverletterGPT to $500/month, a good read</title>
      <dc:creator>Mihovil Ilakovac</dc:creator>
      <pubDate>Wed, 15 Jan 2025 13:57:56 +0000</pubDate>
      <link>https://dev.to/infomiho/vinny-built-coverlettergpt-to-500month-a-good-read-56gb</link>
      <guid>https://dev.to/infomiho/vinny-built-coverlettergpt-to-500month-a-good-read-56gb</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/wasp" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__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%2Forganization%2Fprofile_image%2F3369%2Fc86918f8-76a9-4b01-accf-cc257f9ee56f.png" alt="Wasp" width="151" height="151"&gt;
      &lt;div class="ltag__link__user__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%2F982343%2F99b7a039-13b3-4eb5-820b-da74b180ddf6.jpeg" alt="" width="460" height="460"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/wasp/can-i-build-market-a-saas-app-to-100-in-1-month-36e7" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Can I build &amp;amp; market a SaaS app to $100 in 1 month?&lt;/h2&gt;
      &lt;h3&gt;vincanger for Wasp ・ Jan 15&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#saas&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#marketing&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#ai&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>career</category>
      <category>productivity</category>
      <category>saas</category>
    </item>
    <item>
      <title>Wasp x Supabase: Smokin’ Hot Full-Stack Combo 🌶️ 🔥</title>
      <dc:creator>Mihovil Ilakovac</dc:creator>
      <pubDate>Wed, 08 May 2024 10:38:26 +0000</pubDate>
      <link>https://dev.to/wasp/wasp-x-supabase-smokin-hot-full-stack-combo-ioe</link>
      <guid>https://dev.to/wasp/wasp-x-supabase-smokin-hot-full-stack-combo-ioe</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; In this post I will tell you about the hyper-productive stack for painlessly building full-stack apps with React &amp;amp; Node.js - Supabase and Wasp! We combined these two technologies to get auth, async jobs, full-stack type safety, managed DB, and managed storage out of the box.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hey, I’m Miho! 👋
&lt;/h3&gt;

&lt;p&gt;I’m a senior full-stack dev and I’ve been in the business of dreaming up and creating projects for almost 10 years. Almost on a daily basis I stumble upon a problem and want to build an app to fix it! That’s why I had to get good at doing it quickly, with as little hassle as possible.&lt;/p&gt;

&lt;p&gt;After having used both Wasp and Supabase for a while, combining them together seemed like a no brainer to me. Turns out I was right!&lt;/p&gt;

&lt;h2&gt;
  
  
  No theory, we’ll build an app!
&lt;/h2&gt;

&lt;p&gt;We've cooked up something interesting: a greeting cards generator that doesn’t just work, but it’s also infinitely &lt;em&gt;creative!&lt;/em&gt; Leveraging open-source AI models — yes, the shiny new Llama 3 and super speedy SDXL-Lighting — we've brought this idea to life.&lt;/p&gt;

&lt;p&gt;Need a visual? Here's a quick sketch I made (good thing I got that tablet!):&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%2F88b9d1bdqr3ijfesn7j2.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%2F88b9d1bdqr3ijfesn7j2.jpg" alt="Sketch of the different app components, some of which are the Wasp full stack app and Supabase DB and Storage"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And this is how our app looks once it's all polished and ready to go: &lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1782416172111024600-118" src="https://platform.twitter.com/embed/Tweet.html?id=1782416172111024600"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1782416172111024600-118');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1782416172111024600&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;Check out &lt;a href="https://lazy-greeting-cards-client.fly.dev/" rel="noopener noreferrer"&gt;the deployed version of our app&lt;/a&gt; — sign in with Google and get some sweet cards!&lt;/p&gt;

&lt;h3&gt;
  
  
  Llama has the last word
&lt;/h3&gt;

&lt;p&gt;In our app, multiple models collaborate to produce a nice looking result.&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%2Fcalgh9rgnpegrc4e5yjm.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%2Fcalgh9rgnpegrc4e5yjm.jpg" alt="Funny image of a llama telling a painter what to paint"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It works like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User gives us a topic&lt;/li&gt;
&lt;li&gt;Llama 3 generates the greeting card text (”text”)&lt;/li&gt;
&lt;li&gt;… it also describes some artwork that fits the text (”image prompt”)&lt;/li&gt;
&lt;li&gt;Stable Diffusion draws the artwork&lt;/li&gt;
&lt;li&gt;???&lt;/li&gt;
&lt;li&gt;Profit!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Imagine requesting a greeting card for your three-year-old, bossy, red-dress-loving llama (because who wouldn't?!).&lt;/p&gt;

&lt;p&gt;You'd get something adorable like this! 🦙:&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%2Ftt9bouh1tb1t1gvt8izb.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftt9bouh1tb1t1gvt8izb.jpeg" alt="Prompt: “a greeting card for my llamas 3rd birthday, it’s quite bossy and loves wearing red clothes”"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Prompt: “a greeting card for my llamas 3rd birthday, it’s quite bossy and loves wearing red clothes”&lt;/p&gt;
&lt;/blockquote&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%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;If you find this post helpful, &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="support us"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/wasp-lang/wasp" class="ltag_cta ltag_cta--branded" rel="noopener noreferrer"&gt;⭐️ Thanks For Your Support 🙏&lt;/a&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  How we pulled it off
&lt;/h2&gt;

&lt;p&gt;That’s quite a cool greeting card IMHO, but we need a bit more to make this a proper app that works for our users.&lt;/p&gt;

&lt;h3&gt;
  
  
  We want to login with Google
&lt;/h3&gt;

&lt;p&gt;We used &lt;a href="https://wasp-lang.dev/" rel="noopener noreferrer"&gt;Wasp&lt;/a&gt;’s built-in &lt;a href="https://wasp-lang.dev/docs/auth/overview" rel="noopener noreferrer"&gt;auth&lt;/a&gt; which makes your auth totally yours and independent of any 3rd party service. Under the hood, it uses &lt;a href="https://lucia-auth.com/" rel="noopener noreferrer"&gt;Lucia&lt;/a&gt; and &lt;a href="https://arctic.js.org/" rel="noopener noreferrer"&gt;Arctic&lt;/a&gt; to give you email, username and multiple OAuth providers out of the box. &lt;/p&gt;

&lt;p&gt;We didn’t need to work too hard beyond this code to set it up:&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%2Fq5y0hm80oipqcuozhdjc.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%2Fq5y0hm80oipqcuozhdjc.png" alt="Wasp config file code"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Wasp config file code&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  We want to split up the card making process
&lt;/h3&gt;

&lt;p&gt;Using Wasp's async jobs, we've split the card creation process into manageable steps, so users aren't left in the dark. They get playful updates like "Warming up the AI" and "Drawing the image" — making the wait a bit more bearable 🐻&lt;/p&gt;

&lt;p&gt;These tasks are managed by &lt;a href="https://github.com/timgit/pg-boss/" rel="noopener noreferrer"&gt;pg-boss&lt;/a&gt; behind the scenes (which is based on PostgreSQL) and, oh look, that seamlessly connects to...&lt;/p&gt;

&lt;h3&gt;
  
  
  Managed PostgreSQL
&lt;/h3&gt;

&lt;p&gt;It was a great experience using &lt;a href="https://supabase.com/" rel="noopener noreferrer"&gt;Supabase&lt;/a&gt;’s rock-solid PostgreSQL database for this app. The DX around that product is phenomenal: viewing and managing the DB data was a lifesaver when you don’t want to craft your own admin panel from scratch.&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%2Flyk8ns3yii8ozs3u2kbv.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%2Flyk8ns3yii8ozs3u2kbv.png" alt="Screenshot of the Supabase table editor"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Table Editor is great for quick admin work on the DB&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Modern apps need modern storage
&lt;/h3&gt;

&lt;p&gt;And for storage, we opted for Supabase’s &lt;a href="https://supabase.com/blog/s3-compatible-storage?utm_source=postmark&amp;amp;utm_medium=email&amp;amp;utm_campaign=launch-week-11" rel="noopener noreferrer"&gt;S3-compatible storage&lt;/a&gt; option. This means our app doesn’t rely on having dedicated disk storage — making it more portable and more easily scalable.&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%2Fswf5v97w3u3om8jhoqd8.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%2Fswf5v97w3u3om8jhoqd8.png" alt="Overview of the greeting card images"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Overview of the greeting card images&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://replicate.com/meta/meta-llama-3-70b-instruct" rel="noopener noreferrer"&gt;Llama 3-70B&lt;/a&gt; model
&lt;/h3&gt;

&lt;p&gt;Meta’s newest Llama3 is an open-source contender to GPT-4 (the 405B model that’s still training).&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1782483699449332144-196" src="https://platform.twitter.com/embed/Tweet.html?id=1782483699449332144"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1782483699449332144-196');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1782483699449332144&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;The text it produced was always usable and funny most of the time. I felt like it didn’t need that much prompt tweaking to get good results.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prompts we used&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Writing the greeting card:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;

&lt;span class="nx"&gt;Write&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;greeting&lt;/span&gt; &lt;span class="nx"&gt;card&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;following&lt;/span&gt; &lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;topic&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;Make&lt;/span&gt; &lt;span class="nx"&gt;it&lt;/span&gt; &lt;span class="nx"&gt;clever&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="nx"&gt;Return&lt;/span&gt; &lt;span class="nx"&gt;it&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;plain&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;no&lt;/span&gt; &lt;span class="nx"&gt;quotes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;no&lt;/span&gt; &lt;span class="nx"&gt;extra&lt;/span&gt; &lt;span class="nx"&gt;syntax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="nx"&gt;Return&lt;/span&gt; &lt;span class="nx"&gt;only&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;greeting&lt;/span&gt; &lt;span class="nx"&gt;card&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;Max&lt;/span&gt; &lt;span class="nx"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;For example, if we used the topic &lt;strong&gt;Laughing about a thing&lt;/strong&gt; we’d get the following result: “Laughter is the best medicine, unless you have health insurance, then that's probably better.”&lt;/p&gt;

&lt;p&gt;Getting a usable image prompt:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;

&lt;span class="nx"&gt;Based&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ll provide, give me a nice artwork to go alongside it.
Describe it in a way of a short list of features of the artwork.
Use descriptive language so someone can paint it.
Only respond with the description, no extra syntax. Max words: 30

Context: &amp;lt;original_topic&amp;gt;

Text:
&amp;lt;text&amp;gt;


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

&lt;/div&gt;

&lt;p&gt;For the example above, we’d get the following image prompt: “Whimsical illustration of a smiling pill bottle surrounded by swirling laughter bubbles, with a subtle medical cross in the background, set against a warm, sunny yellow sky.”&lt;/p&gt;

&lt;p&gt;Now, why did we do the second step? Just compare the images generated directly from the “text” and the “image prompt”:&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%2F562chgclebakt1vyiq6m.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%2F562chgclebakt1vyiq6m.png" alt="Image generated with the text as prompt"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Using the “text” directly&lt;/p&gt;
&lt;/blockquote&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%2Fsmb20h6us30g6siumohl.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%2Fsmb20h6us30g6siumohl.png" alt="Image generated with the special image prompt"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Using the “image prompt” generated by Llama 3&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As you can see, the version based on the image prompt aligns much better aesthetically with greeting card vibes—colorful and friendly.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://replicate.com/bytedance/sdxl-lightning-4step" rel="noopener noreferrer"&gt;SDXL-Lighting&lt;/a&gt; (4 step variant) model
&lt;/h3&gt;

&lt;p&gt;Bytedance produced this model based on Stable Diffusion XL and made it &lt;em&gt;super fast&lt;/em&gt;. The greeting card images are created in 1-2 seconds. The images remind me of Midjourney’s quality which means the model is doing a good job.&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%2Fk9rla0gdcojylfzgtxt4.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%2Fk9rla0gdcojylfzgtxt4.png" alt="Example SDXL-Lighting image 1: abstract art"&gt;&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%2F24gutoemdtr4oso82myy.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%2F24gutoemdtr4oso82myy.png" alt="Example SDXL-Lighting image 2: image of a cat"&gt;&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%2Fu51c3m48qrwsewk3usq9.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%2Fu51c3m48qrwsewk3usq9.png" alt="Example SDXL-Lighting image 3: image of an astronaut"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Cost and Time to Generate
&lt;/h3&gt;

&lt;p&gt;We used &lt;a href="https://replicate.com/" rel="noopener noreferrer"&gt;Replicate&lt;/a&gt; to run the models and the cost so far is 26 cents for 90 cards — which means it’s less than a third of a cent per card!&lt;/p&gt;

&lt;p&gt;The combination of open-source models, minimal token use, and quick image generation keeps costs impressively low.&lt;/p&gt;

&lt;p&gt;Producing a single card takes under 5 seconds which helps if you are in a hurry 🙂&lt;/p&gt;

&lt;h2&gt;
  
  
  Give It a Try!
&lt;/h2&gt;

&lt;p&gt;Check out the beautiful UI crafted by &lt;a href="https://ui.shadcn.com/" rel="noopener noreferrer"&gt;ShadCN&lt;/a&gt; over at &lt;a href="https://lazy-greeting-cards-client.fly.dev/" rel="noopener noreferrer"&gt;the deployed version of our app&lt;/a&gt; — sign in with Google and get some sweet cards! Plus, the entire project is open source. Grab the code from &lt;a href="https://github.com/wasp-lang/lazy-card-generator" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>supabase</category>
      <category>wasp</category>
    </item>
    <item>
      <title>Building a full-stack app for learning Italian: Supabase vs. Wasp</title>
      <dc:creator>Mihovil Ilakovac</dc:creator>
      <pubDate>Thu, 16 Mar 2023 13:51:00 +0000</pubDate>
      <link>https://dev.to/wasp/building-a-full-stack-app-for-learning-italian-supabase-vs-wasp-3djg</link>
      <guid>https://dev.to/wasp/building-a-full-stack-app-for-learning-italian-supabase-vs-wasp-3djg</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What to expect
&lt;/h3&gt;

&lt;p&gt;In this blog post, I will explain how I created the &lt;a href="https://phrasetutor.com/" rel="noopener noreferrer"&gt;Phrase Tutor&lt;/a&gt; app for learning Italian phrases using two different technologies. I will share some code snippets to show what was required to build the app with both Wasp and Supabase.&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%2F1488dvuzgd87jw23jhbr.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%2F1488dvuzgd87jw23jhbr.gif" alt="Phrase Tutor’s front-end"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a senior full-stack developer with experience in building many side-projects, I prefer a quick development cycle. I enjoy turning ideas into POCs in just a few days or even hours.&lt;/p&gt;

&lt;p&gt;We will examine how each technology can help when building a full-stack app and where &lt;a href="https://wasp-lang.dev/" rel="noopener noreferrer"&gt;Wasp&lt;/a&gt; and &lt;a href="https://supabase.com/" rel="noopener noreferrer"&gt;Supabase&lt;/a&gt; excel.&lt;/p&gt;

&lt;h3&gt;
  
  
  I wanted to learn Italian fast
&lt;/h3&gt;

&lt;p&gt;Whenever I travel abroad, I enjoy imagining what it would be like to live in that place. For instance, I usually don't like taking crowded public transportation, but for some reason, it brings me joy when I do it in a foreign country. It's all about the feeling that I'm living there. One of the most important things for me to fully experience the culture is to learn the language or, at the very least, be able to not speak English all the time.&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%2Fb2filg2ntj5qvh4cb9oc.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%2Fb2filg2ntj5qvh4cb9oc.gif" alt="Pretending to be Italian"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My girlfriend and I were planning a trip to Italy, and I wanted to learn some Italian. I thought about what would be the easiest way to learn as much as possible with the least amount of effort. I decided that learning the top 100 Italian phrases would be a good start. I had a week to do it, and learning 100 phrases seemed doable if I practiced every day.&lt;/p&gt;

&lt;h3&gt;
  
  
  The learning method
&lt;/h3&gt;

&lt;p&gt;In high school, I had a system for learning historical facts and dates quickly called "focusing on things you don’t know".&lt;/p&gt;

&lt;p&gt;Here's how it works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Gather a pool of facts you want to learn (e.g. "When did WWI start?" - "1914").&lt;/li&gt;
&lt;li&gt;Ask yourself each question in the pool.&lt;/li&gt;
&lt;li&gt;If you know the answer, remove the fact from the pool.&lt;/li&gt;
&lt;li&gt;If you don't know the answer, keep it in the pool.&lt;/li&gt;
&lt;li&gt;Repeat with the smaller pool until there are no more facts left.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I made a small app for this and shared it with my classmates, but it didn't go further than that.&lt;/p&gt;

&lt;p&gt;Now, I want to use the same method to learn Italian phrases for my trip. So, as a better developer now, I'll make a proper app and host it somewhere 🙂&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the Phrase Tutor app
&lt;/h2&gt;

&lt;p&gt;We will create an app that follows the method described above. The app will show you a phrase and you can tell it if you know the translation or not by selecting "I knew it" or "I didn't know 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%2Fdg6siotghhb5y55x0r2v.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%2Fdg6siotghhb5y55x0r2v.png" alt="How the learning in the app should work"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The app will keep track of your answers and suggest which phrases you should learn next 🕵️&lt;/p&gt;

&lt;p&gt;I’ve built the app twice: first with Supabase and then with Wasp. Supabase is a well-rounded open-source Backend as a Service (BaaS) product that adds superpowers to your front-end apps. On the other hand, Wasp is an open-source framework for building full-stack apps that helps to keep the boilerplate low. Let’s see how they compare.&lt;/p&gt;

&lt;h3&gt;
  
  
  Initial Supabase version
&lt;/h3&gt;

&lt;p&gt;When I made the initial version, I worked heavily with Vue.js, which I used to create the first version of the Phrase Tutor app. I started by collecting some phrases. I searched on Google for "best Italian phrases to learn" and came across an article titled "100 Italian phrases to learn." (After extracting the phrases from the HTML, I found out that there were only 96 phrases, but that was still good enough for me.)&lt;/p&gt;

&lt;p&gt;The initial app contained the phrases in a &lt;a href="https://github.com/infomiho/phrase-tutor-supabase/blob/master/src/phrases/italian.json" rel="noopener noreferrer"&gt;JSON file&lt;/a&gt; that the frontend loaded. It was completely static, but it worked.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"group"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"general"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"translations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Yes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"it"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Si"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I put it on Cloudflare Pages and it went live.&lt;/p&gt;

&lt;p&gt;I showed it to my girlfriend, but she didn't like some of the phrases I used. If only I had a backend with a database to edit the phrases. Then I had an idea: let's add a database with &lt;a href="https://supabase.com/" rel="noopener noreferrer"&gt;Supabase&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Supabase is a managed backend solution that provides a lot of free stuff: a PostgreSQL database and social authentication among other things.&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%2Ftjp8rk8h4cejl7m1cobr.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%2Ftjp8rk8h4cejl7m1cobr.png" alt="Phrase Tutor built with Supabase"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I set up the database tables using the Supabase UI which was pretty straightforward.&lt;/p&gt;

&lt;p&gt;The table I needed only had a few fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;phrases&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;bigint&lt;/span&gt;  &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;group&lt;/span&gt; &lt;span class="nb"&gt;character&lt;/span&gt; &lt;span class="nb"&gt;varying&lt;/span&gt;  &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;translations_en&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;  &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;translations_it&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;  &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I had to seed the database with some SQL. Executing SQL statements is easy with the use of Supabase’s UI. You just log in, open the SQL editor and paste in the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;phrases&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;"group"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;translations_en&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;translations_it&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&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="s1"&gt;'general'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'Yes'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'Si'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;phrases&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;"group"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;translations_en&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;translations_it&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'general'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'No'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'No'&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;Integrating Supabase into my existing front-end app was simple using their &lt;a href="https://github.com/infomiho/phrase-tutor-supabase/blob/master/src/services/supabase.ts#L4" rel="noopener noreferrer"&gt;Javascript SDK&lt;/a&gt;. If you're familiar with Firebase, it should feel similar. Essentially, you build your SQL queries on the frontend and use the resulting data in your app.&lt;/p&gt;

&lt;p&gt;Using the SDK felt pretty straightforward and I could get what I wanted out of the database without much hassle.&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;phrases&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And just like that, my static Vue.js app had a database to rely on 🎉&lt;/p&gt;

&lt;p&gt;Adding the login with Google was a matter of enabling it in Supabase UI and setting up the Client ID and Client Secret variables. In order to trigger the login process with Google, I &lt;a href="https://github.com/infomiho/phrase-tutor-supabase/blob/master/src/stores/user.ts#L9" rel="noopener noreferrer"&gt;once again&lt;/a&gt; relied on their Javascript SDK.&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="nx"&gt;supabase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signInWithOAuth&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;google&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Awesome! I'm glad that I can now edit the phrases and that there is a login feature that I plan to use later.&lt;/p&gt;

&lt;p&gt;In the future, I have plans to add more languages to the app and also allow registered users to contribute new phrases and translations. I believe this will make the app more useful and engaging for language learners.&lt;/p&gt;

&lt;p&gt;And just like that, my app went from a pure static app to an app with a database and Google login 🤯&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Check out the deployed app written with Vue.js and Supabase: &lt;a href="https://phrase-tutor.pages.dev/" rel="noopener noreferrer"&gt;https://phrase-tutor.pages.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;View the source &lt;a href="https://github.com/infomiho/phrase-tutor-supabase" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Joining Wasp and dogfooding it
&lt;/h3&gt;

&lt;p&gt;Some background before the second part: I started working at Wasp earlier this year. I'm really happy to work on a technology that solves a problem I care about: when I do side-projects, I dislike writing the same dull parts every time from scratch. I copy and paste from my previous side projects, but eventually, the code snippets become old and outdated.&lt;/p&gt;

&lt;p&gt;Naturally, I wanted to test out Wasp by rewriting one of my side projects. I decided to see how Wasp could work with the Phrase Tutor project.&lt;/p&gt;

&lt;p&gt;Wasp works by having an easy-to-understand config file called &lt;code&gt;main.wasp&lt;/code&gt; which coordinates your pieces of client and server functionalities. Its main purpose is to keep you productive and focused on writing interesting bits. It feels pretty much like using a web framework that covers your whole app.&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%2F3lwd2tv4fer2yp4qub45.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%2F3lwd2tv4fer2yp4qub45.png" alt="Phrase Tutor built with Wasp"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's begin by creating the data models. Wasp uses Prisma under the hood to communicate with your database, which makes it easy to manage your database without worrying about the details. This is just one of the many choices the framework made for me, and I appreciate the feeling of using a setup that works.&lt;/p&gt;

&lt;p&gt;I had to first declare all of the entities I needed with Prisma PSL in the Wasp config file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;entity&lt;/span&gt; &lt;span class="nx"&gt;Phrase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;psl&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="nx"&gt;Int&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;id&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;autoincrement&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="nx"&gt;group&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;
  &lt;span class="nx"&gt;phrase&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;
  &lt;span class="nx"&gt;translations&lt;/span&gt; &lt;span class="nx"&gt;Translation&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="nx"&gt;psl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;entity&lt;/span&gt; &lt;span class="nx"&gt;Language&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;psl&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="nx"&gt;Int&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;id&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;autoincrement&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;unique&lt;/span&gt;
  &lt;span class="nx"&gt;emoji&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;
  &lt;span class="nx"&gt;translations&lt;/span&gt; &lt;span class="nx"&gt;Translation&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="nx"&gt;psl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;entity&lt;/span&gt; &lt;span class="nx"&gt;Translation&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;psl&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="nx"&gt;Int&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;id&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;autoincrement&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="nx"&gt;phraseId&lt;/span&gt; &lt;span class="nx"&gt;Int&lt;/span&gt;
  &lt;span class="nx"&gt;languageId&lt;/span&gt; &lt;span class="nx"&gt;Int&lt;/span&gt;
  &lt;span class="nx"&gt;translation&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;
  &lt;span class="nx"&gt;phrase&lt;/span&gt; &lt;span class="nx"&gt;Phrase&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;relation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;phraseId&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;references&lt;/span&gt;&lt;span class="p"&gt;:&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="nx"&gt;onDelete&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Cascade&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;language&lt;/span&gt; &lt;span class="nx"&gt;Language&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;relation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;languageId&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;references&lt;/span&gt;&lt;span class="p"&gt;:&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="nx"&gt;onDelete&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Cascade&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;psl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;I'm using a PostgreSQL database again, and you can see that the field definitions are similar.&lt;/p&gt;

&lt;p&gt;I improved the data schema a bit by defining three tables instead of one. I separated the concept of a &lt;code&gt;Phrase&lt;/code&gt; from the concepts of &lt;code&gt;Language&lt;/code&gt; and &lt;code&gt;Translation&lt;/code&gt;. This will make it easier to add new languages in the future.&lt;/p&gt;

&lt;p&gt;I added some phrases to the database using Prisma and a &lt;a href="https://github.com/infomiho/phrase-tutor-wasp/blob/master/src/server/actions.js#L1" rel="noopener noreferrer"&gt;Wasp action&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;seedItalianPhrases&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
       &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;general&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;translations_en&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Yes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;translations_it&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Si&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;for &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;phrase&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;seedPhrases&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;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Phrase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="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;Let’s now look at what I needed to do to get the data flowing from the backend to my React app.&lt;/p&gt;

&lt;p&gt;First, I declared a query in my Wasp config file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="n"&gt;phraseTutor&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="n"&gt;query&lt;/span&gt; &lt;span class="n"&gt;fetchAllPhrases&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;fn:&lt;/span&gt; &lt;span class="n"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;getAllPhrases&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="s"&gt;"@server/queries.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;entities:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Phrase&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;Then I wrote the code for my backend to fetch the phrases. You’ll notice it’s quite similar to the code I wrote for fetching phrases with the Supabase SDK, but I had to include the &lt;code&gt;translations&lt;/code&gt; relation since we now have multiple tables.&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="c1"&gt;// My query got the Prisma entity through the context parameter&lt;/span&gt;
&lt;span class="c1"&gt;// which I just used to fetch all the phrases&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getAllPhrases&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&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;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Phrase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;translations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And lastly, I could just import the query into my React app. It’s set up in a way that it handles cache invalidation automatically, one less thing to worry about, which is awesome 😎&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="c1"&gt;// Wasp relies on React Query in the background&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;phrases&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isLoading&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fetchAllPhrases&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s also add support for Google auth for our app. It involves declaring you want it in the Wasp file, adding some env variables and using it in the React application.&lt;/p&gt;

&lt;p&gt;We declare it to the Wasp file by adding the &lt;code&gt;google&lt;/code&gt; key under &lt;code&gt;auth&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="nx"&gt;phraseTutor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;userEntity&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;externalAuthEntity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SocialUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Define we want the Google auth&lt;/span&gt;
      &lt;span class="nl"&gt;google&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Optionally, we can adjust what is saved from the user's data&lt;/span&gt;
        &lt;span class="na"&gt;getUserFieldsFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getUserFields&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@server/auth/google.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;onAuthFailedRedirectTo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Some of the entities needed for auth&lt;/span&gt;
&lt;span class="nx"&gt;entity&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;psl&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="nx"&gt;Int&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;id&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;autoincrement&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;unique&lt;/span&gt;
  &lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;
  &lt;span class="nx"&gt;profilePicture&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;
  &lt;span class="nx"&gt;externalAuthAssociations&lt;/span&gt; &lt;span class="nx"&gt;SocialUser&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="nx"&gt;createdAt&lt;/span&gt; &lt;span class="nx"&gt;DateTime&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="nx"&gt;psl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;entity&lt;/span&gt; &lt;span class="nx"&gt;SocialUser&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;psl&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt;          &lt;span class="nx"&gt;Int&lt;/span&gt;       &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;id&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;autoincrement&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="nx"&gt;provider&lt;/span&gt;    &lt;span class="nb"&gt;String&lt;/span&gt;
  &lt;span class="nx"&gt;providerId&lt;/span&gt;  &lt;span class="nb"&gt;String&lt;/span&gt;
  &lt;span class="nx"&gt;user&lt;/span&gt;        &lt;span class="nx"&gt;User&lt;/span&gt;      &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;relation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;references&lt;/span&gt;&lt;span class="p"&gt;:&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="nx"&gt;onDelete&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Cascade&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;userId&lt;/span&gt;      &lt;span class="nx"&gt;Int&lt;/span&gt;
  &lt;span class="nx"&gt;createdAt&lt;/span&gt;   &lt;span class="nx"&gt;DateTime&lt;/span&gt;  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;@@&lt;/span&gt;&lt;span class="nd"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;providerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nx"&gt;psl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And … that’s it. We can now use the Google auth in our frontend 🎉&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;signInUrl&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;googleSignInUrl&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@wasp/auth/helpers/Google&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&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="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useAuth&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Writing a full-stack React and Express.js with Wasp felt like a guided experience; I didn't have to focus too hard on the dev tooling, building, or deploying.&lt;/p&gt;

&lt;p&gt;Instead, I could focus on the logic needed for Phrase Tutor to work and just run &lt;code&gt;wasp start&lt;/code&gt; most of the time. I did need to write some extra code to get everything running, but I'm free to customize this code however I want.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Check out the deployed project built with Wasp: &lt;a href="https://phrasetutor.com/" rel="noopener noreferrer"&gt;https://phrasetutor.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;View the source &lt;a href="https://github.com/infomiho/phrase-tutor-wasp" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Let's compare some of the features
&lt;/h3&gt;

&lt;p&gt;I want to compare the features of Supabase and Wasp. It's good to think about different ways to do things and their pros and cons.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Supabase&lt;/th&gt;
&lt;th&gt;Wasp&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Getting data from the API&lt;/td&gt;
&lt;td&gt;Use the Supabase JS SDK to query database tables&lt;/td&gt;
&lt;td&gt;Declare query in Wasp config and use Prisma JS SDK to implement it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Custom business logic&lt;/td&gt;
&lt;td&gt;Writing custom PostgreSQL procedures or by writing edge functions&lt;/td&gt;
&lt;td&gt;Declare actions in the Wasp file and write server-side JS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Defining the database schema&lt;/td&gt;
&lt;td&gt;Visual editor or by CREATE TABLE query&lt;/td&gt;
&lt;td&gt;By code - edit Prisma schema and commit changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auth&lt;/td&gt;
&lt;td&gt;Enable in UI&lt;/td&gt;
&lt;td&gt;Enable it in the Wasp file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment&lt;/td&gt;
&lt;td&gt;Supabase managed instance or self-host it&lt;/td&gt;
&lt;td&gt;Deploy anywhere, support for &lt;a href="https://fly.io" rel="noopener noreferrer"&gt;https://fly.io&lt;/a&gt; one line deployment&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;With Supabase, I liked how familiar the SDK felt and their UI made it easy to configure parts of my backend. I didn’t need to think about deploying Supabase since I used their hosted version, but it did get paused after 1 week of inactivity on the free tier.&lt;/p&gt;

&lt;p&gt;On the other hand, Wasp felt like the glue for my React + Express.js + Prisma app and I needed to write more code to get things done. It felt more explicit because I wrote code closer to what I would normally write. I deployed it to &lt;a href="https://fly.io" rel="noopener noreferrer"&gt;fly.io&lt;/a&gt; with the Wasp command &lt;code&gt;wasp deploy fly launch&lt;/code&gt; and it’s now live on &lt;a href="https://phrasetutor.com/" rel="noopener noreferrer"&gt;https://phrasetutor.com&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;h3&gt;
  
  
  It's all about the use case
&lt;/h3&gt;

&lt;p&gt;Choosing the right solution for your needs can be difficult. That's why it's important to try out different options and see how they work for you. In this case, I compared two options: Supabase and Wasp.&lt;/p&gt;

&lt;p&gt;Supabase is a great choice if you want a well-rounded open-source BaaS product that adds superpowers to your front-end apps. It provides a lot of free stuff, such as a PostgreSQL database and social authentication, which can make development easier and faster. It also has a nice SDK and UI that the end user can use to easily define their app's configuration.&lt;/p&gt;

&lt;p&gt;Wasp is an open-source framework for building full-stack apps that helps out with keeping the boilerplate low. It is a bit more explicit about some things, such as defining your auth entities, but that can be a plus when you have more advanced use cases. By using Wasp as the glue for your full-stack application, you can have the best of both worlds: a development and production setup that works out of the box while still allowing you to develop your app any way you like.&lt;/p&gt;

&lt;p&gt;In the case of Phrase Tutor, I liked working with both Supabase and Wasp. I did, however, get a different feeling from working with the two technologies. With Supabase I felt like my front-end app got instant superpowers and it now has a database and login, which was nice considering the effort I had to put in. But now I had a black-box dependency that I needed to build around.&lt;/p&gt;

&lt;p&gt;When I used Wasp to rebuild Phrase Tutor, it felt different because it was a full-stack app. I had more control over the application code, so I could change it and evolve it as I wanted. I felt like I had built an app that could grow in any direction. Although I had to write more code, it felt like a good trade-off for future needs.&lt;/p&gt;

&lt;p&gt;To decide which option is best for you, I would suggest trying both and seeing how you feel. It is easy to set up both tools and see if they make sense for you.&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%2Fystrixl19d9godu5854p.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%2Fystrixl19d9godu5854p.png" alt="Grazie for reading 🙃"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you try out the Phrase Tutor app, please let me know what you think. You can reach me on Twitter. I'm always looking for ways to make it better.&lt;/p&gt;

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