DEV Community

Cover image for How to host a Sapper.js SSR app on Firebase.

How to host a Sapper.js SSR app on Firebase.

Eckhardt on May 27, 2019

I've been spending about two days scouring the net on trying to find the optimal way to integrate Sapper with Firebase. It's not as easy as it soun...
Collapse
 
mortscode profile image
Mort

Amazing work. For some reason, I'm not getting any images or styling when I run npm start. Everything renders correctly with npm run dev, but no luck on the firebase side.

Any thoughts?

Collapse
 
charukahs profile image
Charuka Samarakoon

Not a solution but a head relief:
Images and styling will work after running npm deploy and visit the hosted url

Don't know the exact reason, but probably due to firebase hosting not mounting locally at /

Collapse
 
mortscode profile image
Mort

In my Firebase Console, if I click on the link under "Hosting" the site seems to be loading just fine. For some reason, I'm not getting anything but html on my localhost:5000 when I run npm start.

Collapse
 
eckhardtd profile image
Eckhardt

Mm, difficult to say, do you have a repo maybe? Is your firebase.json set like in the article? And are your external css and image files in your static folder?

Thread Thread
 
mortscode profile image
Mort • Edited

repo: github.com/mortscode/sapper-firebase

The issue in my browser console is that all of the asset urls are like so: http://localhost:5000/[firebase-project-name]/us-central1/ssr/global.css. They're all returning 404's.

Is there a location in the app where I should be defining that path?

One more thing, the command firebase init functions hosting didn't give me the hosting options in the console. I had to run firebase init hosting separately.

Thread Thread
 
focusat profile image
focus-at • Edited

works for me

const functions = require("firebase-functions");

// We have to import the built version of the server middleware.
const { sapper } = require("./sapper/build/server/server");
const middleware = sapper.middleware();

exports.ssr = functions.https.onRequest((req, res) => {
req.baseUrl = '';
middleware(req, res);
});

Thread Thread
 
eckhardtd profile image
Eckhardt

Hi Mort,

So sorry for the late reply. Have been taking a screen break.

Have you figured it out? I will look at your repo in my breaks.

Regards

Thread Thread
 
eckhardtd profile image
Eckhardt

I can't seem to replicate your problem, my urls are correct out of the box. I'll have to spend time finding the issue. I did however notice something else regarding versions of dependencies in the ./functions folder. It seems that I have to use the exact versions in package.json in functions here github.com/Eckhardt-D/sapper-fireb.... Strange. I'll explore. Sorry I don't have an off-the-cuff response.

Thread Thread
 
aslak01 profile image
aslak01

I'm having the exact same issue as Mort. I'm using newest versions in the functions package.json, as I got an error trying to use firebase admin 7.0.0. Once built and uploaded it works fine, but on localhost:5000 the firebase project name and useast1 time zone stuff shows up in the urls and causes 404s.

Thread Thread
 
mikedane profile image
Mike Dane

focus-at's solution worked for me, I'm using latest firebase-admin. I suspect this is a problem with the baseUrl var

Thread Thread
 
olyno profile image
Olyno

Hi,
Same issue here with latest versions. Any fix?

Thread Thread
 
bodhiz profile image
bodhiz

focus-at workaround worked for me as well.

Collapse
 
nedwards profile image
n-edwards

Thanks very much for breaking this down.

I also tried following this video,
youtu.be/fxfFMn4VMpQ
and found the workflow a bit more manageable when creating a Firebase project first, and then adding in a new Sapper project. I got through it without any issues, on Windows. Would have much preferred it to be a write-up like yours though, so here's a summary:

Create a new empty folder, then navigate to it in the VS Code terminal.

firebase init

  • functions
  • hosting
  • use an existing project you've created on Firebase
  • no ESLint
  • don't install dependencies now
  • public directory: functions/static (Sapper project will go into functions folder)
  • SPA: no

Move or rename package.json, and delete .gitignore (sapper will create these for us later)

cd functions

npx degit sveltejs/sapper-template#rollup --force

Copy contents of scripts block (Firebase commands) from old package.json into scripts block of new package.json that was generated by Sapper.
Rename Firebase's start command to fb_start.

Copy entire engines block from old to new package.json, and change node version to 10.

Copy over contents of dependencies and devDependencies blocks.

Delete old package.json, once all Firebase stuff is moved over, and save the new Sapper one.

Remove polka from dependencies in package.json.

npm install --save express
npm install

