DEV Community

Cover image for Serverless - Is it worth it? Build a serverless chat webapp with Svelte and Firebase
arnu515
arnu515

Posted on • Edited on

Serverless - Is it worth it? Build a serverless chat webapp with Svelte and Firebase

Hey! I'm back here with another tutorial/guide. Today, we'll be looking at Serverless.

An intro to serverless

What is serverless?

Serverless is where you don't have a backend. Yes, that's right. You only code the frontend, and use SaaS (Software as a Service) and BaaS (Backend as a Service) for the backend. An example of SaaS is Firebase. We'll be using it in this tutorial.

Should I go serverless?

I don't like serverless. I like to have control over my backend, my database and my users, but for some people, it is amazing, because you don't need a backend developer. Since the backend is already made for you, you just need to make the frontend.

What are the advantages of serverless?

There are many advantages to serverless. First of all, there is less code. You don't have to code the backend because it has already been done for you. Second, serverless scales well. Most, if not all of these SaaSes have a generous free tier which you can then upgrade to one of their paid plans if your software grows.

Are there any disadvantages?

Yes. Your users and your database are left in the mercy of these companies. This might not be that bad, because they usually have very good security, but this also means that you have very less, if any, control over your users, database, and the backend itself. There is no REST api, or graphql layer usually, and you will have to use the SaaS's SDK. Speaking of SDKs, usually, most services only have first party SDKs for javascript. If you use any other language, like python or c# for web development (like me), you usually have to use a third party library

The app

Now that we're done with the explaining, let's make the applicaiton.

What we'll be building

We'll make a realtime updating chat application using svelte as the frontend framework and firebase as the backend. We'll use Firebase Auth to authenticate users using email/password and Google signin. We'll then be using Firebase Firestore for the database. Firestore also has a special feature that allows us to listen to changes in the database, which we'll use to build the app. Along with serverless, you'll also learn things like routing in svelte and cleansing cuss words. Try out the app here. Please keep it clean!

Creating a svelte app

For the frontend framework, or compiler rather, we'll be using Svelte. It is a very easy to learn framework, and is also rather new, but the main advantage of svelte is that it is small, very small. The final gzipped application will be much smaller in size compared to similar apps in other frameworks.

To create a brand new svelte app,

npx degit sveltejs/template serverless-chat
cd serverless-chat
npm install
Enter fullscreen mode Exit fullscreen mode

I prefer typescript instead of javascript, so I'm gonna convert my svelte app to typescript. You can skip this if you want, but you will have to change the code samples a little.

node scripts/setupTypescript.js
Enter fullscreen mode Exit fullscreen mode

Creating a firebase project

We'll be using firebase as our SaaS/BaaS because firebase has many useful services like authentication, databases and storage, plus, they also integrate with each other, which is crutial. If you decide to use Auth0 instead of firebase auth and Fauna instead of firebase firestore (database), then you will find it harder to connect the two, but since both services are made by the same provider, we should be able to skip that step.

Let's create our firebase project. Head over to the firebase console and sign in/up to firebase using your google account.

Follow these steps to initialise firebase:

  • Create firebase project

  • Create firebase app

  • Configure firebase auth and firebase firestore

Tip: Of course, you can change the name of the project and app to whatever you like.

Saving your firebase config

If you remember, in the second video, I copied the config object firebase provided to me. We need this object, because it will allow us to connect firebase to our svelte app. Create a new file src/firebase_config.ts or src/firebase_config.js if you're not using Typescript and enter this in it.

// src/firebase_config.ts
export default {
    // your firebase config object here
}
Enter fullscreen mode Exit fullscreen mode

Tip: Remember to add this file to your .gitignore and other ignores.

Tip: Lost your firebase config? Don't worry, you can get it from the firebase settings.

Connect firebase to svelte

First, we need the firebase sdk. Install it by typing:

npm install firebase
Enter fullscreen mode Exit fullscreen mode

Now, let's create a file that will initialise firebase for us. I'll put it in src/services/firebase.ts, but you can do it wherever.

