Tris Tolliday Creative Technologist at Greenwood Campbell specialising in innovation in the tech industry.

Build an SSR web app with Firebase functions, hosting, and Svelte Sapper

7 min read 2008

The traditional role of a frontend developer sat wholly on the client side. But with the rise of Node.js, the Jamstack, and serverless architecture, frontenders with a reasonable level of JavaScript knowledge can now build their own fullstack solution.

The route you go down is a mix of preference and specialism. Today, I’m going to take a look at a mix that I believe offers a great combo of speed, scalability, and cost.

What we are going to build in this tutorial is a server-side rendered (SSR) web app that uses scalable serverless functions to serve your application. When we’re done, our app will feel like a Single Page Application(SPA) from a user perspective, and search engines will find it highly digestible thanks to the utilization of SSR.

This duality of server-side rendering on a serverless function sounds super dumb. However, all is not as it seems.

Serverless simply means that you don’t have to manage the server yourself. In the case of Firebase functions, Google will be doing the server management, application scaling, and other complex infrastructure and platform optimization for us. That leaves us to focus on the best bit — the code.

For this example, I will be using Firebase as the platform for deploying our web app. Keep in mind, though, that there is a flavor of serverless function available for every taste, from Microsoft’s Azure Functions to AWS’ Lambda Functions and the increasingly popular Netlify. It’s worth noting that most platforms have a free tier for functions, meaning small apps with lower usage (e.g. portfolio sites) are likely to be free to run.

Let’s look at how this will hook up

There are two parts that will work together to serve up our web app:

  • Hosting: Holds our static files, such as any media we have, our favicons, and any other static content.
  • Functions: Small JavaScript functions that deal with the server-side rendering of our application, as well as any providing an end point for  APIs that we might end up using.

The framework we will be using to craft our application will be Sapper, a sister project to Svelte. Svelte is the rising star in the front-end world. This will allow us to compile our application before we send it up to the spirit in the sky, providing super speedy vanilla JS functionality and reactive components from the get-go.

What are Svelte and Sapper, and why should I care?

Svelte and Sapper are the brainchild of Javascript mastermind Richard Harris (creator of the infamous Rollup, among other open source favorites).

They are built upon the concept that modern Javascript frameworks are too bloaty and too Virtual-DOM heavy. Instead, they offer a light and fluffy vanilla solution by compiling code ahead of time rather than using the client to do the heavy lifting.

We made a custom demo for .
No really. Click here to check it out.

For developers, they offer a familiar toolset of HTML, CSS, and JS packaged in .svelte files. With a little framework specific syntax, they can be linted by your favorite tools. When you’re ready to send your code up, they blend everything together into a soft machine readable meringue of code that runs fast.

Setting it all up

If I have convinced you to give Sapper a try, then welcome to a tutorial that will hopefully give you a taste of what a modern, frictionless web app can look like.

We are going to build a very simple site, hook in some nifty features, and deploy it to the cloud with the command line. Let’s get cracking.

Your local environment

Walking before we can run, let’s talk about your setup. Visual Studio Code is my editor of choice for this tutorial. Feel free to use your own, but you may need to mess around to get your setup working. You should also have Node.js on your machine. Any LTS version above 10 should work nicely.

Setting up your project

Let’s start by creating our project folder. We’ll give it a handsome name like sapper-site and open it up in Visual Studio Code.

Up at the top of your toolbar, you should see Terminal, hit it, and select New Terminal. This will open up the command prompt of your chosen OS. Handily, we will have done so in the project folder that you intend to be working in.

We will use the terminal to build the Sapper base from the main Sapper repo with the command:

npx degit "sveltejs/sapper-template#rollup"

This will plunk the template straight into your project. We will install our dependencies with npm:

npm install

And finally, we’ll run the app with:

npm run dev

If you open up your browser on localhost:3000, you will see the Sapper base project: Big Success! Hooray.

If you want to know more about the fundamentals of what you have just made, I would recommend the official docs rather than repeating them for you here.

Making it our own

So, we have a base project and we have it open. Big whoop.

It’s worth noting that Sapper and Svelte have very lean initial setups. This helps them run super fast, but it also means that we are going to have to do a little bit of housekeeping to craft our web app to how we want it. That isn’t to say that Sapper comes with just the bare bones — your app already has its own service worker! Rather, it’s born a blank canvas and it’s up to you to add your own splash of color.

Before you get too creative, make sure you get your project into a git repository. Even the most seasoned gitter will leave it one line of code too late to untangle their mistakes.

I personally like my CSS with a little extra S, so my first step in any project tends to be finding an appropriate way to add a SCSS precompiler into the mix. Finding suitable resources is usually a case of trial and error. Check out the great Svelte Society cookbook recipes to get you going. By the way, the Svelte Society is a great community for all things Svelte and Sapper.

Up in the cloud

Once you have your web app just the way you like it, it’s time to do a little bit of refactoring to get it ready for Firebase.

This is, again, a great point for a git commit. We are about to move huge swathes of our project around, and we wouldn’t want to lose all the hard work we have done.

From now on in, we are stepping into the world of Google, so you will need a Google Account. As we are using Firebase functions, we will also need to run a blaze plan, which does require a billing account.

To begin our transition to the cloud, we need to install Firebase tools locally. As we already have Node installed, we can use npm:

npm install -g firebase-tools

