<?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: AssafKr</title>
    <description>The latest articles on DEV Community by AssafKr (@assafkr).</description>
    <link>https://dev.to/assafkr</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%2F636319%2F8f7da06a-fd06-4c3e-81bf-9df4770be6a2.png</url>
      <title>DEV Community: AssafKr</title>
      <link>https://dev.to/assafkr</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/assafkr"/>
    <language>en</language>
    <item>
      <title>Building A Slack Clone With Preview Environments</title>
      <dc:creator>AssafKr</dc:creator>
      <pubDate>Wed, 17 May 2023 10:54:00 +0000</pubDate>
      <link>https://dev.to/livecycle/building-a-slack-clone-with-preview-environments-2dh6</link>
      <guid>https://dev.to/livecycle/building-a-slack-clone-with-preview-environments-2dh6</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;In this tutorial, we’re going to build a full-stack Slack-like application, with multiple running services. We’ll also set up preview environments for every commit in every branch, complete with all the running services. (spoiler alert - if you prefer to look straight at the code you can check out the finished product is in this &lt;a href="https://github.com/livecycle/supabase-nexjs-preevy-example"&gt;repo&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;To do this we’ll be using a combination of &lt;a href="https://supabase.com/"&gt;supabase&lt;/a&gt; + &lt;a href="https://nextjs.org/"&gt;Next.js&lt;/a&gt;. We’ll also be using a tool called &lt;a href="https://github.com/livecycle/preevy"&gt;Preevy&lt;/a&gt; to provision the preview environments for our project.&lt;/p&gt;

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

&lt;p&gt;On the backend side of things, we’re going to use &lt;a href="https://supabase.com/"&gt;Supabase&lt;/a&gt;, an OSS project designed as an alternative to &lt;a href="https://firebase.google.com/"&gt;Google’s Firebase&lt;/a&gt; - a platform for DB, realtime communication, authentication and more.&lt;/p&gt;

&lt;p&gt;On the frontend side of things, we’ll be using a simple &lt;a href="https://nextjs.org/"&gt;Next.js&lt;/a&gt; app - a &lt;a href="https://slack.com/"&gt;Slack&lt;/a&gt; clone, taken from one of Supabase’s examples - &lt;a href="https://github.com/supabase/supabase/tree/master/examples/slack-clone/nextjs-slack-clone"&gt;here’s a link to the repo&lt;/a&gt; that we’ll be using.&lt;/p&gt;

&lt;p&gt;While this application has many backend services and a frontend server, we’re going to show how to easily set up preview environment, automatically built and deployed on every commit in every branch. &lt;/p&gt;

&lt;p&gt;To achieve that, we’ll be using &lt;a href="https://github.com/livecycle/preevy"&gt;Preevy&lt;/a&gt;, an open source tool for provisioning preview environments with minimal configuration. Preevy will build and deploy our app using a cloud provider, in this case cheap &lt;a href="https://aws.amazon.com/lightsail/"&gt;AWS Lightsail&lt;/a&gt; VMs, while exposing the services with a public, sharable URL for team collaboration.&lt;/p&gt;

&lt;h2&gt;
  
  
  A quick request 🤗
&lt;/h2&gt;

&lt;p&gt;I’m trying to hit our first 1K stars for &lt;a href="https://github.com/livecycle/preevy"&gt;Preevy&lt;/a&gt;. Can you help me out by starring the repository? It helps more developers discover &lt;a href="https://github.com/livecycle/preevy"&gt;Preevy&lt;/a&gt; and helps us to keep creating interesting content for the community. Thanks!&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is interesting
&lt;/h2&gt;

&lt;p&gt;Preview environments are a game-changer in fast-paced development workflows. They allow teams to review actual running versions of the application in review time, before deployment to staging or production. &lt;/p&gt;

&lt;p&gt;Preview environments (also known as “deploy previews” or “ephemeral environments”) enable every stakeholder on the team to view and collaborate on a code change at PR time, which makes for faster product development.&lt;/p&gt;