server.js:

  • import express instead of polka
  • change function to: const expressServer = express()...
  • change .listen to if (dev) { expressServer.listen ... }
  • export { expressServer }

index.js:

  • const {expressServer} = require('./__sapper__/build/server/server')
  • exports.ssr = functions.https.onRequest(expressServer);

npm run build
npm run dev

localhost:3000 will show the Firebase default index.html from static folder, which can be deleted.
Page reload will bring up Sapper project.

firebase.json:

  • "rewrites": [ { "source": "**", "function": "ssr" }]

npm run build
firebase deploy

Visit app and click around, refresh to verify functionality.

Try Postman, send a GET to your project URL.
In output, look for confirmation that content is SSR.

package.json:

  • "deploy": "npm run build && firebase deploy"

Nav.svelte:

  • add a new li to the navbar, for a new page

routes:

  • create new .svelte page, and add some quick HTML content

npm run deploy

Verify new content shows.
Run audit from Chrome dev tools.

Collapse
 
xanderjakeq profile image
Xander Jake de los Santos

trying to follow this tutorial. But when I try to run the final setup of firebase functions with sapper, I get this error. Any idea why?

Error: ENOENT: no such file or directory, open '__sapper__/build/build.json'

Collapse
 
eckhardtd profile image
Eckhardt

I have not had this error. Keep in mind this is an old post and perhaps things are outdated. I don't keep this post updated, but I'll consider doing an edit to address all the questions here. Have you checked out the repo at github.com/Eckhardt-D/sapper-fireb... ? Perhaps you can find something there.

Collapse
 
xanderjakeq profile image
Xander Jake de los Santos

I tried running the repo but I'm still getting the same error. Maybe I did something wrong with my setup. I'll find a more recent tutorial for now.

Thanks for your time!

Thread Thread
 
eckhardtd profile image
Eckhardt

I'll have a thorough look at the repo this weekend and update the post accordingly. Sorry I can't give you a fast answer. Hope you get it sorted, if not maybe check in here the weekend or watch the repo for changes :).

Collapse
 
kahilkubilay profile image
Kubilay Kahil • Edited

You have to get build before npm start command. You should npm run build in the "functions" folder.

Collapse
 
jofont profile image
Diogo Fontainhas Garcia Marques

Trying to get this to work but I'm getting stuck in the editing the npm scripts.
First, i'm assuming we need to edit the npm scripts from sapper (so not the package.json from inside functions), secondly, when I edit the scripts, the prebuid doesn't run, there are two errors; the first says that rm is not a valid command (fair enough, changed to del) then it didn't like the mkdir -p so I removed the -p. At the end it still doesn't run and says Invalid switch - "__sapper__"., Any idea why? Thank you for the article anyway!

Collapse
 
eckhardtd profile image
Eckhardt

Hi!

It would really be easier for me if I can get a link to a github repo and explore myself so I can give a complete answer.

I’d like to update this post if there were any changes to the workflow because of updates etc.

Also what OS are you running? I haven’t tested this on all platforms. You would be a huge help here!

Thanks.

Collapse
 
jofont profile image
Diogo Fontainhas Garcia Marques

Hello!
Sorry for the delay. So here you go: gitlab.com/JoFont/sapper-firebase-...

The repo has a Readme with the same info I wrote in the first comment along with some images.

I'm running Windows 10. Thank you very much for you help, and have a great day!

Thread Thread
 
eckhardtd profile image
Eckhardt • Edited

Hi! No problem.

I just cloned your repo and holy ... is Windows copy and folder deletion a pain... Here's my solution (Also adding to post):

  1. Add some npm packages to help with removing and copying stuff:

npm install -D rimraf mkdirp ncp

The -D flag adds it to dev dependencies, since that's where we need it. Change your scripts to this:

...
"build": "sapper build --legacy && ncp ./__sapper__/build ./functions/__sapper__/build",
"prebuild": "rimraf - functions/__sapper__/build && mkdirp functions/__sapper__/build",
...

I'm afraid there's more.. An error pops up if the firebase-admin dependency in functions/package.json is greater than 7, so just set it to:

"firebase-admin": "^7.0.0"

Remember to:

cd functions && npm install

These solutions are hacky and this post might bite me in the future due to rapid changes in sapper and firebase stuff. But this got it working for me on Windows 10 using your repository. Regards!

Thread Thread
 
jofont profile image
Diogo Fontainhas Garcia Marques • Edited

Ok! So we do have success with your changes!

