Hosting static content in public directory on firebase - node.js

I am hosting an app on Firebase making use of cloud functions and hosting. The former serves dynamic content in the form of a NodeJS express app with an API included, the latter serves static content such as css, javascript, etc. for my express app.
(Here is a good tutorial to get setup with express & firebase hosting)
Usually in express apps with the standard structure setup, one has a few folders created such as bin, routes, views, and public. Most of these are easily replicated in your firebase app.
Problem:
The issue is working with hosting & functions simultaneously.
Using the following index.js (in express known as app.js), I can successfully view & emulate my application locally using firebase emulators (functions, hosting, etc.)
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const express = require("express");
const engines = require("consolidate");
...
admin.initializeApp({
credential: admin.credential.applicationDefault(),
databaseURL: "https://....firebaseio.com"
});
// app routes
const usersRouter = require('./app/users');
...
const app = express();
// #Working locally only - for deplopyment, all static linkes must load resources as [hosting-domain.app.com]/css/styles.css
app.use("/public", express.static(path.join(__dirname,'../public')));
app.use(flash());
app.engine('pug', require('pug').__express)
app.set('views', __dirname + '/views');
app.set('view engine', 'pug');
// Use the createSession endpoint for login & verify each session
app.use(authenticate);
...
app.use('/user', usersRouter);
app.get('/', (req, res) => {
return res.redirect("/user/login");
});
app.get('*', (req, res, next) => {
res.render("404");
})
exports.app = functions.https.onRequest(app);
// api routes
exports.api = require('./api/api.js');
How can I serve static content successfully on firebase hosting while using firebase functions for dynamic content?
(answer follows)

You can add your static files in the Firebase Hosting's public directory.
In this case 'public' is my hosting directory. To store files at <domain>/css/.., just create a new folder called 'css' as shown and add your files.

scroll to bottom for TL;DR & solution
While writing the question - I found the solution after thinking about each of the firebase features and what they do individually.
Hosting static content locally vs deployed on firebase
When deploying, the path for assets e.g. [my-domain.web.com]/public/styles/my-styles.css which I can get locally doesn't load when deployed. Instead, I need to use [my-domain.web.com]/styles/my-styles.css.
Works Locally, but not when deployed:
[my-domain.web.com or localhost:5000]/public/styles/my-styles.css
Works Locally AND when deployed
[my-domain.web.com or localhost:5000]/styles/my-styles.css
The reason for this is anything inside of the public directory is hosted directly to your domain. Thus, using the express.static requires the same folder structure, etc. however the contents of the public directory are hosted (automatically) from the root of your domain.
In express, one needs to specify where your static content can be found which is then hosted by nodejs or other hosting feature of your choice.
BUT Firebase does the static content hosting for you automatically. Thus, there is no need to include or even have the following line:
app.use("/public", express.static(path.join(__dirname,'../public')));
as it only serves as a point of confusion (and also serves files locally only i.e. the point of confusion). Keep in mind, anything inside of the public folder takes precedence over dynamic content.
See the little blue note right above the middleware section:
Note: The static files in the public directory take precedence over the rewrites, so any static files will be served alongside the Cloud Functions endpoints.
The public directory being:
my-firebase-app
\ functions
\ public < ------ this one
\ ..other files
TL;DR
Given the folder structure:
my-firebase-app
\ functions /
\ views /
\ root.pub < ---- pug template file
\ public \ < ------ this one
\ styles /
\ my-styles.css < ----- styles file
\ ..other files
Ensure all your pug/handlebars/ejs, etc templates assume the public directory is the root of your domain, thus your static content is loaded as [localhost:5000 or yourdomain.web.app]/styles/my-styles.css
Normal Express App
Normally hosting your express app will use something like:
app.use("/public", express.static(path.join(__dirname,'../public')));
where in your pub (template file) you will have something like
link(rel="stylesheet", href="/public/styles/my-styles.css")
Firebase Functions + Hosting App, this should be changed to:
Remove the app.use("/public"...) function (firebase hosting hosts this to your root domain)
any links to static content should use
link(rel="stylesheet", href="/styles/my-styles.css")
Note the omission of the "/public" in the href

Related

Express static folder does not load properly

I'm having trouble with a Node APP that I am building.
App folder structure:
controllers // controllers
helpers // helpers
config // some config files
public // stores a built angular app
...
App URL 1: www.domain.com
App URL 2: www.another.com/myapps/app1
So this is how I set the static folder to load the assets:
app.use(express.static(__dirname + '/public'));
And this is how I would access the files in the static folder:
URL1: www.domain.com/assets/main.js
URL2: www.another.com/myapps/app1/assets/main.js
Now, the problem is that if I deploy the app on URL1 everything works perfectly. But deploying the app to URL2 gives me some issues.
The static files cannot be accessed on the app on URL2. I get 404 (Cannot GET ...).
www.another.com/myapps/app1/assets/main.js // returns 404
www.domain.com/assets/main.js // returns the JavaScript file.
There are multiple apps running on URL2 that is why I have used contexts to separate the apps.
My initial thoughts are that because of the additional contexts to the url on URL2, express is failing to set the static folder properly.
Could this be because the static folder is not being set properly?
express.static receives data from somewhere and applies this data to the root of your domain.
Actually you can get your files inside another folder by adding some structure.
In your case set app.use(express.static(__dirname + '/public')); and place assets inside app1 and then in myapps folders. All this should be inside public folder

How to configure Firebase hosting to use pug instead of html file?

I want to hosting my webapp to firebase hosting but it seems that firebase doesn't work with pug template because it return me 404 page not found with my "index.pug".
Thanks in advance.
Firebase Hosting won't compile any sort of frameworks or templates for you. It just serves static content. If you want to use a pug template, you'll have to compile it, then move the static assets into the Firebase Hosting public directory.
tested as of 2020-05-16. express + pug works on firebase hosting.
a few notes :
test with a private window of your browser ;
after you deploy, it may take a couple more seconds for the pug page to be available. i'm from Hongkong while i'm deploying the functions to the default location us-central, not sure if this is related though ;
the view you submit to "res.render", for example, make sure it's index and NOT /index ;
try these :
[project folder root]/functions/index.js
const functions = require('firebase-functions');
const path = require("path");
const express = require("express");
const app = express();
//###############################################################
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
//###############################################################
app.get("/index", function(req, res, next){
res.render("index", {title:"title lo, rendered from index.js"});
});
app.get("/page/:page_num", function(req, res, next){
const page_num = req.params.page_num;
res.render("page", {title:"page here", page_num: page_num});
});
// export to firebase functions
exports.app = functions.https.onRequest(app);
[project folder root]/functions/views/index.pug
h1 okok hello from pug
p this is a line
p another line here
p here is the title : #{title}
[project folder root]/functions/views/page.pug
h1 page
p this is page title : #{title}
p this is page number : #{page_num}
To do that you should use cloud functions. You can use handlebars or pug. Here it is an example using handlebars.
https://github.com/firebase/functions-samples/tree/master/template-handlebars
I haven't used pug, but it should be in the same way that handlebars.
You should divide the dynamic files in the functions folder (pug files goes there under views) and your CSS and JS should be in the public folder. The public only serves static content.
There is a really good video to get a step by step guide (also you can learn about CDN and caching content) here https://www.youtube.com/watch?v=LOeioOKUKI8

Firebase functions not serving static files with express.js and EJS view engine

