My Vuetify Nuxt SSR (Universal) application on Firebase is hanging - firebase

Update: My approach to use 2 folders: functions and src in not working. I started to use another approach when folder for firebase functions is located inside of the src folder. This approach is implemented by winzaa123 user: winzaa123/nuxt2-vuetify-ssr-on-firebase and I could launch it on my Google firebase.
My Steps: Preparation of the project from scratch
Create a Nuxt app inside of the src folder:
Create a Nuxt app in src folder with create-nuxt-app src
Choose the package manager Npm
Choose UI framework Vuetify.js
Choose rendering mode Universal (SSR)
Create a Firebase Project
Create a Firebase project with firebase init
Select Firebase Hosting, Firebase Functions, Firebase Firestore, Firebase Storage, Emulators
Use public directory? (public)
Configure as a single-page app (y/N) - Yes
Create a package.json file in the root folder
{
"name": "test-nuxt",
"version": "1.0.0",
"description": "My fine Nuxt.js project",
"author": "Alex Pilugin",
"private": true,
"scripts": {
"postinstall": "cd functions && npm install",
"dev": "cd src && npm run dev",
"start": "cd src && npm run dev",
"serve": "NODE_ENV=development firebase serve",
"build": "cd src && npm run build",
"build-deploy": "firebase deploy --only hosting,functions",
"build-deployf": "firebase deploy --only functions",
"build-deployh": "firebase deploy --only hosting"
},
"dependencies": {
"nuxt": "^2.0.0"
},
"devDependencies": {
"#nuxtjs/vuetify": "^1.0.0"
}
}
Edit firebase.json file
{
"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": {
"public": "public",
"predeploy": [
"rm -rf public/* && mkdir -p public/_nuxt/ && cp -r functions/nuxt/dist/client/ public/_nuxt/ && cp -a src/static/. public/"
],
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"function": "nuxtssr"
}
]
},
"firestore": {
"rules": "firestore.rules",
"indexes": "firestore.indexes.json"
},
"storage": {
"rules": "storage.rules"
}
}
You can see that I plan to copy content of the src/.nuxt/dist/client into the public/_nuxt folder.
I am doing it since I found "publicPath": "/_nuxt/" inside of the src/.nuxt/dist/server/client.manifest.json
Nest Step is editing of the functions/index.js file
const functions = require('firebase-functions');
const { Nuxt } = require("nuxt");
//const { Nuxt } = require("nuxt-start");
const config = {
ssrLog: true,
dev: true, // Don't start in dev mode.
debug: true, //<----------------------- Debug logs
buildDir: "nuxt",
build: {
//publicPath: ''
//publicPath: '/_nuxt/', //Default: '/_nuxt/' <-- content of .nuxt/dist/client
/*
** You can extend webpack config here
*/
extend ( config, { isDev, isClient, isServer } ) {
if ( isServer ) {
config.externals = {
'#firebase/app': 'commonjs #firebase/app',
'#firebase/firestore': 'commonjs #firebase/firestore',
//etc...
}
}
}
}
}
const nuxt = new Nuxt(config);
let isReady = false;
async function handleRequest(req, res) {
console.log("nuxtssr is running...");
if (!isReady) {
console.log("isReady: " + isReady);
try {
console.log("waiting for nuxt.ready().......");
isReady = await nuxt.ready();
console.log("nuxt is ready");
} catch (error) {
console.log("ERROR.....................");
console.log(error);
//throw new functions.https.HttpsError('error in Nuxt', error);
process.exit(1);
}
}
console.log("waiting for nuxt.render().......");
/*
* res.set('Cache-Control', 'public, max-age=1, s-maxage=1');
* await nuxt.render(req, res);
*/
res.set("Cache-Control", "public, max-age=300, s-maxage=600");
return new Promise((resolve, reject) => {
console.log("before nuxt.render......");
nuxt.render(req, res, promise => {
console.log("inside nuxt.render......");
promise.then(resolve).catch(reject);
});
});
}
exports.nuxtssr = functions.https.onRequest(handleRequest);
I use $ npm start to launch the application locally:
Nuxt.js v2.12.2
Running in development mode (universal)
Listening on: http://localhost:3000/
I deploy the application using $ firebase deploy command but I cannot see any Frontend. My application is hanging.

From my understanding, firebase hosting is only for static files. If you're trying to do SSR, that implies that there's a server running and some processing happening on the server side. You may need to deploy this to Google Cloud Functions as a "serverless" app. Take a look at this tutorial for an example.
That said, I'm not super familiar with Nuxt, but I can see two things you potentially need to do differently if you are, indeed, deploying to the correct place.
First, the hosting.public property in firebase.json should be the path to the folder that contains your built project. Since you've said your project is being built in public/_nuxt, you might need to change this property to match, i.e. in firebase.json have
{
...
"hosting": {
"public": "public/_nuxt",
...
},
...
}
For what it's worth, in regular (i.e. non-Nuxt) Vue projects, the project gets built in the dist/ folder, so in my firebase.json file I have "public": "dist". That said, I haven't ever hosted a Nuxt SSR project on firebase so this may not be how it is structured.
Second, you likely need to run nuxt build or nuxt generate before you run firebase deploy so that the project is built to the target directory. The firebase deploy command really is just a fancy way to upload files onto Google's platform.
Hope this helps!

