Nuxt SSR firebase functions returns 504 timeout - firebase

I'm trying to implement Nuxt with SSR in Firebase hosting (using Firebase functions), but after my function is triggered I keep getting an '504 timed out waiting for function to respond'.
My Firebase function:
const functions = require("firebase-functions");
const { Nuxt } = require("nuxt");
const express = require("express");
const app = express();
const config = {
dev: false,
buidlDir: 'src',
build: {
publicPath: '/'
}
};
const nuxt = new Nuxt(config);
function handleRequest(req, res){
console.log('handling request');
//res.set('Cache-Control', 'public, max-age=600, s-maxage=1200')
nuxt.renderRoute('/')
.then(result => {
console.log('result: ' + result.html);
res.send(result.html);
})
.catch(e => {
res.send(e);
console.log(e);
})
}
app.get('*', handleRequest);
exports.ssrApp = functions.https.onRequest(app);
I also tried with:
function handleRequest(req, res) {
console.log("log3");
res.set("Cache-Control", "public, max-age=300, s-maxage=600");
return new Promise((resolve, reject) => {
nuxt.render(req, res, promise => {
promise.then(resolve).catch(reject);
});
});
}
I also have node vs8 as default for my functions because I read that that could give problems. :
"engines": {
"node": "8"
},
But with the same result. My function is being triggered but it always times out, btw: I have this problem serving locally and when trying to deploy to Firebase itself.
Let me know if you need more information/code to try to help and see what the problem could be.

First, if you want to find out what caused it, use debug option.
Second, if you face the timeout error, check the path is valid.
If you success build Nuxt and nuxt.render, the error is processed by Nuxt, and Nuxt show this error page.
In other words, if you don't see Nuxt error page, the cause may be not related with Nuxt.
I also stuck 4 hours due to timeout error, and I finally found out the cause was the contents of publicPath.
Please check these 2 things.
buidlDir is valid ?
The path of buildDir is valid ? You should check .nuxt folder is deployed to your cloud functions successfully.
publicPath contents is uploaded successfully?
The builded contents in .nuxt/dist must be uploaded to Firebase Hosting. You should check it manually.
Type URL to address bar ex) https://test.firebaseapp.com/path/to/file.js
Finally, I post a URL of
my sample project, using Nuxt and Firebase.
I also stucked like you and it made me rally tired. I'm really happy if this answer helps someone like me.
PS: When you build Nuxt in functions folder, nuxt start is failed. Be careful. In my project, I build it in root, and when deploy, I copied it.
Nuxt SSR with Firebase Integration

I got the same problem because Nuxt is not ready yet (promise is undefined)
So you can try to add nuxt.ready() after new Nuxt()
Example:
const functions = require('firebase-functions');
const express = require('express');
const { Nuxt } = require('nuxt');
const config = {
dev: false
// Your config
};
const nuxt = new Nuxt(config);
const app = express();
nuxt.ready(); // <---------- Add this!
async function handleRequest(req, res) {
res.set('Cache-Control', 'public, max-age=1, s-maxage=1');
await nuxt.render(req, res);
}
app.get('*', handleRequest);
app.use(handleRequest);
exports.ssrApp = functions.https.onRequest(app);
Ref: https://github.com/nuxt/nuxt.js#using-nuxtjs-programmatically

Related

Deploying Nextjs Puppeteer Web Scraper to Vercel

