Building A Slack Clone With Preview Environments

AssafKr - May 17 '23 - - Dev Community

TL;DR

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 repo).

To do this we’ll be using a combination of supabase + Next.js. We’ll also be using a tool called Preevy to provision the preview environments for our project.

Getting Started

On the backend side of things, we’re going to use Supabase, an OSS project designed as an alternative to Google’s Firebase - a platform for DB, realtime communication, authentication and more.

On the frontend side of things, we’ll be using a simple Next.js app - a Slack clone, taken from one of Supabase’s examples - here’s a link to the repo that we’ll be using.

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.

To achieve that, we’ll be using Preevy, 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 AWS Lightsail VMs, while exposing the services with a public, sharable URL for team collaboration.

A quick request 🤗

I’m trying to hit our first 1K stars for Preevy. Can you help me out by starring the repository? It helps more developers discover Preevy and helps us to keep creating interesting content for the community. Thanks!

Why this is interesting

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.

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.

Since supabase allows local development, we can leverage it to completely build a self-contained environment - 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.

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.

How to build it

1. Create the docker-compose.yaml

First, we’re going to create a docker-compose.yaml file with all the supabase services. We are basing our compose file out of supabase's own compose file. Along with the docker-compose.yaml 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.

2. Create a Dockerfile for the Next.js application

We need to add the Slack clone application to the compose file. To do that, we need to create a Docker file for it.

Here are its contents

FROM node:18-alpine AS base

FROM base AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app

COPY yarn.lock ./
RUN yarn --frozen-lockfile


FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ARG NEXT_PUBLIC_SUPABASE_URL
ARG NEXT_PUBLIC_SUPABASE_KEY

RUN yarn build

FROM base AS runner
WORKDIR /app

ENV NODE_ENV production

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

COPY --from=builder /app/public ./public

COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs

EXPOSE 1988

ENV PORT 1988

CMD ["node", "server.js"]
Enter fullscreen mode Exit fullscreen mode

Notice we are passing the NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_KEY 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.

3. Add the application's schema to the compose service volume

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. Here is the schema SQL file in the example repo.

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

  db:
    # Removed the rest of the configurations for clarity
    volumes:
      # add the following line after the rest of the volumes: 
      - ./volumes/db/full-schema.sql:/docker-entrypoint-initdb.d/init-scripts/100-full-schema.sql:Z

Enter fullscreen mode Exit fullscreen mode

4. Inject the preview environment services’ urls to the environment variables

In order for the services to communicate with each other, they need the addresses of one another. In the original ./docker/.env.example file from the supabase repo, localhost 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.

To get the URLs, Preevy exposes the public facing URIs using special build time environment variables, as explained in the docs.

We can make our environment variables support both the Preevy URLs and local development by leveraging bash's parameter substitution.

For example, instead of defining the SITE_URL variable with the local URL

SITE_URL=http://localhost:3000
Enter fullscreen mode Exit fullscreen mode

We'll define

SITE_URL=${PREEVY_BASE_URI_STUDIO_3000:-http://localhost:3000}
Enter fullscreen mode Exit fullscreen mode

Which we'll set the value of http://localhost:3000 if the value of PREEVY_BASE_URI_STUDIO_3000 is null, meaning we run outside of Preevy. You can look at the modified ./docker/.env.example file in the repo of this example - over here

5. Using Preevy to deploy the environment on your local machine

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.

First step is to make sure Preevy is installed and configured, as detailed in the documentation.

Once that's done, do the following:

  1. Clone this repo https://github.com/livecycle/supabase-nexjs-preevy-example
  2. Inside the docker directory, run cp .env.example .env
  3. Inside the docker directory, run preevy up

And that's it!

6. Creating a GH action workflow automatically deploys on every update

Preevy has a convenient GitHub action - livecycle/preevy-up-action, as described in its documentation, here is a usage example:

name: Deploy Preevy environment
on:
  pull_request:
    types:
      - opened
      - synchronize
permissions:
  id-token: write
  contents: read
  pull-requests: write
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: aws-actions/configure-aws-credentials@v2
        with:
          role-to-assume: arn:aws:iam::12345678:role/my-role
          aws-region: eu-west-1
      - uses: actions/checkout@v3
      - uses: livecycle/preevy-up-action@latest
        id: preevy
        with:
          profile-url: "s3://preevy-12345678-my-profile?region=eu-west-1"
          docker-compose-yaml-path: "./docker/docker-compose.yaml"
      - uses: mshick/add-pr-comment@v2
        with:
          message: ${{ steps.preevy.outputs.urls-markdown }}           
Enter fullscreen mode Exit fullscreen mode

Summary

In summary, using open source tools and frameworks like supabase, Next.js and Preevy can be a powerful combination for creating scalable full-stack applications, and more effective development workflows.

By following the provided steps and utilizing Docker Compose, developers can easily set up preview environments that automatically build and deploy with each commit, facilitating efficient code review and collaboration.

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.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .