How to configure Firebase hosting to use pug instead of html file? - node.js

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

Related

Hosting static content in public directory on firebase

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

Skip express js view for API routes

I have my API and Website in same Express Js Project and needs to use ejs view for the website only. But not for the API to return JSON for API routes.
const app = express();
// For static website
app.use(express.static(path.join(__dirname, "public"), {
extensions : ['html']
}));
// API Routes
const bookRoutes = require("./routes/event.route");
app.use("/v1/books", bookRoutes);
// Website
// Set the view engine for dynamic header, footer on website
const ejs = require('ejs');
app.set('view engine', 'ejs');
API
/v1/books
Website
/index.html
How can I skip the view engine for my API routes and apply to /public folder only or even for selected files?
Error message, when I open /v1/books in Postman
{"message":"Failed to lookup view \"C:\\Users\\admin\\github\\test-app\\public\\v1\\books\" in views directory \"C:\\Users\\admin\\github\\test-app\\views\""}
The JSON was expected for /books API
{
id : 1,
name : 'book name'
}
For starters, routes are matched in the order you declare them. So, if you put this first:
// API Routes
const bookRoutes = require("./routes/event.route");
app.use("/v1/books", bookRoutes);
first, then it will get processed before it tries to match any of the static routes. But, this really shouldn't be an issue because there shouldn't be any files in your public folder that would match a request for /v1/books/xxx anyway.
As for the error for /v1/books, you will have to show us what the code is for bookRoutes. It appears that error is coming from that router. express.static() does not make errors like that.
How can I skip the view engine for my API routes and apply to /public folder only or even for selected files?
The app.set('view engine', 'ejs'); line just configures what view engine will be used if/when some code calls res.render(). There's nothing happening with the view engine on one of your API requests because you should never be calling res.render() in an API route. You are presumably calling res.json() instead.
You should ONLY have files in your public folder that you want express.static() to match. That's how you control it. Don't put something in that folder that shouldn't be served statically.

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.

expressjs - not sending the file from view config

In my node app, I configured the views folder, later simply I am passing the html name alone. now the html file need to load from using the views config + html file right. ( am I wrong!)
But it's not working. any one give me the suggestion please?
here is my code :
var express = require('express'),
http = require('http'),
jade = require('jade'),
app = express();
app.set('view engine', 'jade');
app.set('views', __dirname + '/views'); // i configured the path so i am passing file name alone on get.
app.get('/', function(req,res){
res.sendfile('index.html'); //it's not working
res.sendfile('views/index.html') //it works
});
http.createServer(app).listen(3000, function () {
console.log('Express server listening on port ');
});
thanks in advance
You appear to have a misconception about what the view engine is. The view engine takes some non-HTML code, and transforms it into HTML. Here, you have it set to use jade.
The view engine is only good with the res.render() function. res.sendfile() merely sends a file from the current directory -- not the views directory.
Using express if you want to serve some static HTML files. You can just put those files directly in public folder.
When server will get a GET request of / it will search for /public/index.html serve that as response. You don't have to add router for that /.
Other wise if you want to use some template views then you have to use some views engine.

Using holder.js on node.js with express web framework does work

I am using node.js with express web framework and with jade template parser.
I am following the bootstrap examples and have problems with holder.js.
I have put holder.js script into static files (public directory), having in app.js the following:
app.use(express.static(path.join(__dirname, 'public')));
I wanted to display some images with following code in jade template:
img(data-src='/holder.js/500x500/auto')
And it does not work. I checked through the browser and I am able to see holder.js file correctly, but adding any parameters is causing that main page is displayed instead.
I am suspecting that static files handler thinks that there is no such file as holder/500x500/auto and redirects to main page. How can I fix that?
Here is a Runnable with Express and Jade with a couple of Holder placeholders: http://runnable.com/U6IlNqsTu-cR6ZT8/holder-js-in-express-using-jade-for-node-js-and-hello-world
The two placeholders use different themes, with one using the auto flag.
server.js:
var express = require('express')
var app = express();
app.set('view engine', 'jade')
app.get('/', function(req, res){
res.render('index')
})
var server = app.listen(80, function(){})
views/index.jade:
doctype html
html
body
script(src='//cdnjs.cloudflare.com/ajax/libs/holder/2.3.1/holder.min.js')
img(data-src='holder.js/200x200/lava/auto')
img(data-src='holder.js/200x200/sky')
Take the leading slash out of the data-src attribute: holder.js/500x500/auto.

Resources