Some notes:

  1. Couldn't replicate error with firebase-admin version. Working fine with version 8.2.0, actually if I were to set it to 7.0.0, firebase throws an error.

  2. npm start does work but as someone mentioned earlier, I also do not get styles and assets. No issues I think, because you don't really need npm start, just use npm run dev for development and npm run deploy when deploying to Firebase.

  3. Finally, this does indeed look like a very half-arsed way to make Firebase deliver Sapper SSR, because I feel like it's one update from Firebase or one dependency away from breaking (at least on windows). So perhaps either thinking on a more stable way of implementing this or waiting for firebase for a proper Node.js server would be the way. Either way at least for now it works, though I'll probably stick with AWS for production runs.

Thank you very much @eckhardtd for the tutorial! Have a great one!

Collapse
 
yiddishekop profile image
Yehuda Neufeld

Brilliant article!!!
I'm really thinking a lot about SSR vs static-site [sapper export].
With Firebase we can host the static files of the site & still use user authentication for login etc.
Which makes me wonder why shouldn't I just serve a static site [which is MUCH faster than SSR]?!

Collapse
 
eckhardtd profile image
Eckhardt

Hey, thank you! And yes. There is a big movement towards static or even ‘JAMstack’, personally I’d use static as much as possible, since it almost always fulfills the needs of my clients. But sometimes I need more complicated server architecture and prefer not having to manage client and server explicitly. With Sapper or Nuxt I can manage both in one project. All up to preference though.

Collapse
 
vorcigernix profile image
Adam Sobotka

Oh. Warning:
"Firebase projects on the Spark plan can make only outbound requests to Google APIs. Requests to third-party APIs fail with an error. For more information about upgrading your project, see Pricing."
I followed all the guide just to find that I can't use it. Nice writing anyway.

Collapse
 
bastin profile image
Sebastian Neumair

Thanks a lot for the step-by-step guide.
I created a small rss reader with svelte/sapper, in which I have implemented a backend-api to get and delete a list representing the rss feed. How do I get the backend to work as well? Currently I get 404s.

For the purposes of handling all requests to the /api, i created an api.js:

import express from 'express'
import Parser from 'rss-parser'

const rssList = new Set()

const parser = new Parser()

export default (server) => {
    const router = express.Router()

    router.post('/add', (req, res) => {
        const { url } = req.body

        if (rssList.has(url)) {
            return res.send({ added: false, rssList: [...rssList] })
        }

        rssList.add(url)
        return res.send({ added: true, rssList: [...rssList] })
    })

    router.post('/delete', (req, res) => {
        const { url } = req.body

        if (rssList.has(url)) {
            rssList.delete(url)
            return res.send({ deleted: true, rssList: [...rssList] })
        }

        return res.send({ deleted: false, rssList: [...rssList] })
    })

    router.get('/list', (_, res) => {
        res.send({ rssList: [...rssList] })
    })

    router.get('/refresh', async (req, res, next) => {
        try {
            let result = []
            for (const url of rssList) {
                const feed = await parser.parseURL(url)
                result.push(...feed.items)
            }

            result = result.sort((a, b) => (a.isoDate > b.isoDate ? 1 : 0))

            res.send({ result })
        } catch (err) {
            console.log(err)
            next(err)
        }
    })

    return router
}

In my server.js I import the apiRouter form api.js and simply added it as middleware:

    server.use('/api', apiRouter())

You can find the repo (branch) here: github.com/basti-n/rss-reader-sapp...

Collapse
 
mikenikles profile image
Mike

An alternative approach is to use Firebase Hosting for static assets and Cloud Run for the server-side part, instead of Cloud Functions. I recently wrote about that in detail, including a template repo.

I'd love to hear feedback if anyone gets a chance to review the article.

mikenikles.com/blog/firebase-hosti...

Collapse
 
bodhiz profile image
bodhiz

Has someone already setup an API proxy with this Firebase/Sapper configuration ?
Would be useful to call the local instance of functions when in dev mode.

Collapse
 
bketelsen profile image
Brian Ketelsen

brilliant! Thanks for writing it up

Collapse
 
tommertom profile image
Tommertom

Good writeup!

npm start does not work, but as mentioned by Diogo in the comments, that does not hurt.

Using node12, need to use engine 10 though

Collapse
 
kamalkech profile image
kamal

what about sapper js with firestore ??

Collapse
 
tommertom profile image
Tommertom • Edited
Collapse
 
bodhiz profile image
bodhiz

Thanks for the article. Really helpful !