Quick Nuxt.js SSR prototyping with Firebase Cloud Functions
Production grade superb CDN + no backend + SSR, all-in-one
tl;dr: Here’s an example repo of Nuxt.js + Firebase Cloud Functions + Firebase Hosting. This stack is suitable for prototype and production use
https://github.com/williamchong/nuxt-ssr-firebaseUpdate: I have wrote a new article on Typescript and Express support.
1. Introduction
I like vue.js a lot for its simplicity, and we use Nuxt.js a lot in both LikeCoin and for personal side projects. It’s best feature is its simple server-side-render (SSR) functionality and routing model.
However, running SSR means I have to spin up a node.js server, which can be troublesome at times. For prototyping, we certainly don’t want to spend too much time messing with VMs/docker/k8s; on the other hand, when a prototype made it into production, one would always regret not spending enough time on planning scalability and availability. Especially in LikeCoin, where we have a small team developing many MVPs in high velocity, very often prototypes have to be ready for production in a short time.
I find Firebase a very suitable platform in my case. It is simple enough to use for rapid prototyping, yet powerful enough to scale for production. There was an old tutorial made by Firebase describing how to deploy Nuxt.js to Firebase, but it was very much outdated so I decided to write an updated one.
Goal: We would write our Nuxt.js app in universal mode (SSR), deploy the Nuxt.js app onto Firebase Functions, and serve our static assets with Firebase Hosting with CDN.
2. Write our Nuxt.js app
Init the Nuxt.js app with npx:
npx create-nuxt-app firebase-ssr-app/src
For the create-nuxt-app
options, pick what is useful in your case, but make sure you picked Universal
mode for SSR support. For simplicity I chose none
for most options. Notice how I initialized the Nuxt.js project inside subdirectory /src
.
Write the Nuxt.js app in development mode as usual. If you don’t know how powerful Nuxt.js can be yet, here’re some examples.
3. Init the Firebase project
Create a Firebase project in Firebase console. Also make sure we have firebase-tool installed.
npm install -g firebase-tools
Init a Firebase project inside the project directory, in our case firebase-ssr-app/
cd firebase-ssr-app
firebase init
Select Functions
and Hosting
feature, then select the project we just created in Firebase console as default project. For other options, the default ones are good enough. After everything is done, firebase-tool would create 2 folders and 1 config file in firebase-ssr-app/
for us. functions/
,public/
and firebase.json
.
P.S. In case anything above feels unclear or confusing, Firebase has a good tutorial on how to initialize a project with firebase-cli.
4. Create the Nuxt renderer cloud function
Since Firebase Cloud Functions are powered by express, we can return a simple nuxt.render
middleware and it’s done.
Update: In newer versions of nuxt, you would need to wait for nuxt.ready()
.
I used nuxt-start
instead of nuxt
. nuxt-start
runs in production mode and is more lightweight, which makes it more suitable for cloud functions environment. I also required a nuxt.config.js
which will be copied from src/
directory in next step.
The dev
config option would build the Nuxt.js app in hot-reload mode, which could be disasterous since the cost of running cloud functions depends on execution time. We explicitly override it with false
.
The debug
option controls whether server side errors are displayed in webpage. It would be useful for debugging our deployment, but for now we leave it as false
.
In functions/package.json
, since Nuxt.js runs in node.js version ≥ 8, we need to specify:
"engines": {
"node": "8"
},
Also for any dependencies that needs to be run in server-side e.g. vuex, nuxtjs plugins, they need to be specified in the package.json
in functions/
. Here’s an example of what it should look like. Remember to run npm insatll
afterwards!
5. Modify the firebase.json config for deployment
For the final step, what we want to achieve is when we run firebase deploy
to deploy our Nuxt.js app, the following should happen:
- Nuxt.js app in
src/
should be built for production nuxt.config.js
and the Nuxt.js server build should be copied intofunctions/
for Firebase Cloud Functions deployment.- Nuxt.js client build and other static assets should be copied into
public/
for Firebase Hosting deployment. - Deploy the Firebase Cloud Functions and Firebase Hosting
Here’s an example of a firebase.json
that could do the job.
functions.predeploy
hook runs everytime before a new version of cloud functions is deployed. Here we run npm run build
inside src/
directory which would build the Nuxt.js app. We then copy the _nuxt/
and nuxt.config.js
folder into functions/nuxt
and functions/nuxt.config.js
.
Similarly, hosting.predeploy
hook runs before a new version of hosting is deployed. Since functions.predeploy
runs before hosting does, we can just copy the Nuxt.js client build in src/nuxt/dist/client/
into public/_nuxt/
, and static assets in src/static/
into public/
. _nuxt
here is the default public path for Nuxt.js app scripts, it is modifiable in nuxt.config.js
if it does’t look intuitive to you.
In hosting.rewrites
, we redirect all traffic to ssrapp
, which is the Nuxt render function we just defined in previous step.
6. Deploy your app
One just simply type:
firebase deploy
Then our Nuxt.js app would be deployed to both Firebase Cloud Functions and Firebase Hosting. Hooray!
The above instructions only cover the essential steps and concepts. For a complete config/code example, please visit my sample repo here. Inside the repo I also added extra steps for lint
, babel
, and a working CircleCI configuration.
Extra tips:
If you see a Nuxt.js server render error page like this, and is wondering how to debug:
Setting debug: true
in your cloud function’s Nuxt config would make the error more verbose, i.e.
const config = {
...nuxtConfig,
dev: false,
debug: true,
buildDir: 'nuxt',
};
const nuxt = new Nuxt(config);
Remember to turn debug
back to false
when you are done.
Edit (22/12/2019): Added
await nuxt.ready()
in cloud function, thank Andrew See for pointing out.
Edit (10/05/2019): I made a mistake infirebase.json
,cp -r src/nuxt/dist/client public/_nuxt
should becp -r src/nuxt/dist/client/ public/_nuxt
. The correct result looks likepublic/_nuxt/*.(js|css)
, so that these asset files are served at default path${your_domain}/_nuxt/
directly.