Related

dynamic routes in app dir in nextjs13 not working after deploying to firebase hosting

I am using NextJs 13 with experimental feature of "appDir"
while deploying to firebase hosting, dynamic routes become static path.
for example :
path app/[slug]/page.jsx become http://example.web.app/[slug]
You can see [slug] is consider as static page
Steps to reproduce
create nextjs13 app npx create-next-app#latest --experimental-app
create app/[slug]/page.jsx.
enable firebase experiments:enable webframeworks
make sure to enable billing.
firebase init hosting then firebase deploy
after successfully deployment, check http://your-domain.web.app/hello, you will see page not found but http://example.web.app/[slug] is working.
in firebase.json
{
"hosting": {
"source": ".",
"cleanUrls": false,
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
]
}
}
in next.config.js
/** #type {import('next').NextConfig} */
const nextConfig = {
experimental: {
appDir: true,
},
}
module.exports = nextConfig
in jsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"#/*": ["./*"]
}
}
}

Quasar vite doesn't return the ssrapp handler for my firebase functions

I started a Quasar V2 app using the quasar CLI with vite config and ssr build. I can't seem to return the firebase functions handler from dist/ssr/index.js as suggested in the docs.
I've tried this with the webpack Quasar V2 config and it works fine.
I ran firebase init then installed firebase-functions and firebase-admin in the root dir.
I changed the source of functions to dist/ssr where I built my ssr app. I also edited hosting to send requests to the function "handler".
firebase.json
"functions": {
"source": "dist/ssr",
"ignore": [
"node_modules",
".git",
"firebase-debug.log",
"firebase-debug.*.log"
]
},
"hosting": {
"public": "dist/ssr",
"rewrites": [
{
"source": "**",
"function": "handler"
}
],
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"]
},
I then returned the handler function, following the instructions from the quasar docs
server.js
import * as functions from "firebase-functions";
export const listen = ssrListen(async ({ app, port, ssrHandler }) => {
if (process.env.DEV) {
await isReady();
return app.listen(port, () => {
if (process.env.PROD) {
console.log("Server listening at port " + port);
}
});
} else {
return {
handler: functions.https.onRequest(ssrHandler),
};
}
});
I then ran "npm install" on both the root directory and the dist/ssr directory to make sure everything is fine.
then when I ran "firebase serve" I get back this result in localhost:5000 "Function us-central1-handler does not exist, valid functions are:" Whats going on :(

what is the best way to deploy nextjs app very first time and after than when a re-deployment is required?

I have a developed a sample application in nextjs and deployed this sample application using npm run build command.
I am following this (https://nextjs.org/docs/advanced-features/custom-server) article for a custom server deployment.
I have created a server.js file for custom server at the root level of build directory.
and modified the following in package.json:
"scripts": {
"dev": "node server.js",
"build": "next build",
"start": "NODE_ENV=production node server.js"
}
and next.config.js
module.exports = {
reactStrictMode: true,
};
const path = require("path");
module.exports = {
distDir: "build",
sassOptions: {
includePaths: [path.join(__dirname, "styles")],
},
env: {
DOM: process.env.DOM,
SERDOM: process.env.SERDOM,
PAPI: process.env.PAPI,
UAPI: process.env.UAPI,
PAEXIST: process.env.PAEXIST,
USEREXIST: process.env.USEREXIST
},
};
Now after that , I am a bit confused. Should I give the path of server.js using pm2 ?
server.js is using port 3000 and nextjs frontend app is using 3001.
Initially my deployment was not working and throwing 404 error but now it is working but here is some issue in it.
When I run the npm run build command to generate a new build/release and try to deploy again then my nextjs deployed app is not displaying the new changes.
Why ?
Does it get from somewhere else or from cached objects ?
How can I enable/display the updated deployment to my end user ?
Moreover, what is the best way to deploy nextjs app first time and after then latest deployment ?
================================package.json================================
{
"name": "New demo",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"bootstrap": "^5.0.2",
"next": "11.0.1",
"node-sass": "^4.14.1",
"react": "17.0.2",
"react-bootstrap": "^1.6.1",
"react-dom": "17.0.2",
"react-icons": "^4.2.0"
},
"devDependencies": {
"eslint": "7.29.0",
"eslint-config-next": "11.0.1"
},
"proxy": "http://localhost:3008"
}
Try like this
"scripts": {
"web:local": "env-cmd -f environments/.env.local node server.js",
"dev": "next dev",
"build": "next build",
"start": "next start"
},
where environments/.env.local is your local settins and some private info (you need to create environments folder and .env.local file). Server.js is your file, code example below
const { createServer } = require('https');
const { parse } = require('url');
const next = require('next');
const fs = require('fs');
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();
// these are keys to run your localhost with https protocol, you can // if you dont need it
const httpsOptions = {
key: fs.readFileSync('./environments/key.pem'),
cert: fs.readFileSync('./environments/cert.pem')
};
app.prepare().then(() => {
createServer(httpsOptions, (req, res) => {
const parsedUrl = parse(req.url, true);
handle(req, res, parsedUrl);
}).listen(3000, err => {
if (err) throw err;
console.log('> Ready on https://localhost:3000');
});
});
Then you run your application with npm run web:local
Why ?
Because you did a build, it's not dynamically updated
Does it get from somewhere else or from cached objects ?
No, its a "built" of your project
Moreover, what is the best way to deploy nextjs app first time and after then latest deployment ?
You upload your code to git, you link git to Vercel. In vercel you need to also add environment variables. Don't forget also, you need to.gitignore /environments folder!

Firebase: deploy same app to multiple Firebase-projects

I´m trying to deploy my code to two different Firebase-projects, one for development and one for production.
The my-app-dev project was already included and working, so I added the my-app (for production) with firebase use --add and selected the my-app.
This is how my Firebase-config looks now:
.firebaserc
{
"targets": {
"my-app-dev": {
"hosting": {
"app": [
"my-app-dev"
]
}
}
},
"projects": {
"default": "my-app-dev",
"prod": "my-app"
}
}
firebase.json
{
"hosting": [
{
"target": "app",
"public": "dist/app",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
}
],
"functions": {
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint",
"npm --prefix \"$RESOURCE_DIR\" run build"
]
}
}
As long as I deploy to my default project, everything works fine, but when I try to firebase deploy -P prod it shows the following error:
Deploy target app not configured for project my-app. Configure with:
firebase target:apply hosting app <resources...>
I tried to find some more information about this command, but still don´t know what to put for resources. Overall I feel like the .firebaserc has a very confusing structure.
I had the same problem but in a different fashion.
The project I have is an Angular 11 project, which has 4 different environments - the same behaviour of deploying to the default project (env) was fine but as soon as I tried to deploy to a different environment (firebase project), it failed with the same error:
Deploy target ___ not configured for project ___. Configure with:
I resolved this by adding to my .firebasesrc > targets:
{
"projects": {
"default": "default-project"
},
"targets": {
"default-project": {
"hosting": {
"frontend": [
"default-project"
]
}
},
"staging-project": { // Added this entry.
"hosting": {
"frontend": [
"staging-project"
]
}
}
}
}
According to this comment in GitHub it cannot be done without a "hacky" method like swapping the firebase.json programmatically during deploying.
Right now the Firebase CLI is built to treat projects as anonymous
environments that are functionally identical. This is important to be
able to deploy the same assets to multiple projects without having to
alter the code (including in firebase.json).
To achieve what you want, you'll need to set up a dev and prod folder,
each with their own firebase.json and each with a target only for that
specific project. Deploying different assets to different projects is
not supported now and is unlikely to be supported in the future
(however, we may allow configuring the location of firebase.json via a
flag at some point).

