Express app on Firebase Functions locally - node.js

I created an Express app using Firebase Functions and am hosting the files on Firebase Hosting.
index.js (Firebase Functions):
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const express = require('express');
const parse = require('body-parser');
const cors = require('cors');
admin.initializeApp();
const app = express();
app.use(cors({ origin: true }));
app.use(parse.json());
app.use(parse.urlencoded({ extended: false }));
app.post('/create-url', (req, res) => {
console.log(req.body);
res.send(req.body);
});
exports.app = functions.https.onRequest(app);
firebase.json:
{
"hosting": {
"public": "public",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"function": "app"
}
]
},
"functions": {
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint"
]
},
"emulators": {
"functions": {
"port": 5001
},
"firestore": {
"port": 8080
},
"hosting": {
"port": 5000
},
"ui": {
"enabled": true,
"port": 4000
}
}
}
When I make an HTTP request to http://localhost:5000/create-url, req.body is emtpy, but when I make a request to http://localhost:5001/PROJECT_NAME_HERE/us-central1/app/create-url (I've hidden the project name), it works properly. I'm using Postman to make the requests, but it isn't working with my front-end code either :(. If I deploy to Hosting and Functions and access the app through the web.app domain, it works. I'm running firebase emulators:start to run locally.
Thanks in advance for your help!

Check out the bottom section of this page https://firebase.google.com/docs/emulator-suite/install_and_configure. 'Generate an auth token (Hosting emulator only)'.
Basically you have to generate an auth token and pass it as a flag in your build script or add it to your ci environment variables.
Let me know if it helps!

Related

Deploy an Express server that uses express.static to serve a build folder to Vercel

I've implemented an express server that uses express.static to serve the build folder created from a static docusaurus site in order to apply basic authentication to access the site. This is working great locally, but I'm running into troubles when deploying to Vercel.
Currently my configuration is allowing the deployed version on vercel to render the basic auth login page, but upon successful login I am directed to a page the states: "Cannot GET /"
I belive this is likely an issue with either my vercel.json configuration, or with my vercel's template settings.
My code is as follows:
index.mjs
import express from 'express';
import dotenv from 'dotenv';
dotenv.config();
const app = express();
app.use(express.json());
const authorize = ((req, res, next) => {
const auth = {login: process.env.USERNAME, password: process.env.PASSWORD}
const b64auth = (req.headers.authorization || '').split(' ')[1] || ''
const [login, password] = Buffer.from(b64auth, 'base64').toString().split(':')
if (login && password && login === auth.login && password === auth.password) {
return next()
}
res.set('WWW-Authenticate', 'Basic realm="401"')
res.status(401).send('Authentication required.')
});
app.use('/', authorize);
app.use('/', express.static('build'));
app.listen(3000);
console.log(`🚀 Server ready at http://localhost:3000`);
vercel.json
{
"version": 2,
"builds": [{
"src": "./index.mjs",
"use": "#vercel/node"
}],
"routes": [{"handle": "filesystem"},
{
"src": "/.*",
"dest": "/"
}
]
}
package.json - start script
"start": "node --experimental-modules index.mjs",
And my vercel template is set to other, with the start script set to npm start.
Any ideas would be greatly appreciated!
I had similar issue: serve some static content and use /api routes. All worked fine in local dev but throwed "Cannot GET/" in vercel.
My solution.
Build separately serverless and static content.
Route adjustements for /api calls and static content.
My final vercel.json:
{
"version": 2,
"builds": [
{
"src": "server.js",
"use": "#vercel/node"
},
{
"src": "public/**",
"use": "#vercel/static"
}
],
"routes":[
{
"src": "/api/(.*)",
"dest": "server.js"
},
{
"src": "/",
"dest": "public/index.html"
},
{
"src": "/(.+)",
"dest": "public/$1"
}
]
}
Notes
All static content is under /public (html,css,img,js,etc)
server.js is the main app: express and route definitions. Similar to yours.
vercel build log shows:
Generated build outputs:
12:28:55.974 - Static files: 12
12:28:55.974 - Serverless Functions: 1
12:28:55.975 - Edge Functions: 0
...
12:28:58.142 Done with "server.js"
Maybe from this point you can find a solution for your case.
Ok, I've been looking for a solution for this one a little, and turns out we have to serve index.html explicitly in express:
app.get('/', (req: Request, res: Response) => {
res.sendFile('index.html', {root: path.join(__dirname, 'public')});
});
given that you probably have index.html in public folder. That's what solved the issue for me.

