I put Nuxt and Firebase project in separate directories
/projects/journey-nuxt
/projects/journey-firebase
Build Nuxt
cd /projects/journey-nuxt
I assume you setup your Nuxt.js project with universal render mode(support both SSR and client).
npm run dev
I prefer to have separate buildDir for development and production, because I use symbolic link to link the .nuxt
directory to firebase project (running npm run dev
will overwrite the .nuxt
directory created by npm run build
, since they both share the same directory).
If you are using axios
to fetch data from the same server, you will realize
- On Nuxt development (
npm run dev
), the data is onhttp://localhost:3000
by default - On Firebase local development (
firebase serve
), the data is onhttp://localhost:5000
by default - On Firebase production, the data is on
https://PROJECT.firebaseapp.com
or custom domain name. - If you switch from Firebase to another environment like Docker, the url for the data might change again.
Refer to dotenv and Nuxt Axios Runtime Environment baseUrl.
Build
npm run build
NOTE: .nuxt
is generated
Firebase
cd /projects/journey-firebase
Setup Firebase Hosting and Functions
Copy .nuxt
to functions/.nuxt
.
cd functionsln -s /projects/journey-nuxt/.nuxt/ .
NOTE: You can either use symbolic links or copy the content
Dependencies
cd functionsnpm install firebasenpm install nuxt-start
NOTE: both nuxt
or nuxt-start
works
Depending on your nuxt project dependencies (/projects/journey-nuxt/package.json
), some packages are required on server side as well
bootstrap-vue
vuelidate
nuxtssr
Create Nuxt SSR app on cloud functions.
Edit functions/index.js
const functions = require('firebase-functions');const config = functions.config();// convert config to node environment variablesprocess.env.RUNTIME_API_URL = config.nuxtssr.runtime_api_url// process.env.RUNTIME_API_URL = 'http://localhost:5000'const { Nuxt } = require('nuxt')const nuxtConfig = { dev: false, // debug: true}const nuxt = new Nuxt(nuxtConfig)exports.nuxtssr = functions.https.onRequest(async (req, res) => { await nuxt.ready(); nuxt.render(req, res);})
Edit firebase.json
and add the following configuration.
{ "hosting": { "rewrites": [ { "source": "**", "function": "nuxtssr" } ] }}
NOTE: This will route all require to nuxtssr
. This setup is sub-optimal, but it will do for now.
Runtime Environment Variables
As mentioned earlier, we might need to setup some runtime environment variables for use cases such as axios
.
For local development (firebase serve
), create functions/.runtimeconfig.json
.
{ "nuxtssr": { "runtime_api_url": "http://localhost:5000" }}
For production
firebase functions:config:set nuxtssr.runtime_api_url="https://PROJECT.firebaseapp.com"
Local Development
firebase server
Access: http://localhost:5000/
Deployment
Deploy Hosting
firebase deploy --only hosting
Set Runtime Configuration
firebase functions:config:set nuxtssr.runtime_api_url="https://PROJECT.firebaseapp.com"
Deploy Functions
firebase deploy --only functions:nuxtssr
Access: https://PROJECT.firebaseapp.com
Web Setups
Copy /projects/journey-nuxt/static
to public
directory.
cd publiccp -R /code/vue/journey-nuxt/static/* .
Our you could use symbolic links.
cd publicln -s /code/vue/journey-nuxt/static/* .
Optimization
static /_nuxt
request
There are a few more things to be done for improved performance.
You shall notice the client script make requets to /_nuxt/2330881c6ecdfac16aec.js
, which is currently served by nuxtssr
cloud functions. We could copy content of .nuxt/dist/client
to public/_nuxt
, or use a symbolic link.
NOTE: This way we could make use of CDN/EDGE features of Firebase Hosting, not to mentioned reduce cost of cloud functions.
cd publicln -s ../functions/.nuxt/dist/client/ _nuxt
Edit firebase.json
{ "hosting": { "headers": [ { "source": "**/_nuxt/*.*", "headers": [{ "key": "Cache-Control", "value": "public, max-age=31536000" }] } ] }}
cache-control for dynamic content
Edit functions/index.js
exports.nuxtssr = functions.https.onRequest(async (req, res) => { res.set('Cache-Control', 'public, max-age=300, s-maxage=600') await nuxt.ready(); nuxt.render(req, res);})
public
: Marks the cache as public. This means that both the browser and the intermediate servers (meaning the CDN for Firebase Hosting) can cache the content. (Default isprivate
)max-age
: how many seconds browser and CDN can cache the content.s-maxage
: how many seconds CDN can cache the content.
For max-age and s-maxage, set their values to the longest amount of time that you're comfortable with users receiving stale content. If a page changes every few seconds, use a small time value. However, other types of content can be safely cached for hours, days, or even months.