loading...
Cover image for How to host Nuxt.js application on firebase with a single command

How to host Nuxt.js application on firebase with a single command

slushnys profile image Zigmas Slusnys ・4 min read

Introduction

I wished to copy a famous website that is lingering across Europe where people share their found deals with others and like that save money (its pepper.com in case anyone is wondering (not that i'm doing this as an ad or anything)).

Therefore I set myself with a task - to learn Nuxt.js as it is a server side rendered (SSR) framework that encapsulates a Node.js server with Vue.js and renders content that is possible to be crawled by google's crawlers in order to be indexed and listed on the search giant.

You can check the demo at:

https://dealas-962d3.firebaseapp.com/

You can fine the full repository at:

https://github.com/slushnys/dealas

Challenges I faced in order to finish my application

  • Understanding the lifecycles of SSR and how to inject data into a page for it to be rendered and be indexed.
  • Understand firebase functions.
  • Learn how to host the code and functions to the cloud on google firebase.

What I have learned

Async data

Even though it's pretty clear in the documentation of nuxt.js (https://nuxtjs.org/guide/async-data) that this method is to retrieve the data of certain resource before the component is rendered.

Also, at that point of time you don't have the access to this within that method, as per documentation:


You do
NOThave access of the component instance throughthisinside asyncData because it is called before initiating the component.

Another thing I learned the hard way is that the asyncData method for retrieving information is only called for a page being rendered, not a regular component where you might try to be filling a select box with data from your api.

Firebase functions

For this part you will need to install firebase under your global npm packages

npm i -g firebase-tools

don't forget to login to firebase after with

firebase login

I had previous experience with running google cloud functions with python language, however I've never made myself learn how do javascript functions run on demand in Google Cloud Platform. This was about to change because i was determined to have my website deployments automated so i wouldn't need to manually do all that work.

// functions/index.js

const functions = require("firebase-functions");
const { Nuxt } = require("nuxt-start");

const nuxtConfig = require("./nuxt.config.js");

const config = {
  ...nuxtConfig,
  dev: false,
  debug: false,
  buildDir: ".nuxt",
  publicPath: "public"
};

const nuxt = new Nuxt(config);

let isReady = false;

async function handleRequest(req, res) {
  if (!isReady) {
    try {
      isReady = await nuxt.ready();
    } catch (error) {
      process.exit(1);
    }
  }
  await nuxt.render(req, res);
}

exports.ssrapp = functions.https.onRequest(handleRequest);

You might think what is that nuxt-start pacakge you see at the beginning of the index.js file 🤔 Well it's a package that takes away the overhead and lets you start the package without many dependencies in production.

On top of that, we don't want to render the unfinished page, therefore on our request handler function handleRequest we wait to see if nuxt is ready and render the full page only then when it is.

Exporting ssrapp is the most important for our application to work on cloud as the each request will be handled by our handleRequest function which is assigned to our google functions onRequest event listener.

But wait... who's going to call the ssrapp function when it's on the cloud and when will google cloud function know which function to call?

Firebase configuration

In this section I'll discuss my learning points on deploying nuxt to the cloud which in my case - was my biggest painpoint to figuring out why my application doesn't work. Lets first take a look at my ./firebase.json configuration file.

{
  "firestore": {
    "rules": "firestore.rules",
    "indexes": "firestore.indexes.json"
  },
  "functions": {
    "source": "functions",
    "predeploy": [
    "rm -rf functions/.nuxt && npm --prefix src run build && mkdir -p functions/.nuxt/dist && cp -r src/.nuxt/dist/ functions/.nuxt/dist && cp src/nuxt.config.js functions/"
    ]
  },
  "hosting": {
    "predeploy": [
      "rm -rf public/* && mkdir -p public/nuxt && cp -r functions/.nuxt/dist/client/ public/nuxt && cp -a src/static/. public/"
    ],
    "public": "public",
    "ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
    "rewrites": [
      {
        "source": "**",
        "function": "ssrapp"
      }
    ]
  }
}

There are different configurations for different services here as you can see by the keys of this dictionary, it includes - firestore, functions and hosting.

firestore keys' value includes the rules that you can set up for your firestore real time database. This should include your configuration to disable users reading your database for malicious purposes.

functions keys' value includes predeploy command, exactly this caused me the most of my troubles as I've taken this configuration from other tutorial I tried to apply to my use case. This resulted in my application crashing and not properly rendering when deployed.

hosting keys' value includes the predeployment script that takes files and uploads them to firebase hosting with a rewrite of source function to be the one we defined in our index.js file as ssrapp.

The reason my deployments were failing: this was actually pretty simple yet took me the longest to figure out. I have overwritten my nuxt.config.js buildDir default value of .nuxt/ to ../nuxt/. This in result caused some errors building some packages of 3rd party libraries I was using (especially Vuetify). I really hope this saves time to someone who's trying to deploy their website on firebase.

I haven't figured put much time soon after to figure out why the deployments weren't successful with a custom build directory of nuxt after I finally deployed my application, therefore I'm not going to contemplate on this.

Conclusion

It was a fun experience creating a Nuxt.js web application which is quite a powerful tool that one can use for basically any development needs, from a small website, to a bigger and meaner application release to production. Firebase also gives a very flexible real time database events that your application can listen on demand whenever certain action happens.

What challenges have you faced while trying to use Nuxt or Firebase? Let me know in the comment section.

Bonus

I've experimented with TypeScript so I refactored the repository to have typescript implementation of nuxt and new Vue 3 composition-api including it in ikelti.vue file if anyones interested in checking out how that integrates, i would be keep to receive some feedback.

Discussion

pic
Editor guide
Collapse
skflowne profile image
skflowne

Nice article very helpful !
Did you manage to make font-awesome work with it though ?
I can't get it to work with the "@nuxtjs/fontawesome" package, I followed their setup and it's working in dev but deployed icons do not display.
Actually, they are here but they all have a size of 0x0, it's like some css is missing.

Collapse
slushnys profile image
Zigmas Slusnys Author

I'd say you have to include the icons you want to use with JavaScript, also depends on the package of icons you want to use, I think there are several types of fa icons available that you have to explicitly define

Collapse
skflowne profile image
skflowne

Yes, I got all the packages there and I can actually go into devtools and make them appear in prod if I change css.
I would expect the package to add the css needed as part of the built assets in client but it's not there.
So I'm not sure what's wrong here, it seems to me that the specific config for firebase shouldn't be an issue.
I've seen they mentionned this css file in another issue where icons were too big in prod.
So I tried adding this in css: ["@fortawesome/fontawesome-svg-core/styles.css"] in nuxt.config.js but no luck either.

I don't understand why this is not working the nuxtjs doc clearly says you can include global css this way and the css file has the classes that I'm missing.

Thread Thread
slushnys profile image
Zigmas Slusnys Author

I think you could find an okay article about this here: medium.com/@kozyreva.hanna/nuxt-js...

Collapse
crow1796 profile image
Joshua

The function is looking for 3rd party libraries used in client.

Collapse
slushnys profile image
Zigmas Slusnys Author

Could you specify which function are you talking about and if that is a problem, and if so, what difficulties does it cause? Thanks

Collapse
crow1796 profile image
Joshua

The functions/index.js I got the same content as yours and when I try to deploy it and open in a browser it gives me this error

FATAL Cannot find module '@nuxtjs/axios'

. And this is my nuxt.config.js pastebin.com/LvxBi1yC

Thread Thread
slushnys profile image
Zigmas Slusnys Author

Did you build it with the non scripts provided in the src directory? Everything should be done with those scripts. If there are some missing dependencies it means the package.json may be missing something in functions directory.

Thread Thread
crow1796 profile image
Joshua

So do I have to add all of the 3rd party libraries installed inside the src/package.json into my functions/package.json?

Collapse
fiik346 profile image
Taufik Nurhidayat
⚠  Error: Cannot find module 'nuxt-start'
Require stack:
- /home/taufik/project/ssr-novelindo/functions/index.js
- /home/taufik/.nvm/versions/node/v12.18.3/lib/node_modules/firebase-tools/lib/emulator/functionsEmulatorRuntime.js
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:965:15)
    at Function.Module._load (internal/modules/cjs/loader.js:841:27)
    at Module.require (internal/modules/cjs/loader.js:1025:19)
    at require (internal/modules/cjs/helpers.js:72:18)
    at Object. (/home/taufik/project/ssr-novelindo/functions/index.js:2:18)
    at Module._compile (internal/modules/cjs/loader.js:1137:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1157:10)
    at Module.load (internal/modules/cjs/loader.js:985:32)
    at Function.Module._load (internal/modules/cjs/loader.js:878:14)
    at Module.require (internal/modules/cjs/loader.js:1025:19)
⚠  We were unable to load your functions code. (see above)
[hosting] Rewriting / to http://localhost:5001/novelindo/us-central1/ssrapp for l"

module nuxt-start is nothing?

Collapse
aonghas profile image
Aonghas Anderson

Should the pubicPath actually be a property of the build object?:

const config = {
...nuxtConfig,
dev: false,
debug: false,
buildDir: ".nuxt",
build: {
**publicPath: "public" <--- this property
}
};

Collapse
slushnys profile image
Zigmas Slusnys Author

I will have to get back to you on this one, I might have changed this, but check the repo I think it should be up to date.

Collapse
hinasoftwareengineer profile image
Hina-softwareEngineer

Nice. But it's not in English

Collapse
slushnys profile image
Zigmas Slusnys Author

That's true, I'll have to introduce multiple languages. It's in Lithuanian right now.

Collapse
joshuaholmes90 profile image
joshuaholmes90

The commands are for linux? I'm having issues, i'm developing via Windows Visual Studio Code, how would I achieve this on Windows?

Collapse
drkreso profile image
Kresimir Bojcic

Nice blogpost, but why is it so painfully slow?

Collapse
leyenda profile image
Daniel Jimenez Gutierrez

It is because the cold start of firebase functions