// src/services/firebase.ts
import firebase from "firebase/app";
import "firebase/auth";
import "firebase/firestore";

import config from "../firebase_config";

firebase.initializeApp(config);

// Auth
export const auth = firebase.auth();
export const googleAuth = new firebase.auth.GoogleAuthProvider();

// Firestore
export const firestore = firebase.firestore();
Enter fullscreen mode Exit fullscreen mode

Coding the app

Let's first make the homepage. We'll be using routing to allow routes like /chat instead of having everything on one page. To do that, we will need a router. I recommend page.js, and that's what we'll be using. There are other routers, but I like this one, because it not only works for svelte, but also works for all other apps, even raw HTML!

If you want robust routing, and also other features like SSR, I recommend sapper, the Next.js for svelte!

Let's start by installing page.js:

npm install page
Enter fullscreen mode Exit fullscreen mode

If you're using typescript, you will also need to install types for page.js:

npm install -D @types/page
Enter fullscreen mode Exit fullscreen mode

Initialise the router

Let's clear the contents of App.svelte. We don't need it. Now, we'll use page.js to route in svelte.

<!-- src/App.svelte -->

<!-- The lang="ts" is there because I'm using typescript -->
<script lang="ts">
    import router from "page";
    import Index from "./routes/index.svelte";

    let page;
    let params = {};

    router("/", () => page = Index)

    router.start();
</script>

<svelte:component this={page} {params} />
Enter fullscreen mode Exit fullscreen mode

You can see how easy it is to setup routing in svelte.

Tip: The svelte:component tag is used to render a component dynamically. The this attribute is the component, and any other attribute passed goes to the component. This is how we route in svelte.

Also, we need to make our app SPA compatible, so let's do just that. In package.json, in the start script, change it to:

  "scripts": {
    "build": "rollup -c",
    "dev": "rollup -c -w",
    "start": "sirv public -s --host",
    "validate": "svelte-check"
  },
Enter fullscreen mode Exit fullscreen mode

Tip: The --host option just exposes your server on your network, but the -s option makes the app SPA compatible.

Styles

We're pro developers here. Nobody's got time for CSS (lmao just kidding). Let's use a CSS library to speed things up. I'm gonna use W3.CSS because it is small and easy to learn. You can download it from here or use a CDN like cdnjs. Modify public/index.html to include w3.css like so

<!-- public/index.html -->