&lt;p&gt;Since supabase allows &lt;a href="https://supabase.com/docs/guides/getting-started/local-development"&gt;local development&lt;/a&gt;, we can leverage it to completely build a &lt;a href="https://github.com/Yshayy/self-contained-repositories"&gt;self-contained environment&lt;/a&gt; - meaning we can easily build the application with all of its dependencies based on the source code alone. No external service is required to make the application work.&lt;/p&gt;

&lt;p&gt;So this project is a great example of building a multi-service application with some great open source tools and experience how preview environments can impact your workflow and your overall developer experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to build it
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Create the &lt;code&gt;docker-compose.yaml&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;First, we’re going to create a &lt;code&gt;docker-compose.yaml&lt;/code&gt; file with all the supabase services. We are basing our compose file out of &lt;a href="https://github.com/supabase/supabase/blob/master/docker/docker-compose.yml"&gt;supabase's own compose file&lt;/a&gt;. Along with the &lt;code&gt;docker-compose.yaml&lt;/code&gt; itself, we need some configurations and SQL files to initialize the services and DBs. Basically they are copied from supabase's repo, so we're not going to talk about them.  &lt;/p&gt;

&lt;h3&gt;
  
  
  2. Create a &lt;code&gt;Dockerfile&lt;/code&gt; for the Next.js application
&lt;/h3&gt;

&lt;p&gt;We need to add the Slack clone application to the compose file. To do that, we need to create a Docker file for it.&lt;/p&gt;

&lt;p&gt;Here are its contents&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:18-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;deps&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; libc6-compat
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; yarn.lock ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;yarn &lt;span class="nt"&gt;--frozen-lockfile&lt;/span&gt;


&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=deps /app/node_modules ./node_modules&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; NEXT_PUBLIC_SUPABASE_URL&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; NEXT_PUBLIC_SUPABASE_KEY&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;yarn build

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;runner&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; NODE_ENV production&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;addgroup &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="nt"&gt;--gid&lt;/span&gt; 1001 nodejs
&lt;span class="k"&gt;RUN &lt;/span&gt;adduser &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="nt"&gt;--uid&lt;/span&gt; 1001 nextjs

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/public ./public&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder --chown=nextjs:nodejs /app/.next/standalone ./&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static&lt;/span&gt;

&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; nextjs&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 1988&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PORT 1988&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "server.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice we are passing the &lt;code&gt;NEXT_PUBLIC_SUPABASE_URL&lt;/code&gt; and &lt;code&gt;NEXT_PUBLIC_SUPABASE_KEY&lt;/code&gt; args so they are used as environment variables in the build of the app, which allows to connect to the supabase services from our frontend app.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Add the application's schema to the compose service volume
&lt;/h3&gt;

&lt;p&gt;The Slack application expects a certain schema in its database, like the channel and messages tables. We need to load the schema to the database when we boot it up. &lt;a href="https://github.com/supabase/supabase/blob/master/examples/slack-clone/nextjs-slack-clone/full-schema.sql"&gt;Here is the schema SQL file in the example repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To do that, we are adding the &lt;code&gt;full-schema.sql&lt;/code&gt; file  to the &lt;code&gt;./docker/volumes/db&lt;/code&gt; folder and adding it the the composes's db service volumes like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Removed the rest of the configurations for clarity&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# add the following line after the rest of the volumes: &lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./volumes/db/full-schema.sql:/docker-entrypoint-initdb.d/init-scripts/100-full-schema.sql:Z&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Inject the preview environment services’ urls to the environment variables
&lt;/h3&gt;

&lt;p&gt;In order for the services to communicate with each other, they need the addresses of one another. In the original &lt;code&gt;./docker/.env.example&lt;/code&gt; file &lt;a href="https://github.com/supabase/supabase/blob/master/docker/.env.example"&gt;from the supabase repo&lt;/a&gt;, &lt;code&gt;localhost&lt;/code&gt; URLs are used to connect to the different services, since it is assumed the environment will run on the developer's private machine. In our case, the environment is going to run on the preview environment and have public internet URLs.&lt;/p&gt;

