I am building a blog using Node js and Express and hosting it on firebase. When I serve the website locally everything works just fine and the html is served as expected. But, when I deploy the server the routes no longer work and the html files can't be found. I'm sure it has to do with how firebase deploy hold the html files.
I'm not really sure where to go from here. I can't really find great guidance on how to set up something like this on the firebase docs.
const functions = require("firebase-functions")
const cors = require("cors")
const express = require("express")
const path = require("path")
/* Express with CORS */
const app = express()
app.use(cors({ origin: true }))
app.get("/", (request, response) => {
response.send("Hello from Express on Firebase with CORS!")
})
//File path consts
const publicDir = "/Users/wilson/wildman-talks-fb/public";
const blogDir = "/Users/wilson/wildman-talks-fb/public/blogs";
app.get("/about/", (req, res) =>{
res.sendFile(path.join(publicDir, "/about.html"));
});
app.get("/contact/", (req, res) =>{
res.sendFile(path.join(publicDir, "/contact.html"));
});
app.get("/tools/", (req, res) =>{
res.sendFile(path.join(publicDir, "/tools.html"));
});
app.get("/five-steps-july-20/", (req, res) =>{
//res.send(path.join(publicDir, "/five-steps-july-20.html"));
res.sendFile(path.join(publicDir, "/five-steps-july-20.html"));
})
exports.app = functions.https.onRequest(app)
So what is happening is when I deploy the site locally all of the links in my webpage work to other html webpages for my site. When I deploy it on firebase I get 404 errors. I was able to use path.join(__dirname, "../public") and print out all of the files contained there. When i did that these were the files that were there on my local host: [".DS_Store","404.html","about.html","blogs","contact.html","css","five-steps-july-20.html","img","index.html","js","mail","tools.html","vendor"]. After deploying it just returns me a 500 error so I guess that won't help.
Your directories contain absolute paths to your filesystem. Try to use dynamic absolute paths.
Change the paths from
const publicDir = "/Users/wilson/wildman-talks-fb/public";
const blogDir = "/Users/wilson/wildman-talks-fb/public/blogs";
To
const path = require("path");
const publicDir = path.join(__dirname, "/public";)
const blogDir = path.join( __dirname, "/public/blogs");
Related
Background
I am migrating an Angular app in GKE cluster. The base docker image that I must use(company policy) does not have any options to install any new softwares like shell, Angular cli command ng etc. The base docker image has only Node installed.
There is a shared base url, let's say, www.my-company.com, that everyone has to use for app deployment with a path added after the base url like www.my-company.com/my-angular-app/ - all the other Angular apps must be differentiated using the path of the app.
What I did
Since I can't run ng serve command in the base image, I added Express dependency in the package.json in Angular application and created an express server to route the traffic to Angular app.
I was following this youtube video to configure the application - https://www.youtube.com/watch?v=sTbQphoYbK0&t=303s. The problem I am facing is to how I load the the static files in the application.
If I define absolute path inside sendFile method of server.js file, although the application is working, but in future, if I need to add any other files in the application, I have to create another route in server.js file.
I don't know how Express can search a file automatically from the static folder(and sub folders) and return only that file when needed. I defined a static folder too, but seems like it is not working.
Following is my server.js code
==============================
const express = require('express');
const http = require('http');
const path = require('path');
const port = 8080;
const contextPath = '/my-angular-app';
const router = express.Router();
const app = express();
app.use(contextPath, router);
app.listen(port, ()=> {
console.log("Listening on port: ", port);
});
app.use(express.static(__dirname + '/dist/testapp/'));
router.get('/', function(req, res) {
// to get index.html file
res.sendFile(path.resolve(__dirname + '/dist/testapp/index.html'));
});
router.get('/*', function(req, res) {
let path = __dirname +'/dist/testapp/' + req.path
console.log('full path: ', path);
// To return static files based on incoming request, I am facing problem here(I think)
res.sendFile(path);
});
==============================
I want Express will send any files based on file name in the request. It should also take care of nested directories in the /dist/testapp/ directory
/dist/testapp/ -> This is the directory where Angular generates code for my app after I execute ng build command
WEBAPP.get("/admin/script.js", (req, res) => {
console.log(req.path);
if (req.session.username !== "Admin") return res.render("error");
res.sendFile(__dirname + "/admin/admin.js")
});
WEBAPP.get("/admin", (req, res) => {
if (!req.session.loggedin) return res.render("error");
if (req.session.username !== "Admin") return res.render("error",);
res.render("admin", {
csrfToken: req.csrfToken(),
title: "ADMIN PORTAL",
username: req.session.username,
nav_avatar: GetImageURL(req.session.avatar, "small")
});
});
There's no need to publically share /admin/script.js in my case but if a user requests this URL say example.com/admin/script.js a check for username equaling "Admin" if all is okay we sendFile.
I would maybe assume that you're not properly targeting your static files. Perhaps console.log the target.
I'm a beginner in Express. So I might've failed to frame the question properly. I have created a MEAN application wherein I've separated my frontend and backened. Frontend runs on port:4200 and server runs on port:3000. I wanted to run both frontend and backend on same port as part of deployment. I'm getting MIME type errors, someone told me that there is some problem with my server environment. Maybe I'm not sending headers properly. Here is my code:
I have mentioned tried solutions in the code itself as <----TRIED THIS
server.js
const express = require('express');
express.static.mime.define({'application/javascript': ['js']}); <----TRIED THIS
const bodyParser = require('body-parser');
const path = require('path');
// express.static.mime.define({'application/javascript': ['js']}); <----TRIED THIS
const api = require('./routes/api');
const PORT = 3000;
const app = express();
app.use(express.static(path.join(__dirname, 'dist')));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use('/api', api);
app.get('/', function(req, res) {
// res.send('Hello from the server'); <----TRIED THIS
// res.writeHead(200, {'Content-Type': 'text/html'}); <----TRIED THIS
// res.set('Content-Type', 'text/plain'); <----TRIED THIS
// res.setHeader("Content-Type","application/json"); <----TRIED THIS
res.sendFile(path.join(__dirname, 'dist/application/index.html'));
})
app.listen(PORT, function() {
console.log('Server listening on PORT '+PORT);
});
api.js
For instance I'm showing you GET function only
const express = require('express');
const router = express.Router();
const mongoose = require('mongoose');
const db = <my db string>;
const jwt = require('jsonwebtoken');
mongoose.connect(
...
)
function verifyToken(req, res, next) {
...
}
router.get('/myarticles', (req, res) => {
var person="Tanzeel Mirza";
console.log('Get request for tanzeel articles');
Article.find({contributor: person}, (error, article) => {
if(error) {
console.log(error)
}
else {
if(!article) {
res.status(401).send('Invalid email')
}
else if(2>4) {
console.log("test passed");
}
else {
res.json(article);
}
}
})
})
module.exports = router;
But still I'm getting
Loading module from “http://localhost:3000/runtime-xxx.js” was blocked because of a disallowed MIME type (“text/html”).
Loading module from “http://localhost:3000/polyfills-xxx.js” was blocked because of a disallowed MIME type (“text/html”).
Loading module from “http://localhost:3000/main-xxx.js” was blocked because of a disallowed MIME type (“text/html”).
Please correct me.
PS: I asked separate questions for MIME error here. But no answers.
Since your assets are inside dist/application folder, Use app.use(express.static(path.join(__dirname, 'dist/application')));
To match all web app routes, Use app.get('*', function(req, res) {
res.sendFile(path.join(__dirname, 'dist/application/index.html'));
}).
This a generic route and will be called into action only if express can't find any other routes and always serve index.html. For example any valid /api route will never reach this handler, as there a specific route that handles it.
Final code for server.js
const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const api = require('./routes/api');
const PORT = 3000;
const app = express();
app.use(express.static(path.join(__dirname, 'dist/application')));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use('/api', api);
app.get('*', function(req, res) {
res.sendFile(path.join(__dirname, 'dist/application/index.html'));
})
app.listen(PORT, function() {
console.log('Server listening on PORT '+PORT);
});
A few points to not.
To serve static files, you need not set any headers manually. Express looks up the files in the folder (dist folder in your case) you set as static directory with the express.static middleware function. Express also sets the response headers based on the file extension.
So you don't need express.static.mime.define in your code anymore.
In your case you have defined app.use(express.static(path.join(__dirname, 'dist'))); which listens for static files at dist folder. In this app.use command, you haven't used a mount path which means that all the requests will go through the static middleware. If the middleware finds an asset with the same name, path and extension in dist folder it returns the file, else the request is passed to the other route handlers.
Also, If you are using static middleware, as long as there is an index.html in dist folder (immediate child of dist folder), your route handler for "/" will never get invoked as the response will be served by the middleware.
If you don't have an index html file in dist folder(immediate child of dist), but it's present somewhere in subfolders of dist, and still you need to access it using root path "/", only then you need a route handler for path "/" as below.
app.get("/", function(req, res) {
res.sendFile(path.join(__dirname, "dist/application/index.html"));
});
JS files referred using "./" in dist/application/index.html are referred relative to dist folder itself and NOT dist/application folder.
You can refer this REPL for updated code 👉.
https://repl.it/repls/SoreFearlessNagware
Try below urls
/api/myarticles - Rendered by "/api" route handler
/api/myarticles.js - Rendered by static asset middleware because the file exists in dist/api folder
/ - rendered using "/" route handler and res.sendFile because index.html doesn't exist in dist folder.
/test.js - Rendered using static middleware because file exists in dist folder
Additional links for reference.
https://expressjs.com/en/api.html#res.sendFile
https://expressjs.com/en/starter/static-files.html
1.Build your angular project, either inside or outside the server folder using ng build cmd.
2.To build your project inside server, change the dist-folder path in angular-cli.
3.To change path, either use cli cmd or edit the angular-cli.json file's "outDir": "./location/toYour/dist"
Or by using this cli cmd ng build --output-path=dist/example/
4.Then In your server root file include the static build/dist folder using express.
5.Like this app.use(express.static(path.join( 'your path to static folder')));
6.Now restart your server.
Problem - I am not able to get any response from postman when hitting localhost:9000. It should give me a user json back which is in my routes file only for time being. Instead it spits out the following.
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script type="text/javascript" src="/static/js/main.ce2f0561.js"></script>
</body>
Setup
Using create-react-app with express to connect.
My folder structure is
--src React app lives in this
--server
-- index.js
-- express.js
-- controllers
-- routes
-- rs_notes.js
rs_routes.js
'use strict';
module.exports = function(router){
const notesController = require('../controllers/cs_notes');
router.route('/', function(req, res, next) {
// Comment out this line:
//res.send('respond with a resource');
// And insert something like this instead:
res.json([{
id: 1,
username: "samsepi0l"
}, {
id: 2,
username: "D0loresH4ze"
}]);
});
};
express.js
const express = require('express');
const morgan = require('morgan');
const path = require('path');
const app = express();
const router = express.Router();
// Setup logger
app.use(morgan(':remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] :response-time ms'));
// Serve static assets
app.use(express.static(path.resolve(__dirname, '..', 'build')));
require('./routes/rs_notes')(router);
// Always return the main index.html, so react-router render the route in the client
router.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, '..', 'build', 'index.html'));
});
module.exports = app;
index.js
'use strict';
const app = require('./express');
const PORT = process.env.PORT || 9000;
app.listen(PORT, () => {
console.log(`App listening on port ${PORT}!`);
});
Full project link - https://drive.google.com/file/d/0B35OQMkRo3KcSHlkeXdWVjVjc0U/view?usp=sharing
My questions or doubts are
Am I passing the router in a right way. We used to pass app in this
way prior to express 4 ? So not sure if same structure works here.
I am able to load it in browser by hitting localhost:9000 (server is run by node server command as configured) but not in postman.
I was able to fix up this stack by learning the use of Router appropriately and moving some code here and there. But it was still not working for base route i.e when I simply do router.get('/', ...). Gives the same error message. So I rather reversed the approach of connecting node and react. I published my efforts on medium for the same reason as two separate posts.
https://medium.com/#kushalvmahajan/i-am-about-to-tell-you-a-story-on-how-to-a-node-express-app-connected-to-react-c2fb973accf2
I'm using React BrowserHistory for navigation. I want the same files to be served from my Node server regardless of the URL path.
My server code:
const http = require('http');
const path = require('path');
const express = require('express');
const app = express();
const hostname = 'localhost';
app.use(express.static('../public'));
app.get('*', (req, res)=>{
res.sendFile(path.join(__dirname, '../public/index.html'));
});
const port = process.env.PORT || 8007;
app.listen(port, ()=>{
console.log('Production Express server running at localhost:' + port)
});
http://localhost:8007 => works fine
http://localhost:8007/help => works fine
http://localhost:8007/help/faq => Fails
I want all URLs to return the same resources, served from ../public. However, it seems that the express.static doesn't work when the resource isn't requested from the root. So, for instance, when the browser asks for <script src="scripts/main.js"></script>, it thinks I want ../public/help/scripts/main.js, whereas I actually want ../public/scripts/main.js/. So, because express doesn't find such file, it moves on to the app.get('*'..., which returns the ../public/index.html file when the script is requested.
So, the desired behavior is:
return the same index.html for any path (with any number of sub-folders), and let React figure out what to show
return the resources always relative to the path index.html is served from, not relative to the path in the URL
It works when I use absolute paths in my resource requests (I.E. <script src="http://localhost:8007/scripts/main.js"></script>), however writing it like that obviously isn't desirable, because it needs to be changed when it's hosted elsewhere.
What should I do?
you can use that condition for your routing:
app.get('*/**', (req, res) => {
res.sendFile(path.join(__dirname, '../public/index.html'));
});
It's work find for me.
Be carefull with this method, your browser will reload the entire page for each call to a route.
I'm trying to get a site to force HTTPS (redirect from HTTP). We've got HTTPS set up via AWS Elastic Beanstalk. The problem is that, currently, both HTTP and HTTPS can be used.
After reading through a few posts, including this one, the code below is what I came up with. Unfortunately this isn't working.
What am I missing?
import express from 'express';
import { join } from 'path';
const app = express();
const buildPath = join(`${__dirname}/../build`);
const enforceHTTPS = (req, res, next) => {
if (req.headers['x-forwarded-proto'] === 'https') return next();
else return res.redirect(301, join(`https://${req.hostname}${req.url}`));
};
app.use(express.static(buildPath));
app.use(enforceHTTPS);
app.get('*', (req, res) => res.sendFile(`${buildPath}/index.html`));
app.listen(process.env.PORT || 3000, () => console.log('Server running on port 3000!'));
export default app;
As it turns out, I simply had to reorder my app.use statements — calling the redirect before serving the static files.
Additionally, in order for this to work on IE/Edge, 'https://' needed to be moved outside of path.join (join removes the second forward slash, and although all other major browsers will handle it properly, IE doesn't like it).
Here's a working example:
import express from 'express';
import { join } from 'path';
const app = express();
const buildPath = join(`${__dirname}/../build`);
const enforceHTTPS = (req, res, next) => {
if (req.headers['x-forwarded-proto'] === 'https') return next();
return res.redirect(301, `https://${join(req.hostname, req.url)}`);
};
app.use(enforceHTTPS);
app.use(express.static(buildPath));
app.get('*', (req, res) => res.sendFile(`${buildPath}/index.html`));
app.listen(process.env.PORT || 3000, () => console.log('Server running on port 3000!'));
export default app;
When I have used node.js as a proxy in the past, the way we got unsecure connections to proxy to secure was as follows, amended to match your code:
const httpsPort = process.env.HTTPS_PORT;
app.use(function(req, res, next) {
if (req.secure) {
return next();
}
res.redirect(301, ['https://', req.hostname, ':', httpsPort, '/', req.url].join('');
}
Where httpsPort is whatever the port your standard https connections will go through. Where process.env.HTTPS_PORT would get the environment variable for the https port (Standard is 443). You could replace this with whatever you want to get the port.
The most obvious issue in your code is that HTTPS is served from port 443. Besides that looks like outdated code. Why not use a recent version of Express? If you look at the example for HTTPS they give here its pretty different from what you wrote: http://expressjs.com/en/api.html search for 'HTTPS'
var express = require('express');
var https = require('https');
var http = require('http');
var app = express();
http.createServer(app).listen(80);
https.createServer(options, app).listen(443);