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.
There are two parts that will work together to serve up our web app:
static files
, such as any media we have, our favicons, and any other static content.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.
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.
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.
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.
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.
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.
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:
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.
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.
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:
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.
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.
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.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to implement one-way and two-way data binding in Vue.js, using v-model and advanced techniques like defineModel for better apps.
Compare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
9 Replies to "Build an SSR web app with Firebase functions, hosting, and Svelte Sapper"
I had an error when running `npm run dev` after doing all the changes. Console says, “sh: 1: sapper: not found”.
Nvm. All good now. There was just a misplacement of the dependencies when things are being merged. Thanks for this great tutorial! Congrats!
When all is deployed to firebase, there was no error in the console except that when I access the URL, I got an error from the firebase function.
Please see this screenshot of the error: https://snipboard.io/o5ldrS.jpg
Thank you.
So it did finally work with Express, not Polka. I guess express would be my preference too 😉
Hi, for me CSS links are broken how i can fix it?
I looked at the logs and I got an error saying the port (8080) was already in use. Has anyone else experienced this?
I’,m getting the following error: https://stackoverflow.com/questions/63677916/sapper-firebase-hosting-broken-css-and-other-asset-links
How I can fix it?
All files (css,js and images) are serving from firebase function. Is there any way by which these files not served by firebase function?
when i deploy my app i gat this error. Please HELP !! :(((
Error: ENOENT: no such file or directory, open ‘__sapper__/build/build.json’