Firebase hosting rewrites to functions not working as expected

I am building a static website with HTML, CSS & JS and hosted on firebase hosting and connected a custom domain. The only serverside function I need is to send mail from the contact form. For this, I am trying to use Firebase cloud function. I have initialized functions on the same project and trying to use firebase hosting rewrites to rewrite the request to mydomain.com/contact to the contact function. But when I try to access the mydomain.com/contact in the browser it shows the below 403 Forbidden error message.
Error: Forbidden
Your client does not have permission to get URL /contact/contact from this server.
firebase.json
{
"hosting": {
"public": "build",
"rewrites": [{
"source": "/contact",
"function": "contact"
}],
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
]
},
"functions": {
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint",
"npm --prefix \"$RESOURCE_DIR\" run build"
]
}
}
The cloud function (not implemented the actual logic)
import * as functions from 'firebase-functions';
exports.contact = functions.https.onRequest((request, response) => {
response.send("<h1>Contact<h1>");
});
I am using Firebase spark plan.
This works for me:
"rewrites": [
{
"source": "**",
"function": "myApp"
}
]
And in the express function,
import * as functions from 'firebase-functions';
import express from 'express';
const app = express();
app.get('/contact', (req, res) => {
res.send("<h1>Contact<h1>");
};
export const myApp = functions.https.onRequest(app);
I had the same issue this week. I had a rewrite from /user to a function called user and I was getting the response Your client does not have permission to get URL /user/user from this server.
I just deleted the functions from the firebase console then deployed them again and now they work. There must be some occasional bug with deploying functions like this and/or using hosting rewrites where the rewrite path gets appended to the function url path on the server.

Resources