Serverless - Is it worth it? Build a serverless chat webapp with Svelte and Firebase
Tags: tutorial, webdev, serverless, javascript
Published: 2020-11-06 14:48:41

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](https://firebase.com). 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](https://serverless-svelte.web.app). Please keep it clean!

## Creating a svelte app

For the frontend framework, or compiler rather, we'll be using [Svelte](https://svelte.dev). 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,
```sh
npx degit sveltejs/template serverless-chat
cd serverless-chat
npm install
```

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.
```sh
node scripts/setupTypescript.js
```

## 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](https://console.firebase.google.com) and sign in/up to firebase using your google account.

Follow these steps to initialise firebase:

- Create firebase project
{% vimeo 476280619 %}

- Create firebase app
{% vimeo 476280688 %}

- Configure firebase auth and firebase firestore
{% vimeo 476280777 %}

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

```ts
// src/firebase_config.ts
export default {
    // your firebase config object here
}
```

> 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:
```sh
npm install firebase
```

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.
```ts
// 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();
```

## 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](https://sapper.svelte.dev), the Next.js for svelte!

Let's start by installing page.js:
```sh
npm install page
```

If you're using typescript, you will also need to install types for page.js:
```sh
npm install -D @types/page
```

### 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.
```html
<!-- 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} />
```

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:

```json
  "scripts": {
    "build": "rollup -c",
    "dev": "rollup -c -w",
    "start": "sirv public -s --host",
    "validate": "svelte-check"
  },
```

> 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](https://w3schools.com/w3css/4/w3.css) or use a CDN like [cdnjs](https://cdnjs.com). Modify `public/index.html` to include w3.css like so

```html
<!-- 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" />
```

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

I'm also gonna include [fontawesome](https://fontawesome.com) for icons.

### Auth component

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

```html
<!-- 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>
```

`static/components/ErrorAlert.svelte`:
```html
<script lang="ts">
    export let message;
</script>

<div class="w3-panel w3-pale-red w3-text-red w3-leftbar w3-border-red">
    {message}
</div>
```

### 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!

```html
<!-- 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 />
```

## Run the app
Let's run the app:
```sh
npm run dev
```

And if you now visit [localhost:5000](http://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!](https://dev.to/arnu515/build-a-serverless-chat-app-with-svelte-and-firebase-part-2-35o1)