I am working on a webscraper with Nextjs and Puppeteer. Everything works well on localhost, but once I deploy, the Vercel deployed version gives a 500 internal server error when I try to access puppeteer. I've looked at some guides on deploying a serverless puppeteer function to Vercel, and some suggested playwright, but it still doesn't work when I deploy it. Here are the code snippets using puppeteer, and here is the github repo: https://github.com/hellolol2016/EquilibriNews
import chromium from "chrome-aws-lambda";
import playwright from "playwright-core";
//FUNCTION TO RUN SEPARATE SCRAPE FUNCTIONS
async function scrapeInfiniteScrollItems(page, getNews, src) {
let items = {};
try {
items = await page.evaluate(getNews);
} catch (e) {
console.log(e);
console.log("bad source", src);
}
return items;
}
//FUNCTION TO SET UP BROWSER AND RETURN
export default async function handler(req, res) {
const browser = await playwright.chromium.launch({
args: chromium.args,
executablePath:
process.env.NODE_ENV !== "development"
? await chromium.executablePath
: "/usr/bin/chromium",
headless: process.env.NODE_ENV !== "development" ? chromium.headless : true,
});
const page = await browser.newPage();
page.setJavaScriptEnabled(false);
page.setViewport({ width: 1280, height: 3000 });
await page.goto("https://www.foxnews.com/politics");
let items = await scrapeInfiniteScrollItems(page, extractFox, "fox");
//NOTE: I didn't include the extractFox function because it didnt use any puppeteer functions
allArticles.fox = items;
await browser.close();
res.status(200).json(allArticles);
}
I've tried some other articles about this like https://puppeteer-screenshot-demo.vercel.app/?page=https://whitep4nth3r.com (This one uses a deprecated version of Node) and https://ndo.dev/posts/link-screenshot (this is what I'm trying right now).
I'm guessing the solution is to install a different library that works in a similar way as playwright / puppeteer / chrome-aws-lambda but can still be used when deployed as a serverless function on Vercel.

Calling an API using Axios and Firebase Cloud Functions

