Express static folder does not load properly - node.js

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

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

cannot reach a file on server though I exposed using express

In my project I need to store some text files dynamically and end user should be able to download them from browser. I know the best way is using object store like MINIO or S3 but unfortunately the only way I have is to use in memory storage. So what I am trying to do is: I made a public folder and I exposed it using the following code:
var express = require('express');
var app = express();
//setting middleware
app.use(express.static( 'public')); //Serves resources from public folder
var server = app.listen(5000);
it is as simple as that. Then for testing just to make sure I can download and reach a file I created a public folder with t.txt file in it and when I try:
http://localhost:5000/public/t.txt
I get
So why am I not getting it? Also is what I am trying to achieve will be a good match to scenario and is it doable at all?
When you're not specifying a path in app.use(), your app will serve the contents of the directory you're pointing to in express.static() on the root path. Try this:
http://localhost:5000/t.txt
That said, if you want it to be accessible at /public/t.txt, just specify the path:
app.use('/public', express.static('public'))
At first use the following line of code:
app.use(express.static(__dirname+'/public'));
This means that your home directory for static HTML pages is in the "public" folder. Note that the "__dirname" points to the directory of the current js file.
After that, call the following URL from the browser or POSTMAN:
http://localhost:5000/t.txt
As you can see, there is no need to write http://localhost:5000/public/t.txt referring to the "public" folder because you have already specified that in the app.use line.

ExpressJS static file serve always serves the same file

I have a expressJs setup which looks like this:
// Imports...
const app: express.Application = express();
const port: number = 3001;
const listener = new StatementListenerAPI();
app.use('/listen', listener.getRouter());
app.use('/welcome', router);
if (fs.existsSync('./client')) {
// Running in prod environment with pre built client directory. Serve this.
app.use(express.static('./client'));
}
app.listen(port);
So I have some routers connected to the express app, and at the bottom I declare that the directory client should be served statically. This directory contains an index.html as well as lots of JS, CSS and PNG files. However, no matter which URL I try to access from the express server, it always shows the code of the index.html within the statically served directory. The references to the JS and CSS files used inside the index.html also just return the code of the index.html.
I am using ExpressJS 4.16.3
What am I doing wrong?
Edit: So technically it works if using __dirname + '/client' instead of ./client. What I am now getting is that, when making GET requests from e.g. Postman (therefore "hand-crafting" the HTTP requests), I am always getting the correct results. If I call the resources from within my web browser, it still always shows the website (resolves the index.html). However, now all resources like JS and CSS scripts are being resolved properly, so apperantly Chrome resolves those dependencies properly, I am just wondering why I am still getting the contents of index.html as result when requesting some of the assets or some of the express endpoints via Chrome. API calls via code are working fine, so its only why manual chrome requests show this weird behaviour, at this point I am only asking out of curiosity.
Answer to your original question:
The path supplied to express.static should be relative to the directory from where you launch your node process or an absolute path. To be safe construct an absolute path (ie from the current directory or file). For example:
app.use(express.static(__dirname + '/client'));
Regarding your followup question:
I assume this is because Chrome uses heavy caching and it thinks this folder should return the html file. You can try and reset all caches in Chrome, or just for the page.

How to include static html pages into my NodeJS project

I have a NodeJS project setup. I want to integrate my static html pages into it. Where can I do it ?
Thank you !
To expose static html, create a directory named "public" that lives next to your primary application file (app.js).
Assuming you are using the express framework, you can expose this directory by using the following code:
app.use(express.static(__dirname + "/public"));
where
var app = express();
Subsequently, any request to "/{something}" will attempt to resolve a route to a static file in your public directory, so if you had a file named {something} in that directory it would get served right up.

How to serve a static site within keystone?

I have one keystone based site and a completely static one.
I would like to integrate the static one into the first one.
All the requests to "/" will serve the static one but requests under "/resources" would serve the keystone site.
basically:
"/" would serve the static folder 'site'
"/resources" would serve the keystone app
At the moment my structure is:
public
| - keystone
| - site
And my keystone static option is set on 'public'
'static': 'public'
How would I go to set up these routes?
I was thinking of using a middleware in this way:
app.use("/", express.static(__dirname + "/site"));
app.use("/resources", express.static(__dirname + "/keystone"));
But how would I do it in keystone?
You don't need any extra middleware.
With the option 'static' : 'public', any static content placed in the public folder within your project folder will be served as static content.
It is also possible to define more than one directory by providing an array of directories like this 'static' : ['public','site'] this way everything in the directory site will also be served.
I assume you want some kind of index.html to be loaded when someone hits your sites root. You can achieve tha by adding a redirect to your /routes/index.js.
Simply add keystone.redirect('/', '/index.html'); or whatever the filename is you want to get served. You also have to remove the keystone route to '/'
I achieved this by just editing one line in the routes/index.js file, and adding the directory name in the keystone.init() "static" setting. Now all files in "client" directory get served under the root of my site URL, and Keystone lives at www.yoursite.com/resources
Step 1
Edit routes/index.js:
Change:
app.get('/', routes.views.index);
To:
app.get('/resources', routes.views.index);
Step 2
edit keystone.js:
Change
keystone.init({
...
'static': 'public',
...
To
keystone.init({
...
'static': ['public','client']
...
```
Step 3
Add site files, including index.html, into a new folder called "client".
Keep in mind that Node will also load files that are in the "public" folder. For example, currently keystone has a file called "logo-email.gif" in public/images. This means that www.yoursite.com/images/logo-email.gif will load that image. It turns out that if there is both a file public/images/logo-email.gif and client/images/logo-email.gif, then the image in public takes precedence. If you reverse the 'static' attribute in keystone.init() so that it says ['client','public'] then the image in "client" takes precedence
KeystoneJS v5, I used this way:
module.exports = {
keystone,
apps: [
new GraphQLApp(),
new StaticApp({path:"/", src:'public'}), // for your static site
new StaticApp({path:"/resources", src:'static'}), // Keystone uploaded resources
new AdminUIApp({
enableDefaultRoute: true,
authStrategy,
})
]}

Resources