Production React App Failing With 429 Error

I have built a react app webpage, i use node, express and react. I serve it within an ec2 ubuntu server with nginx, i also installed ssl and configured DNS but when I load the page, the first time it gives no problems, but when reloaded, i get this Server responded with 429 status code
The page is very simple itself, but stills failing and i can't solve the problem, i have read a lot of posts and blogs to fix it but none has worked (this only happens in production, in dev environment i get no error when running my node app).
Some of those said that you had to lazy load images and i did it, i have also tried loading components in timeout so requests are made every 1.5 seconds...
The main files that give me problems are the manifest.json and favicon.ico, they are accesible by url so i can't understand why (another 429 example).
Node server.js
Routes has nothing, just console logs
require('dotenv').config();
const express = require("express");
const app = express();
const path = require("path");
const cors = require("cors");
const allowedOrigins = [`http://localhost:${process.env.PORT}`];
var corsOptions = {
origin: function (origin, callback) {
if (allowedOrigins.indexOf(origin) !== -1 || !origin) {
callback(null, true)
} else {
callback(new Error('Not allowed by CORS'))
}
}
};
app.use(cors(corsOptions));
app.use(express.urlencoded({ extended: false }));
app.use(express.json());
app.use(express.static(path.join(__dirname, "/public")));
app.use("/", require("./routes/routes"));
app.get("*", (_, res) => res.redirect("/"))
app.listen(process.env.PORT, function () {
console.log(`Listening on port ${process.env.PORT}`);
});
App directory structure
This is my folders structure, public folder is the build that react script npm run build returns.
React app directory structure
Here you can see the structure, nothing special to what you get when run npx create-react-app. As you can see, there's a favicon.ico and manifest.json.
Manifest.json
{
"short_name": "Proximedica Bajío",
"name": "Proximedica Bajío - Oxígeno Medicinal",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
This is all, if you need me to provide some other file it would be a pleasure.
Thanks!

Firebase - Cloud Functions not running when deployed

I'm developing a blog application with firebase. I'm using express with ejs on this project.
PROBLEM - The project is working perfectly when I run them using the emulator, but doesn't work on firebase's hosting.
I've tried changing my code and re-deploying all the files many times. The function is visible in the 'functions' tab in firebase, and doesn't log any errors when I open the page.
I've checked the following ->
1) Firebase functions SDK, npm modules, and the CLI updated to the latest version.
// >>package.json<<
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"scripts": {
"serve": "firebase emulators:start --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"engines": {
"node": "10"
},
"dependencies": {
"ejs": "^3.1.3",
"express": "^4.17.1",
"firebase-admin": "^8.10.0",
"firebase-functions": "^3.7.0"
},
"devDependencies": {
"firebase-functions-test": "^0.2.0"
},
"private": true
}
2) The project is running on node 10.
3) I've added rewrites to my function in firebase.json
//>>firebase.json<<
"firestore": {
"rules": "firestore.rules",
"indexes": "firestore.indexes.json"
},
"hosting": {
"public": "public",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [ {
"source": "**",
"function": "app"
} ]
},
4) Tried the Port Numbers, and cross-checked other essential configurations.
5) My project is linked to google cloud, which has an active payment account.
6) Works fine when tested locally in a firebase emulator.
This is my function
// >>index.js<<
const functions = require('firebase-functions');
const express = require('express');
const app = express(); // express initialize
//firestore
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
app.listen(443); // listen for requests
// register view engine
app.set('view engine', 'ejs');
app.set('views', './views');
// middleware & static files
app.use(express.static('stats'));
// HANDLING REQUESTS
app.get('/', (req, res) => {
res.render('index', { title: "Blog by Zeal For Good" });
})
app.get('/index', (req, res) => {
res.redirect('/');
})
app.use((req, res) => {
res.status(404).render('404', { title: '404' });
});
exports.app = functions.https.onRequest(app);
I'm getting this message when I'm opening my website (https://zealforgood-blog.web.app/) ->
Install Express on 'functions' folder.
Install all modules that you need of the ExpressJs on 'functions' folder.
The Firebase with Cloud Functions need 'Cors' module running in ExpressJs. Install it.
The System needs a 'public' folder on project's base folder, another 'public' folder on the 'functions' folder.
After, run 'npm install' in project's base folder and on the 'fuctions' folder
delete 'index.html' and '404.html' on the 'public' folders genereted for Default by Firebase
change your files .html to .ejs;
Adjust your project like this:
// in firebase.json use: //
"source": "/**",
// in index.js: //
const cors = require('cors');
...
const app = express();
...
// set view engine setup EJS
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
// set this line below too
app.engine('ejs', require('ejs').__express);
app.use(cors({ origin: true }));
Note: This line below is a copy from the japanese site that do EJS render on Cloud Functions (https://qiita.com/kingpanda/items/aa9bdef2706857720058).
app.engine('ejs', require('ejs').__express);
Works to me!
Good Luck!

Firebase-hosting can't get to api routes node

I have a route for '/' but it serves the public html index file instead, I tried changing the root directory of my api to /api/** but it gives me a 404 error.
it works quiet well when I run it locally but when I deploy it I run into the issue of only having the index.html file.
here is the firebase.json file
{
"hosting": {
"public": "public",
"rewrites":[
{
"source":"/api/**",
"functions":"app"
}
],
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
]
}
}
and the index.js folder
const functions = require('firebase-functions')
const express = require('express')
\\.
\\.
\\requiring routes and middleware
app.use(helmet())
app.use(express.json())
app.use(cookieParser())
app.use(cors({
origin:"*",
credentials:true
}))
app.get('/',(req,res)=>{
res.status(200).send("works");
})
app.use(locationActions)
app.use(filterActions)
app.use('/auth',userAuth)
app.use('/adm',AdminRoutes)
app.use('/prod',prodActions)
app.use('/user',userActions)
exports.app = functions.https.onRequest(app);
.send() doesn't seem to work quiet well with firebase I just have to say something like response.json({message}), this will actually send the response. also mongoose connection is prohibited as I was using a free account so any connection outside firebase is rejected.

Error 403 Forbidden. No permission to access the URL

I am trying to migrate to firebase. But before that I gave it a small try by performing the following one. I have successfully added and deployed cloud function but am getting a error
Error: Forbidden
Your client does not have permission to get URL /app/lol from this server.
This is occurring on execution of following code:
const functions = require('firebase-functions');
const express = require('express');
const app = express()
// tried adding this as middleware
// const cors = require('core'({origin: true}))
var admin = require('firebase-admin');
// app.use(cors)
var serviceAccount = require("./ServiceAccount.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://<my-app>.firebaseio.com/"
});
app.get('/lol', (req, res) => {
res.send(`${Date.now()}`);
})
exports.site = functions.https.onRequest(app);
my firebase.json is as following:
{
"database": {
"rules": "database.rules.json"
},
"hosting": {
"public": "public",
"rewrites" :[{
"source" : "**",
"function" : "app"
}],
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
]
}
}
when i go to the route, it takes me to the google auth and after signing through correct account. I get the error.
Whats the issue here!
2 alternative ways to solve:
you should change
exports.site = functions.https.onRequest(app);
to
exports.app = functions.https.onRequest(app);
you could modify firebase.json
from
"rewrites" :[{
"source" : "**",
"function" : "app"
}],
to
"rewrites" :[{
"source" : "**",
"function" : "site"
}],

Resources