I want to make a Google Cloud Function calling an external API for me. After some research on Google I found the way using Axios. The call is actually working, when I'm using it on my own nodejs but when I want to deploy the function to Google Cloud functions I'm always getting an error (Function cannot be initialized. Error: function terminated.)
I'm on the Blaze plan.
const functions = require("firebase-functions");
const axios = require("axios");
exports.getData = functions.https.onRequest((req, res) => {
return axios.get("http://api.marketstack.com/v1/eod?access_key='myAccessKey'&symbols=AAPL")
.then((response) => {
const apiResponse = response.data;
if (Array.isArray(apiResponse["data"])) {
apiResponse["data"].forEach((stockData) => {
console.log(stockData["symbol"]);
});
}
}).catch((error) => {
console.log(error);
});
});
Could someone please help me?
EDIT: I finally fixed it: the mistake was, that I ended up with two package.json files (one in the directory where it should be and one which I actually didn't need). When I was installing the dependencies with npm install, axios was added into the wrong package.json file. Unfortunately the other package.json file made it up to the server and I ended up with a package.json file without the necessary dependencies on the server and thus this made the error occur.
I didn’t test your code but you should return "something" (a value, null, a Promise, etc.) in the then() block to indicate to the Cloud Function platform that the asynchronous work is complete. See here in the doc for more details.
exports.getData = functions.https.onRequest((req, res) => {
return axios.get("http://api.marketstack.com/v1/eod?access_key='myAccessKey'&symbols=AAPL")
.then((response) => {
const apiResponse = response.data;
if (Array.isArray(apiResponse["data"])) {
apiResponse["data"].forEach((stockData) => {
console.log(stockData["symbol"]);
});
}
return null;
}).catch((error) => {
console.log(error);
});
});
You probably want do more than just logging values in the then() e.g. call an asynchronous Firebase method to write to a database (Firestore or the RTDB): in this case take care to return the Promise returned by this method.

Why am I getting a CORS error in the browser but not in Postman?

I am using the Firebase emulators to serve Firebase functions on localhost. If I test my functions using Postman, this all works using the following request:
http://localhost:5001/project-XXXX/us-central1/api/users
Then, I fire up my Next.js application on port 3000 and try to use Axios to get the same data as follows:
useEffect(() => {
axios
.get(
"http://localhost:5001/project-XXXX/us-central1/api/users"
)
.then((res) => {
console.log(res);
})
.catch(function (error) {
console.log(error);
});
}, []);
However, now I'm getting a CORS error: "Access to XMLHttpRequest at 'http://localhost:5001/project-XXXX/us-central1/api/appointments/availability' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource."
In response to similar questions I have tried changing region and starting chrome with web-security disabled, which was both unsuccesful in solving the matter.
Does anyone have an idea as to why I am getting this error when requesting from the browser (and not from Postman)?
For full information, my index.js file of the Firebase cloud functions using Express.js:
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const express = require("express");
const cors = require("cors");
const authMiddleware = require("./authMiddleware");
const { user } = require("firebase-functions/v1/auth");
admin.initializeApp();
const app = express();
const { users, updateUser } = require("./routes/users");
// user-related routes
app.use("/users", users);
const api = functions.https.onRequest(app);
// export as Firebase functions
module.exports = { api: api, updateUser: updateUser };
Thanks jub0bs! My problem was solved by installing cors (npm install cors) and adding the following to my index.js:
const cors = require("cors");
app.use(cors());
This enables CORS for all origins, so only use for development and change before you go to production.

Angular Front - NEST JS on Cloud Functions - has been blocked by CORS policy: No 'Access-Control-Allow-Origin'

I have a forehead in ANGULAR - IONIC.
I have an API with NESTJS.
I hosted the API on the cloud functions of firebase with this tutorial: https://www.youtube.com/watch?v=IVy3Tm8iHQ0&ab_channel=Fireship
When I run the API locally (npm a start) it works perfectly!
I have a system for the cors which is the following (stored in main.ts):
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const options = new DocumentBuilder()
.setTitle('MY WEB API')
.setDescription('READ ONLY')
.setVersion('1.0')
.addTag('TAG')
.build();
const document = SwaggerModule.createDocument(app, options);
SwaggerModule.setup('api', app, document);
app.use(helmet());
const whitelist = ['http://localhost:8100/', 'http://localhost:8100', '*'];
app.enableCors({
origin: function (origin, callback) {
if (whitelist.indexOf(origin) !== -1) {
console.log('allowed cors for:', origin);
callback(null, true);
} else {
console.log('blocked cors for:', origin);
callback(new Error('Not allowed by CORS'));
}
},
allowedHeaders:
'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept, Observe',
methods: 'GET, OPTIONS',
credentials: true,
});
await app.listen(3000);
}
bootstrap();
Unfortunately when I run the api on the firebase environment (firebase serve --only functions) I get the following error:
(I checked, I can use it well with Postman)
I have tried many things to fix that:
Directly from the controller
#Get('ByName')
#Header('Access-Control-Allow-Origin', 'https://localhost:8100')
async findByName(#Query('name') name: string) {
return await this.personnesService.findByName(name);
}
Desactivate the cors
const app = await NestFactory.create(AppModule, {
logger: console,
cors: false,
});
Activate them like that
const app = await NestFactory.create(AppModule);
const corsOptions = {
methods: 'GET',
preflightContinue: true,
optionsSuccessStatus: 204,
credentials: true,
origin: ['http://localhost:8100/', 'http://localhost:8100'],
};
app.enableCors(corsOptions);
I've checked on the cloud functions, the code is up to date (you never know!) and the requests arrive well.
In fact, when I launch the request and I get the error, the API still executes the request (a console.log in the API allows to check it) but doesn't seem to return the result, from what I understand because of the cors.
In reality, it doesn't go through the main.ts either since this console.log doesn't appear. I don't know how to make it go through there.
How can I activate the cors (or deactivate them?) so I don't get the error anymore?
Some information about my versions:
NESTJS API :
"#nestjs/common": "^7.5.1",
"firebase-functions": "^3.13.0",
"#types/express": "^4.17.8",
"typescript": "^4.0.5
"node": "12"
My FRONT:
"#angular/common": "~10.0.0",
"#angular/core": "~10.0.0",
"ts-node": "~8.3.0",
"typescript": "~3.9.5"
I now understand my mistake. My configuration is correct, but it's not in the right place to make it work in production!
In a classic NESTJS application, the main.ts is used to launch the application. In my case, having followed this tutorial https://fireship.io/snippets/setup-nestjs-on-cloud-functions/, the index.ts replaces the main.ts.
So the solution is to move my configuration to the index.ts!
Here is my new function.
const server = express();
export const createNestServer = async (expressInstance) => {
const app = await NestFactory.create(
AppModule,
new ExpressAdapter(expressInstance),
);
const corsOptions = {
methods: 'GET',
preflightContinue: true,
optionsSuccessStatus: 204,
credentials: true,
origin: ['http://localhost:8100/', 'http://localhost:8100'],
};
app.enableCors(corsOptions);
return app.init();
};
createNestServer(server)
.then((v) => console.log('Nest Ready'))
.catch((err) => console.error('Nest broken', err));
export const api = functions.https.onRequest(server);
Don't forget to build before relaunching!
npm run build
firebase serve --only functions
firebase deploy --only functions