This will allow us to set up repo for hosting and functions. Head back into the terminal for our project folder, and initialize firebase:

firebase init

This provides us with a set of options for our project. We will select hosting and functions. If you want to test the serving of your function locally later, you can install emulators too:

Firebase UI.

We then select create a new project from the list. Give your project a unique name. Optionally, you can give your project a friendly name. We’ll select JavaScript and enable ESlint to catch any buggy code.

Let npm install its dependencies now, and give it a few minutes to run.

We can leave the default public repo as public for now, and we’ll say yes to configure as a Single Page Application.

Refactor Sapper repo for Firebase

We need to move our Sapper app into our firebase functions folder so that we can run SSR.

Lets start by shimmying some of our folders and files into the functions folder:

  • Cypress/
  • Cypress.json
  • Rollup.config.js
  • src/
  • static/

Next, we move our Sapper package.json over to functions. We can use Visual Studio Code’s Source Control function to match changes between our two files.

A package.json working text.

The idea of this merge is to retain as much of both files as possible to produce one super package file in our functions folder.
At this point, I also swap Polka for Express, which is my preferred lightweight server.

Once we have merged our package.json, we can do an npm install in our functions directory. If you get stuck, check out the demo repo. This can be a bit of a thorny process.

Next, we need to update some of our files. We’ll start with index.js, which our main entrypoint for the SSR side of our web app.

// index.js
const functions = require('firebase-functions');
const { sapperServer } = require('./__sapper__/build/server/server');
exports.ssr = functions.https.onRequest(sapperServer); // see the server.js later for the name

All we are doing here is redirecting Firebase function requests to Sapper for handling. We’ll need to match this with the corresponding server.js file so that everything is still talking to each other.

// server.js
import sirv from 'sirv';
import express from 'express';
import compression from 'compression';
import * as sapper from '@sapper/server';

const { PORT, NODE_ENV } = process.env;
const dev = NODE_ENV === 'development';

const sapperServer = express() // You can also use Express
   .use(
       compression({ threshold: 0 }),
       sirv('static', { dev }),
       sapper.middleware()
   )
   if(dev){
       sapperServer.listen(PORT, err => {
       if (err) console.log('error', err);
   });
   }
  
export {sapperServer}

Here, I have swapped Polka for Express out of preference, but both will work. The main changes here are that we now need to export Express so that index.js can read it.

{
 "hosting": {
   "public": "functions/static",
   "ignore": [
     "firebase.json",
     "**/.*",
     "**/node_modules/**"
   ],
   "rewrites": [
     {
       "source": "**",
       "function": "ssr"
     }
   ]
 }
}

We need to tell Firebase where to point all of its resources, so this file gets mostly rewritten.

This is the key to getting our Sapper app up and running:

  • We tell hosting to use our functions/static file for all of our resources. This serves up our media, favicon, and manifest.
  • We tell any requests to look to the SSR function to run effectively.

So lets make sure we are in the functions directory, and reinstall our package.json:

cd functions
npm install

and check that our Sapper project still works.

npm run dev

If all is well, we can start a production build and deploy it to Firebase. We are getting super close, don’t worry.

npm run build

No errors? Wonderful. We are ready to do the final deploy.

Errors? No worries, it’s common to have a few issues at this point. We have moved a ton of stuff, and missing any one of those steps will cause you issues. This is also not a step that you would expect to do regularly. The best way to debug is to read your console, make sure you’re not missing any packages, and compare your project files to the sample repo.

Deploy for the cloud

The final step is to deploy our project. We will use:

firebase deploy

This sends our app up to the cloud. Especially with the first deploy to a new project, you can expect the functions side of the deployment to take a good 5 minutes. If you have successfully deployed, you will see your new hosting URL in the console, which you can preview your web app on. If you have a custom domain, this can be attached to your project in the Firebase console.

Conclusion

Congratulations! You have successfully built a Sapper project, refactored it to work with Firebase, and sent it up to the serverless server in the sky.

Your app is lightweight, scalable, and bitesized all at the same time. If you are the TL;DR type, you can use the sample repo as a starting point for future projects and go from 0 to Firebase functions in the time it takes to make a coffee.

Check out the github here.

You come here a lot! We hope you enjoy the LogRocket blog. Could you fill out a survey about what you want us to write about?

    Which of these topics are you most interested in?
    ReactVueAngularNew frameworks
    Do you spend a lot of time reproducing errors in your apps?
    YesNo
    Which, if any, do you think would help you reproduce errors more effectively?
    A solution to see exactly what a user did to trigger an errorProactive monitoring which automatically surfaces issuesHaving a support team triage issues more efficiently
    Thanks! Interested to hear how LogRocket can improve your bug fixing processes? Leave your email:

    : Full visibility into your web apps

    LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

    In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

    .
    Tris Tolliday Creative Technologist at Greenwood Campbell specialising in innovation in the tech industry.

    7 Replies to “Build an SSR web app with Firebase functions, hosting,…”

    1. I had an error when running `npm run dev` after doing all the changes. Console says, “sh: 1: sapper: not found”.

    2. Nvm. All good now. There was just a misplacement of the dependencies when things are being merged. Thanks for this great tutorial! Congrats!

    3. I looked at the logs and I got an error saying the port (8080) was already in use. Has anyone else experienced this?

    Leave a Reply