I've been researching over this issue for the past month and I am not finding any useful solutions. I am currently trying to host a website using the firebase functions, along with express.js and the EJS view engine.
I'm going into the issue of serving static CSS files into the EJS files. I've looked all over but can't seem to find any proper answer that helps.
I have the following file structure (inside the functions folder):
public
views
index.js
Here's my index.js:
const functions = require('firebase-functions');
const express = require("express");
const ejs = require("ejs");
var path = require('path');
// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//
// exports.helloWorld = functions.https.onRequest((request, response) => {
// response.send("Hello from Firebase!");
// });
var app = express();
app.set('view engine', 'ejs');
// app.use('/static', express.static('public'));
app.use(express.static(__dirname + "/public"));
app.get("/", (request, response) => {
response.render("test");
})
// app.listen(5000, () => {
// console.log("Active app on port 5000");
// });
exports.webApp = functions.https.onRequest(app);
The page it is rendering (test.ejs):
<html>
<head>
<link rel="stylesheet" href="style.css" type="text/css">
</head>
<body></body>
</html>
And finally, the style.css file:
html {
background-color: black;
}
(The style.css is done like that along with the test.ejs only for testing as I was having a big issue with all of this)
I tried most of the solutions online, however all of them only work while running the express.js app locally and not through the firebase serve or deploy commands. I am totally lost in regards to this and any help is appreciated.
The TL;DR of the issue:
In normal NodeJS & Express hosting one needs to specify the directory of your static content. Since firebase hosting does this static content hosting for you (where the /public directory is hosted at your domain root),
Solution: you can do the following:
simply omit the app.use(express.static(__dirname + "/public")); (as this removes the duplicate hosting attempt that firebase hosting is doing for you)
all your styles, javascript, etc needs to omit the /public portion,
For example
from this:
link(rel="stylesheet", href="/public/styles/my-styles.css")
to this:
link(rel="stylesheet", href="/styles/my-styles.css")
You can test this:
Host your app with the original (faulty/incorrect static resource links), then browse to the static resource link - you should get a 404 or failure to load resource atleast.
Using the same "faulty" link, omit the ".../public..." portion of the url, your static content will render correctly.
This means firebase hosting is hosting all the content of the /public directory to the root of your domain.
Next, make the change by removing the static directory app.use() function and remove all traces of /public in your static content links (as shown above).
Browse to your page (that includes the altered static routes), your page will load correctly with all your styles, scripts, etc (assuming you don't have any faulty code ;) )
The Problem is because of the Google Cloud Plan.
In the beginning, all firebase users go with the Spark plan, which is completely free. In recently, Google Cloud may have blocked some features that we used free a year ago.
So, You need to go to Google Cloud console, then change the plan of your project you want to deploy. Then if you didn't register your payment card, you should do that for the firebase hosting or firebase functions feature.
A few minutes later, it loads all necessary files, such as a styling CSS, JS files that were missing when loading your deployed website.
Then enjoy your deployed website. ;)
If you are facing this problem in July 2021.
the firebase cloud function is not available for the Spark(free) plan.
You need to upgrade your project to a Blaze plan first (Worry not! This is Pay as you go plan. the spark plan is included in it, So you will be not charged until you exceed the limit).
To upgrade to the blaze plan you need to set up billing information in the Google Cloud platform which will be directed automatically when you opt for the upgrade.
Here is my code for EJS View engine setup in firebase function:
const functions = require('firebase-functions');
const express = require("express");
const app = express();
app.set("views",__dirname+"/tmp");
app.set("view engine","pug");
app.engine('pug', require('pug').__express);
app.get("/",p3p(p3p.recommended),function(req,res){
res.render("index");
});
app.get("/login",p3p(p3p.recommended),function(req,res){
res.render("login");
});
exports.main = functions.https.onRequest(app);
I hope you can relate it to the pug view engine and configure your setup in the firebase function for serving the static files.

How to host a static landpage and react on the same node server?

Currently, I have a static landing page hosted on firebase on the url "mycompany.com" (not the actual URL).
My node server (using express) runs on heroku and it contains my backend API and my react frontend (using react-router). In order to make everything run, I had to point this to a subdomain: app.mycompany.com
What I really wanted to do, was to have my landing page on "/" and have that to be the default redirect, all hosted in my node server (without having to have two servers and point to a subdomain).
I'm struggling to understand how could I set up this, or if have to change something to make this work.
Thanks in advance!
If you make a static build of your react app you can serve this build folder along with your static landing page. You'll still have to specify the path where you want your app traffic to go:
const express = require('express');
const app = express();
const staticLandingPage = express.static(path.join(__dirname, "./path/to/landingpage"));
const reactApp = express.static(path.join(__dirname, "./path/to/reactBuild"));
app.use("/", staticLandingPage);
app.use("/app", reactApp);

How to create a node based server to serve REST API and also deploy the application.

I am new to nodeJS server area, need help in understanding how to work with REST API (using express) and deploy the angular application over a singe node server and same ports.
By deploying i want to understand if user hit below url http://localhost:8000/<page_name> then the specified page should open.
And is user hit below url using get or post request
http://localhost:8000/api/<api_name> then a json or a text will be returned.
How to run both the thing over a single node server.
Lets assume, you have all your static files in the /public folder of you app. Generally spoken, if you are using express.static, you should also get your index.html because this is handled by default for each directory.
In your case, as you are using Angular, the routing is handled from the client side (SPA). You should only have one single index.html after building your Angular app. All files from your dist folder should then be placed into your /public folder. Then you need to make sure, that initial file serving provides your index.html like so:
In this example static files are served first, then your API and if nothing is found, you are getting back you index file.
const express = require('express');
const app = express();
// serve static files
app.static(__dirname + '/public'));
// serve your API
app.get('/api/welcome', function (req, res) {
res.send('Welcome');
});
// fallback routing (server side handling)
app.get(/.*/, function (req, res) {
res.sendFile(__dirname + ‘/public/index.html‘
});
app.listen(3000);
Next time please make sure, to give all necessary information in your question ;-)
With the help from Sebastian, so far I can find a solution but its not working when i am hitting URL for different pages.
const express = require('express');
const app = express();
app.use(express.static('public'))
Please provide your suggestions.

Resources