Flutter Firebase Config Variables

I've set some config variables using the command firebase functions:config:set algolia.appid="app_id" algolia.apikey="api_key", but how do I utilize them in my Flutter app? I have firebase_core installed.
In TypeScript you would write the following:
import * as admin from 'firebase-admin';
admin.initializeApp();
const env = functions.config();
console.log(env.algolia.appid);
But what about in Dart and Flutter?
Thanks
The configuration variables you set through firebase functions:config:set are (as the command implies) available only in Cloud Functions. They're not in any way propagated to client-side application by this command. In general that'd be an anti-pattern, as such configuration variables are often used for keeping trusted credentials.
If you have a use-case where the value needs to be available in the client-side application too, you have a few ways to do that:
Create an additional Cloud Functions endpoint where you expose the value of the configuration variable. Typically this would be a HTTPS or Callable function, which you then call from your client-side code.
Push the value into another place where your application code can read it from at the same time that you call firebase functions:config:set. This could be a configuration file of your app, or even a .dart file that you generate.
I also ran into this problem and found myself on this S.O. thread. I tried following Frank van Puffelen's suggestion above.
In functions/.runtimeconfig.json:
{
"algolia": {
"appid": "ID",
"apikey": "KEY"
},
"webmerge": {
"key": "KEY",
"secret": "SECRET",
"stashkey": "STASH_KEY"
},
}
In functions/index.ts:
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
. . .
const cors = require('cors')({origin: true})
const envObj = functions.config()
. . .
export const getEnv = functions.https.onRequest((req, resp) => {
cors(req, resp, () => resp.status(200).send(JSON.stringify(envObj)));
});
. . .
NOTE: I used the cors package to get around CORS errors when working locally. I would get these errors when localhost:5000 (Emulator hosting) called localhost:5001 (Emulator functions).
In web_flutter/main.dart:
Future<Map<String, dynamic>> fetchEnv(String functionsURL) async {
var response = await http.get('${functionsURL}/getEnv');
return json.decode(response.body);
}
Future<void> main() async {
try {
var functionsURL = 'FUNCTIONS_URL';
var app = fb.initializeApp(firebase app details);
if (window.location.hostname == 'localhost') {
app.firestore().settings(Settings(
host: 'localhost:8080',
ssl: false,
));
functionsURL = 'http://localhost:5001';
}
var env = await fetchEnv(functionsURL);
var searchClient = Algolia.init(
applicationId: env['algolia']['appid'],
apiKey: env['algolia']['apikey']);
runApp(MyApp(
repository: Repository(app.firestore(), searchClient),
authentication: Authentication(app.auth())));
} on fb.FirebaseJsNotLoadedException catch (e) {
print(e);
}
}
Once I confirmed that this was working locally, I was able to use firebase functions:config:set to set this data in the live Functions environment and deploy my updated hosting and functions with firebase deploy.

Resources