&lt;p&gt;To get the URLs, Preevy exposes the public facing URIs using special build time environment variables, &lt;a href="https://preevy.dev/recipes/service-discovery"&gt;as explained in the docs&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;We can make our environment variables support both the Preevy URLs and local development by leveraging bash's &lt;a href="https://tldp.org/LDP/abs/html/parameter-substitution.html"&gt;parameter substitution&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For example, instead of defining the &lt;code&gt;SITE_URL&lt;/code&gt; variable with the local URL&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SITE_URL=http://localhost:3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll define&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SITE_URL=${PREEVY_BASE_URI_STUDIO_3000:-http://localhost:3000}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which we'll set the value of &lt;code&gt;http://localhost:3000&lt;/code&gt; if the value of &lt;code&gt;PREEVY_BASE_URI_STUDIO_3000&lt;/code&gt; is null, meaning we run outside of Preevy. You can look at the modified &lt;code&gt;./docker/.env.example&lt;/code&gt; file in the repo of this example - &lt;a href="https://github.com/livecycle/supabase-nexjs-preevy-example/blob/master/docker/.env.example"&gt;over here&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Using Preevy to deploy the environment on your local machine
&lt;/h3&gt;

&lt;p&gt;To create a preview environment from your local machine keep reading this section. You can also skip that and configure it directly to run on your CI, as described in the next section.&lt;/p&gt;

&lt;p&gt;First step is to make sure &lt;code&gt;Preevy&lt;/code&gt; is installed and configured, as &lt;a href="https://preevy.dev/#getting-started"&gt;detailed in the documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once that's done, do the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Clone this repo &lt;a href="https://github.com/livecycle/supabase-nexjs-preevy-example"&gt;https://github.com/livecycle/supabase-nexjs-preevy-example&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Inside the &lt;code&gt;docker&lt;/code&gt; directory, run &lt;code&gt;cp .env.example .env&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Inside the &lt;code&gt;docker&lt;/code&gt; directory, run &lt;code&gt;preevy up&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And that's it!&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Creating a GH action workflow automatically deploys on every update
&lt;/h3&gt;

&lt;p&gt;Preevy has a convenient GitHub action - &lt;a href="https://github.com/marketplace/actions/preevy-up"&gt;livecycle/preevy-up-action&lt;/a&gt;, as described in its documentation, here is a usage example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy Preevy environment&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;opened&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;synchronize&lt;/span&gt;
&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;id-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
  &lt;span class="na"&gt;pull-requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-actions/configure-aws-credentials@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;role-to-assume&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;arn:aws:iam::12345678:role/my-role&lt;/span&gt;
          &lt;span class="na"&gt;aws-region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;eu-west-1&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;livecycle/preevy-up-action@latest&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;preevy&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;profile-url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3://preevy-12345678-my-profile?region=eu-west-1"&lt;/span&gt;
          &lt;span class="na"&gt;docker-compose-yaml-path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./docker/docker-compose.yaml"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mshick/add-pr-comment@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.preevy.outputs.urls-markdown }}&lt;/span&gt;           
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;In summary, using open source tools and frameworks like &lt;a href="https://supabase.com/"&gt;supabase&lt;/a&gt;, &lt;a href="https://nextjs.org/"&gt;Next.js&lt;/a&gt; and &lt;a href="https://github.com/livecycle/preevy"&gt;Preevy&lt;/a&gt; can be a powerful combination for creating scalable full-stack applications, and more effective development workflows. &lt;/p&gt;

&lt;p&gt;By following the provided steps and utilizing &lt;a href="https://docs.docker.com/compose/"&gt;Docker Compose&lt;/a&gt;, developers can easily set up preview environments that automatically build and deploy with each commit, facilitating efficient code review and collaboration. &lt;/p&gt;

&lt;p&gt;With preview environments, stakeholders can review running versions of the application in real time, and developers can benefit from clearer feedback, faster development cycles and a much better developer experience.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>opensource</category>
      <category>devops</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
