How to return a 404 Not found page in an Express App? - node.js

I have an express app, in which I have the following code:
app.get('*', (req, res) => {
res.send('404', {
title: 404,
name: 'James Olaleye',
errorMessage: 'Page not found',
});
});
However, My IDE is warning about this message:
express deprecated res.send(status, body): Use
res.status(status).send(body) instead
And with the above code, My Browser is returning the following payload as a JSON object:
{
"title": 404,
"name": "James Olaleye",
"errorMessage": "Page not found"
}
What I want, is to display a 404 Not found page to the user, how can this be achived?

You have two seperate problem
1: you are using an old way to response to the request insted use this res.status(STATUS_CODE).send(BODY)
2: you are sending a json yet you want to display a 404 page in this case you need to send a html template
so your code should look like this
app.get('*', (req, res) => {
res.status(404).send("<div>404 Not Found</div>");
});

I updated your question a bit to make it clearer for future references.
the method res.send is deprecated, among other things because it's usages is too ambiguous. A server response, can be a lot of things, it can be a page, it can be a file, and it can be a simple JSON object (which you have here).
In your case, when you run res.send(404,{ /*...*/ }), the express app assumes you want to send a JSON object, so it does just that.
There are multiple possible ways, to achieve what you want, but I will stick to the most simple solution.
If you want to display an HTML page, in the most simplest form, you can actually just change your piece of code to do this instead:
app.status(404).send(`<h1>Page not found</h1>`)
This will essentially, show a page, instead of a JSON object.
You can even define the whole HTML file if you like:
app.status(404).send(
`
<html>
<!DOCTYPE html>
<head>
<title>404</title>
</head>
<body>
<h1>James Olaleye</h1>
<h1>Page Not Found</h1>
</body>
</html>
`
)
This would be the fastest way to achieve what you want.
A step further, would be to create an HTML file some where in your app, and to send the HTML file instead.
If your source code looks like this:
/
src/
index.js
htmls/
404.html
<!-- htmls/404.html -->
<html>
<!DOCTYPE html>
<head>
<title>404</title>
</head>
<body>
<h1>James Olaleye</h1>
<h1>Page Not Found</h1>
</body>
</html>
// src/index.js
const express = require('express');
const app = express();
const path = require('path');
const PORT = 3000;
app.get('/', function(req, res) {
const options = {
root: path.join(__dirname, '..', 'htmls')
};
res.sendFile('404.html', options, function (err) {
if (err) {
next(err);
} else {
console.log('Sent:', fileName);
}
});
});
This would allow you to have multiple HTML files which you can send around.
There are like I stated, other options as well, but that would make this answer way too long and out of scope. If you are interested, you can research Using template engines with Express and start with the following link.
Happy coding :)

Related

react express Uncaught SyntaxError: Unexpected token <

I understand this error is not the reason for the failing. It is failing because in my index.html file i have:
<body><noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script type="text/javascript" src="/static/js/main.f4a49fba.js"></script>
</body>
That script tag src is failing and returning the same file contents as the index.html itself. This causes it to render HTML (hence < unexcpected from <!DOCTYPE html>).
I am trying to have both express server with /graphql and react together. The working solution is using express.static middlewear shown below. Problem is the working solution breaks the /graphql endpoint so I cannot return any data.
app.use(express.static(path.join(__dirname, 'client/build')));
I need to get this working so it first allows the previous enpoints (/graphql) before checking static pages so I am trying to use this:
app.get('*', (req, res) => {
res.sendFile('index.html', { root: path.join(__dirname, 'client/build/') });
});
This is successfully getting pulled back but failing to work because main.f4a49fba.js in the script tag does not want to load. I tried changing it to /client/build/static/js/main.f4a49fba.js but still wont load. This is using a build file for production.
UPDATE:
I replaced my code with below which helped but for some reason even though I have /graphql above this it is still being run when a full address is being run.
app.get('*', (req, res) => {
const link = (req.path == '/' ? 'index.html' : req.path);
const root = path.join(__dirname, 'client/build');
res.sendFile(link, { root: root }, (error) => {
if (error) {
res.sendFile('/', { root: root });
}
});
});
I am getting this now when a graphql request comes in which seems like it is missing the first /graphql and going to my updated function above. It is very strange and sticking the graphql full address at the end of whatever current page I am in.
http://localhost:3000/dashboard/accounts/my.herokuapp.com/graphql 404 (Not Found)