<!-- in head tag -->
<link rel="stylesheet" href="https://w3schools.com/w3css/4/w3.css" />
<!-- or use your downloaded w3.css (place it in the public directory -->
<link rel="stylesheet" href="/w3.css" />
Enter fullscreen mode Exit fullscreen mode

While we're there in index.html, you might want to change the site title.

I'm also gonna include fontawesome for icons.

Auth component

For simplicity, I'm gonna put Login and Register in one component, src/components/Auth.svelte

<!-- src/components/Auth.svelte -->

<script lang="ts">
    import {fade} from "svelte/transition"
    import ErrorAlert from "./ErrorAlert.svelte";

    export let authMode: "login" | "register" = "register";
    let isAuthenticated = false;
    let err: string | null = null;

    function login() {}

    function register() {}

    function logout() {}

    function google() {}
</script>

<div class="w3-card-4" style="width: 40%; margin: 1rem auto">
    {#if !isAuthenticated}
    <div class="w3-container">
        <h2 class="w3-center">{authMode === "login" ? "Login" : "Register"} to Serverless Chat</h2>
    </div>
    <div class="w3-container">
        <div class="w3-bar w3-center w3-border-bottom w3-border-gray w3-text-dark-gray">
            <button on:click={() => authMode = "login"} class="w3-bar-item w3-button w3-text-center {authMode === "login" && "w3-blue"}" style="width: 50%">LOGIN</button>
            <button on:click={() => authMode = "register"} class="w3-bar-item w3-button w3-text-center {authMode === "register" && "w3-blue"}" style="width: 50%">REGISTER</button>
        </div>
        <!-- Email login/register forms -->
        {#if authMode === "login"}
            <form in:fade on:submit|preventDefault={login}>
                {#if err}
                <ErrorAlert message={err} />
                {/if}
                <h4>Login</h4>
                <p>
                    <label for="l-email">Email</label>
                    <input type="email" class="w3-input w3-border" placeholder="Enter your email" id="l-email">
                </p>
                <p>
                    <label for="l-password">Password</label>
                    <input type="password" class="w3-input w3-border" placeholder="Enter your password" id="l-password">
                </p>
                <p>
                    <button type="submit" class="w3-button w3-blue">Login</button>
                    <button on:click={() => authMode = "register"} type="button" class="w3-button w3-light-gray">Register</button>
                </p>
            </form>
        {:else}
            <form in:fade on:submit|preventDefault={register}>
                {#if err}
                <ErrorAlert message={err} />
                {/if}
                <h4>Register</h4>
                <p>
                    <label for="r-email">Email</label>
                    <input type="email" class="w3-input w3-border" placeholder="Enter your email" id="r-email">
                </p>
                <p>
                    <label for="r-password">Password</label>
                    <input type="password" class="w3-input w3-border" placeholder="Enter a password" id="r-password">
                </p>
                <p>
                    <label for="r-cpassword">Confirm Password</label>
                    <input type="password" class="w3-input w3-border" placeholder="Re-enter that password" id="r-cpassword">
                </p>
                <p>
                    <button type="submit" class="w3-button w3-blue">Register</button>
                    <button on:click={() => authMode = "login"} type="button" class="w3-button w3-light-gray">Login</button>
                </p>
            </form>
        {/if}
        <hr>
        <p>
            <button class="w3-button w3-blue" style="width: 100%" on:click={google}><i class="fab fa-google"></i> Sign in with Google</button>
        </p>
    </div>
    {:else}
    <div class="w3-container">
        <h2>Logged in</h2>
    </div>
    <div class="w3-container">
        <p class="w3-large w3-text-green w3-center"><i class="fas fa-check fa-5x"></i></p>
        <p class="w3-center">Logged in</p>
        <p>
            <button class="w3-button w3-blue" style="width: 100%" on:click={logout}>Log out</button>
        </p>
    </div>
    {/if}
</div>
Enter fullscreen mode Exit fullscreen mode

static/components/ErrorAlert.svelte:

<script lang="ts">
    export let message;
</script>

<div class="w3-panel w3-pale-red w3-text-red w3-leftbar w3-border-red">
    {message}
</div>
Enter fullscreen mode Exit fullscreen mode

Index page

If you noticed, in App.svelte, we have a / route with points to src/routes/index.svelte. We don't have that yet, so let's make it!

<!-- src/routes/index.svelte -->
<script lang="ts">
    import Auth from "../components/Auth.svelte";
</script>

<h1 class="w3-jumbo w3-center">Serverless Chat</h1>
<p class="w3-large w3-center">Please log in</p>

<Auth />
Enter fullscreen mode Exit fullscreen mode

Run the app

Let's run the app:

npm run dev
Enter fullscreen mode Exit fullscreen mode

And if you now visit localhost:5000, you should see a login page with no functionality. We're gonna save that for the next part. Here's the next part!

Top comments (4)

Collapse
 
doomd profile image
Doomd • Edited

I encountered an error when I used your services/firebase.ts template...

export const googleAuth = firebase.auth.GoogleAuthProvider()
Enter fullscreen mode Exit fullscreen mode

should be:

export const googleAuth = new firebase.auth.GoogleAuthProvider()
Enter fullscreen mode Exit fullscreen mode

according to this:
stackoverflow.com/questions/519181...
...and indeed, your own code:
github.com/arnu515/serverless-chat...

Collapse
 
arnu515 profile image
arnu515

Ah yeah! Thanks! I'm gonna change it

Collapse
 
arnu515 profile image
arnu515

The github code for the above project is available here

Collapse
 
arnu515 profile image
arnu515