Creating HapiJs server that can serve complex web pages

I have just started learning Node.js and hapi.js. What I am trying to accomplish now is build a REST web server that should also have a web interface for configuration and statistics collection.
I found that Inert plugin allows serving static pages and, as I understand, this limits me to loading a single web page that consists of a single file.
However, what I do not understand is whether it is possible to setup hapi.js to serve a full dynamic webpage with css, js and other files referenced within its body.
Am I heading the wrong direction with this or else how can I setup my scenario?
You can serve multiple static files from a specified directory.
Just tried out inert with hapi, using this example:
https://github.com/hapijs/inert#static-file-server
Inert has no problem serving multiple static files from a given directory, e.g public.
So you'll have no issue serving multiple static html, css, js files from a specified dir. You can then build a dynamic JSON api using Hapi, and have that consumed by your js client-side code, served from static js files in your public dir.
If you are needing templating, where you generate dynamic content on the serverside, hapi can do that out of the box, check out:
http://hapijs.com/tutorials/views
Sorry if this isn't what you mean, do feel free to clarify if not :-)
Hope that helps!
The vision plugin is used for templating, which is what I think you're after. If you want to bundle css and js files along with your pages, you can put them in a public directory and serve that with the inert plugin. And then you only need to reference the relative path in whatever html file you're trying to render.
Here's a simple example that uses handlebars. Inert is responsible for serving your css and js files while vision still renders your templates.
./index.js
var hapi = require('hapi');
var server = new hapi.Server();
server.connection({port: 5555});
server.register([require('vision'), require('inert')], (err) => {
if(err){
throw err;
}
server.views({
engines: {
html: require('handlebars')
},
relativeTo: __dirname + '/',
path: 'www'
});
var homeroute = {
method: 'GET',
path: '/',
handler: (request, reply) => {
reply.view('index', {name: 'cuthbert'});
}
};
var publicassetsroute = {
method: 'GET',
path: '/public/{param*}',
handler: {
directory: {
path: './public',
listing: false,
index: false
}
}
};
server.route(homeroute);
server.route(publicassetsroute);
server.start((err) => {
console.log('server started -- ' + server.info.uri)
});
});
www/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hapi Test</title>
<link rel="stylesheet" href="../public/index.css">
</head>
<body>
<h1>A Hapi Happy Test.</h1>
<p>This is a test page. Woo!</p>
<p>My name is {{name}}.</p>
</body>
</html>
public/index.css
p {
color: blue;
}

React Components on the Server

I've been playing around with React for a while now but still can't wrap my head around on how to integrate it with an existing node/express/handlebars app.
For example, if I had a feed component which required json data to be fetched from AWS - how do I handle this.
var VideoFeed = require('./component/VideoFeed');
app.use('/', function(res, req) {
DataService.getVideoFeed().then(function(data) {
res.render('home', {videoComponent: React.renderToString(<VideoFeed feed={data} />);
});
});
Home
<!DOCTYPE html>
<body>
Some sample text. Here's the Video Feed
{{videoComponent}}
</body>
</html>

Angular not updating html template variables when served via NodeJS server

This is a bit of a specific question, but I'm at a bit of a loss for an answer.
First, a little background. I've been trying to learn angular, and I wanted to start using Node as the backend. I currently have a working tutorial app that I can run locally that just returns data that is hard coded into the main controller.
When I moved the files to my NodeJS server, it stopped working though. Here is what works:
The files load correctly - there are no console errors, and I can view each of the files in the source (index.html, app.js, maincontroller.js)
The scope exists, and the variables are defined. I put a console.log($scope) inside the mainController.js file, and I can see all of the variables defined correctly.
Non-angular javascript works - I can place alerts outside/inside the mainController, and they all work correctly (also, console.log obviously works)
I am serving the files via a simple Node.js server. I am using express and hbs. I was originally using compression, and 0 cache length, but have since removed those with no change in the result.
The specific issue I'm having is that none of the template variables update. I've simplified it down to the following code for testing. When viewed locally, the page says 'I now understand how the scope works!', when served from Cloud 9, the structure exists, but the {{understand}} variable in the template doesn't work.
index.html
<!DOCTYPE html>
<head>
<title>Learning AngularJS</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<script src="js/app.js"></script>
<script src="js/maincontroller.js"></script>
</head>
<body>
<div id="content" ng-app="MyTutorialApp" ng-controller="MainController">
{{understand}}
</div>
</body>
app.js
var app = angular.module('MyTutorialApp',[]);
maincontroller.js
app.controller("MainController", function($scope){
$scope.understand = "I now understand how the scope works!";
});
server.js (Node server on Cloud 9)
var express = require('express');
var app = express();
var hbs = require('hbs');
app.set('view engine','html');
app.engine('html',hbs.__express);
app.configure(function() {
app.set('views', __dirname);
});
//app.use(express.compress());
app.use('/js',express.static(__dirname + '/client/js'));
app.use('/css',express.static(__dirname + '/client/css'));
app.use('/img',express.static(__dirname + '/client/img'));
//router
app.get('/',function(req,res){
res.render('client/index.html');
return;
});
//404 responses
app.use(function(req, res, next){
res.status(404);
// respond with html page
if (req.accepts('html')) {
res.render('client/404.html', { url: req.url });
return;
}
// respond with json
if (req.accepts('json')) {
res.send({ error: 'Not found' });
return;
}
// default to plain-text. send()
res.type('txt').send('Not found');
});
app.listen(process.env.PORT);
console.log('listening on port '+process.env.PORT);
everythin became clear when i read
"Handlebars.js is an extension to the Mustache templating language"
what this menas is that hbs uses {{}} as delimiters as well as angular so the {{understand}} in your html never gets to angular because is first parsed and substituted by hbs. if you want to use hbs with angular youll need to change your delimiters using your angulars $interpolateProvider in your app configuration something like
$interpolateProvider.startSymbol('{/{');
$interpolateProvider.endSymbol('}/}');
You can use \{{understand}} as this will counter your hbs and put your angular on top.

How to create global variables accessible in all views using Express / Node.JS?

Ok, so I have built a blog using Jekyll and you can define variables in a file _config.yml which are accessible in all of the templates/layouts. I am currently using Node.JS / Express with EJS templates and ejs-locals (for partials/layouts. I am looking to do something similar to the global variables like site.title that are found in _config.yml if anyone is familiar with Jekyll. I have variables like the site's title, (rather than page title), author/company name, which stay the same on all of my pages.
Here is an example of what I am currently doing.:
exports.index = function(req, res){
res.render('index', {
siteTitle: 'My Website Title',
pageTitle: 'The Root Splash Page',
author: 'Cory Gross',
description: 'My app description',
indexSpecificData: someData
});
};
exports.home = function (req, res) {
res.render('home', {
siteTitle: 'My Website Title',
pageTitle: 'The Home Page',
author: 'Cory Gross',
description: 'My app description',
homeSpecificData: someOtherData
});
};
I would like to be able to define variables like my site's title, description, author, etc in one place and have them accessible in my layouts/templates through EJS without having to pass them as options to each call to res.render. Is there a way to do this and still allow me to pass in other variables specific to each page?
After having a chance to study the Express 3 API Reference a bit more I discovered what I was looking for. Specifically the entries for app.locals and then a bit farther down res.locals held the answers I needed.
I discovered for myself that the function app.locals takes an object and stores all of its properties as global variables scoped to the application. These globals are passed as local variables to each view. The function res.locals, however, is scoped to the request and thus, response local variables are accessible only to the view(s) rendered during that particular request/response.
So for my case in my app.js what I did was add:
app.locals({
site: {
title: 'ExpressBootstrapEJS',
description: 'A boilerplate for a simple web application with a Node.JS and Express backend, with an EJS template with using Twitter Bootstrap.'
},
author: {
name: 'Cory Gross',
contact: 'CoryG89#gmail.com'
}
});
Then all of these variables are accessible in my views as site.title, site.description, author.name, author.contact.
I could also define local variables for each response to a request with res.locals, or simply pass variables like the page's title in as the optionsparameter in the render call.
EDIT: This method will not allow you to use these locals in your middleware. I actually did run into this as Pickels suggests in the comment below. In this case you will need to create a middleware function as such in his alternative (and appreciated) answer. Your middleware function will need to add them to res.locals for each response and then call next. This middleware function will need to be placed above any other middleware which needs to use these locals.
EDIT: Another difference between declaring locals via app.locals and res.locals is that with app.locals the variables are set a single time and persist throughout the life of the application. When you set locals with res.locals in your middleware, these are set everytime you get a request. You should basically prefer setting globals via app.locals unless the value depends on the request req variable passed into the middleware. If the value doesn't change then it will be more efficient for it to be set just once in app.locals.
You can do this by adding them to the locals object in a general middleware.
app.use(function (req, res, next) {
res.locals = {
siteTitle: "My Website's Title",
pageTitle: "The Home Page",
author: "Cory Gross",
description: "My app's description",
};
next();
});
Locals is also a function which will extend the locals object rather than overwriting it. So the following works as well
res.locals({
siteTitle: "My Website's Title",
pageTitle: "The Home Page",
author: "Cory Gross",
description: "My app's description",
});
Full example
var app = express();
var middleware = {
render: function (view) {
return function (req, res, next) {
res.render(view);
}
},
globalLocals: function (req, res, next) {
res.locals({
siteTitle: "My Website's Title",
pageTitle: "The Root Splash Page",
author: "Cory Gross",
description: "My app's description",
});
next();
},
index: function (req, res, next) {
res.locals({
indexSpecificData: someData
});
next();
}
};
app.use(middleware.globalLocals);
app.get('/', middleware.index, middleware.render('home'));
app.get('/products', middleware.products, middleware.render('products'));
I also added a generic render middleware. This way you don't have to add res.render to each route which means you have better code reuse. Once you go down the reusable middleware route you'll notice you will have lots of building blocks which will speed up development tremendously.
For Express 4.0 I found that using application level variables works a little differently & Cory's answer did not work for me.
From the docs: http://expressjs.com/en/api.html#app.locals
I found that you could declare a global variable for the app in
app.locals
e.g
app.locals.baseUrl = "http://www.google.com"
And then in your application you can access these variables & in your express middleware you can access them in the req object as
req.app.locals.baseUrl
e.g.
console.log(req.app.locals.baseUrl)
//prints out http://www.google.com
In your app.js you need add something like this
global.myvar = 100;
Now, in all your files you want use this variable, you can just access it as myvar
One way to do this by updating the app.locals variable for that app in app.js
Set via following
var app = express();
app.locals.appName = "DRC on FHIR";
Get / Access
app.listen(3000, function () {
console.log('[' + app.locals.appName + '] => app listening on port 3001!');
});
Elaborating with a screenshot from #RamRovi example with slight enhancement.
you can also use "global"
Example:
declare like this :
app.use(function(req,res,next){
global.site_url = req.headers.host; // hostname = 'localhost:8080'
next();
});
Use like this:
in any views or ejs file
<%
console.log(site_url);
%>
in js files
console.log(site_url);
With the differents answers, I implemented this code to use an external file JSON loaded in "app.locals"
Parameters
{
"web": {
"title" : "Le titre de ma Page",
"cssFile" : "20200608_1018.css"
}
}
Application
var express = require('express');
var appli = express();
var serveur = require('http').Server(appli);
var myParams = require('./include/my_params.json');
var myFonctions = require('./include/my_fonctions.js');
appli.locals = myParams;
EJS Page
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title><%= web.title %></title>
<link rel="stylesheet" type="text/css" href="/css/<%= web.cssFile %>">
</head>
</body>
</html>
Hoping it will help
What I do in order to avoid having a polluted global scope is to create a script that I can include anywhere.
// my-script.js
const ActionsOverTime = require('#bigteam/node-aot').ActionsOverTime;
const config = require('../../config/config').actionsOverTime;
let aotInstance;
(function () {
if (!aotInstance) {
console.log('Create new aot instance');
aotInstance = ActionsOverTime.createActionOverTimeEmitter(config);
}
})();
exports = aotInstance;
Doing this will only create a new instance once and share that everywhere where the file is included. I am not sure if it is because the variable is cached or of it because of an internal reference mechanism for the application (that might include caching). Any comments on how node resolves this would be great.
Maybe also read this to get the gist on how require works:
http://fredkschott.com/post/2014/06/require-and-the-module-system/

Resources