Temporary 'loading' route in Express? - javascript

I have a web app that takes a moment to load - as it needs to connect to a database, and so some other things that take time.
What's the best way to have a temporary loading route in Express?
I'd like to do something like the following:
const express = require('express')
const app = express()
// Temporary / for if someone hits the server before it's finished starting
app.get('/', (req, res) => res.send(`Loading....`))
// In my non-demo app, there's a router here that takes a moment to load, instead of a timeout.
setTimeout(function(){
app.get('/', (req, res) => res.send(`Ready!`))
}, 3 * 1000)
app.listen(3000, () => console.log('Example app listening on port 3000!'))

Routes can't be deleted at runtime, but you can add a middleware that checks if everything is initialized, if it isn't you end the request with: res.send('Loading'); otherwise you go to the next middelware.
let initialized = false;
app.get('/', (req, res, next) => {
if(!initialized)
return res.send('Loading...');
next();
});
app.get('/', (req, res, next) => {
res.send(`Ready!`);
});
setTimeout(() => initialized = true, 3000);

If your app needs some time to load properly, the best option is to NOT let it start the server.
It works very well with i.e. load balancers and multiple containers as they wait for the /health check to pass before they put the container behind the loadbalancer. Which is something you want to do for modern services.
For example this:
import { app } from './app';
import { config } from './config';
import { logger } from './components/ourLog';
import { initPromise } from './components/ourMongo';
const port = config.server.port;
async function startServer() {
await initPromise;
app.listen(port, () => {
logger.info(
{
port,
params: config.params,
processEnv: process.env,
},
'App has started'
);
});
}
startServer()
.catch(err => {
logger.error({ err }, 'Critical error, cannot start server');
process.exit(1);
});
We have component that connects to mongo and it expose initPromise, which is the promise, which is resolved after the connection is sucesfully done and you can start with using the db.

You could ping the server "x" seconds to test when it is ready.
Server
We will create a ready variable, that is set to false, when all of your database, api and other things are done doing what they need you set it to true.
We will also create a route that such as /ping that will respond with the value of ready.
const express = require('express')
const app = express()
let ready = false
// Do database stuff, API stuff, etc.
// Set ready to true when ready
// Temporary / for if someone hits the server before it's finished starting
app.get('/', (req, res) => res.send(`Loading....`))
app.get('/ping', (req, res) => res.json({ready}))
app.listen(3000, () => console.log('Example app listening on port 3000!'))
Client
Here we will ping the server every "x" seconds, I set it to 0.5s, once ping returns true, we cancel the timeout and run the initialization code that builds the page.
let timeout
timeout = setInterval(async () => {
let response = await fetch('/ping')
let ready = await response.json()
if (ready.ready) {
clearInterval(timeout)
loadpage()
}
}, 500)

Related

Showing MQTT response on a node server API

I'm trying to output the response I receive from MQTT to a node serve setup using express.
There will be a json string message received from the mqtt service at every second.
The response would be output on /main API, which I would call from a Ionic 4 mobile app I'm working on.
However, now I can't display the data on the server itself just to check, I haven't thought of how I would constantly update the data as well on the server. The page doesn't refresh it just keeps loading.
const mqtt = require('mqtt')
const express = require('express')
const PORT = 8000
const app = express()
var client = mqtt.connect("mqtt://bac.com")
client.on('connect', () => {
console.log("Connected")
client.subscribe('/test')
})
app.get("/", (req, res) => {
res.send("ROOT");
});
app.get("/main", (req, res) => {
client.on('message', (topic, message) => {
res.send(message)
})
});
app.listen(PORT, () => {
console.log(`Server is listening on port ${PORT}`);
});
You would need to store your data somehow on the server side for your approach to work.
Implement some kind of storage service that stores the messages. Your client will need to respond to the queue messages and push these to storage, your api action will retrieve them from the storage, not from the queue.
const mqtt = require('mqtt');
const express = require('express');
const PORT = 8000;
const app = express();
const storageService = require("SOME KIND OF STORAGE SERVICE");
var client = mqtt.connect("mqtt://bac.com");
client.on('connect', () => {
console.log("Connected")
client.subscribe('/test')
});
client.on('message', (topic, message) => {
storageService.save(topic, message); //or this has to provide storage and enterpretation of the data comming in
});
app.get("/", (req, res) => {
res.send("ROOT");
});
app.get("/main", (req, res) => {
res.send(storageService.getAll());
});
app.listen(PORT, () => {
console.log(`Server is listening on port ${PORT}`);
});
You could also revisit the entire implementation, and push messages to the frontend via a socket or some other kind of persistant connection.
I have found a workaround from a similar question here. On the server side it will send data on message received. On the client side, it is an ajax call for every second to retrieve the data on the server side.
As described in the link, it really is a bad pattern. However, this would be suitable for those who have constraints to making changes to the MQTT service.
// Server.js
const mqtt = require('mqtt')
const express = require('express')
const PORT = 8000
const app = express()
var client = mqtt.connect("mqtt://bac.com")
var mqttMessage
client.on('connect', () => {
console.log("Connected")
client.subscribe('/test')
})
client.on('message', (topic, message) => {
console.log('Topic: ' + topic + '\nMessage: ' + message)
mqttMessage = message
})
app.get("/", (req, res) => {
res.sendFile("__dirname + "/index.html");
});
app.get("/main", (req, res) => {
if(mqttMessage)
res.send(mqttMessage);
else
res.status(404).send();
});
app.listen(PORT, () => {
console.log(`Server is listening on port ${PORT}`);
});
and on the index.html page:
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script>
$(document).ready(
function () {
setInterval(function () {
$.get('/main', function (res) {
$('#data').text(res);
});
}, 1000);
}
);
</script>
<p id="data"></p>
</body>

Continuously changing Express Route Paths for protection

I want to create a hidden internal webserver with ever-changing routes.
The aim is to prevent/deter people from scraping the site or using the API without permission.
There's probably a better way (or this could be totally useless)
I've written some code that works but either convince me why I'm wasting my time or why it would work.
const express = require('express');
const uuid = require('uuid/v4');
const app = express();
// These are the hidden routes
const routes = {
hiddenPage: undefined,
};
setInterval(() => {
// Change the path of the hidden page
// every 5 seconds
routes.hiddenPage = `/${uuid()}`;
}, 1000 * 5);
app.get('*', (req, res) => {
// There would be other middleware for
// security reasons
if (req.path === routes.hiddenPage) {
res.send('Welcome to the hidden page');
return;
}
res.send(`Page at <a href=${routes.hiddenPage}>${routes.hiddenPage}</a>`);
});
// Listen on 3000
app.listen(3000);
You can use CORS middleware to allow only specific clients to access your server.
https://expressjs.com/en/resources/middleware/cors.html
Example:
var express = require('express')
var cors = require('cors')
var app = express()
var corsOptions = {
origin: 'http://example.com',
}
app.get('/products/:id', cors(corsOptions), function (req, res, next) {
res.json({msg: 'This is CORS-enabled for only example.com.'})
})
app.listen(80, function () {
console.log('CORS-enabled web server listening on port 80')
})

Jest detects open handle with Express app

I've been trying to figure this out for a while now, but with no luck. I initially had the issue when performing simple integration tests with a MongoDB database, but I've stripped the code right down and made it as simple as I can. The only thing I have running is a single test file:
// blah.test.js
const express = require('express');
const app = express();
describe('test block', () => {
let server = null;
beforeEach(() => {
server = app.listen(3000, () => console.log('Listening on port 3000'));
});
afterEach(async () => {
await server.close();
});
it('should pass the test', () => {
expect(1).toBe(1);
});
});
The test passes, but Jest informs me of an open handle which could prevent it from exiting:
On this occasion, it did exit, but still notices an open handle. I would like to know how to safely close this, without forcing exit. With previous tests, I would get the same open handle warning, but also with an error stating that Jest didn't exit one second after completing the tests.
I also tried using beforeAll() and afterAll(), instead of beforeEach() and afterEach(), but still get the same problem. I know the test doesn't require the server on this occasion, but I was trying to make it as simple as possible to try to find the issue with opening and closing the server.
UPDATE: I noticed that it seems to work fine as soon as I add a second test file:
// blah2.test.js
const express = require('express');
const app = express();
describe('test block', () => {
let server = null;
beforeEach(() => {
server = app.listen(3000, () => console.log('Listening on port 3000'));
});
afterEach(async () => {
await server.close();
});
it('should pass the test', () => {
expect(2).toBe(2);
});
});
No open handles with two tests running
If anyone can share any ideas as to why this happens, it would be much appreciated.
I am using:
Express: 4.16.3
Jest: 23.5.0
NPM: 6.3.0
So I solved the issue by abstracting all the app logic to an app.js file:
const express = require('express');
const routes = require('./routes');
const app = express();
app.use('/api', routes);
module.exports = app
Then creating a server.js file with all my server logic:
const app = require('./app.js')
const PORT = process.env.PORT || 3000
app.listen(PORT, () => console.log(`Listening on port: ${PORT}`))
Then just import app.js into your test file like this:
const app = require('../app.js')
Works like a charm!
I had the same issue and I was able to fix it yesterday. The issue is calling app.listen(). You should avoid calling app.listen in your app.js file or jest test file. Just export the app with routes from app.js. Then, you can use supertest to make the request in your test.
// in your app.js
const app = express();
// add routes and middleware
module.exports = app;
// in your test file
import app from './app.js';
import supertest from 'supertest';
describe('test block', () => {
it('test route', async (done) => {
const res = await supertest(app).get('/').expect(200);
done();
});
});
Try this:
beforeAll(async () => {
// let's create a server to receive integration calls
app = express();
app.get('/', (req, res) => res.send('Hello World!'));
await new Promise((resolve) => {
app.listen(6666, () => {
console.log('Example app listening on port 6666!');
return resolve();
});
});
});
afterAll(() => { console.log('closing...'); app.close(); });

Express server using default route, also serve API

The way I have this app set up, every request gets serviced by the same route that will do some JS serverside rendering:
server.use("*", (req, res) => {
console.log(`from the server route: ${req.path}`)
const context = {};
const serverRenderedHTML = ReactDOMServer.renderToString(
React.createElement(
StaticRouter,
{ location: req.url, context },
React.createElement(AdminApp)
)
);
if (context.url) {
res.redirect(context.url);
}
res.write(template({ serverRenderedHTML }));
res.end();
});
But, I would also like to use my express server as an api for some resources, routes set up like this:
//server.js
server.use("/api/products", productRoutes);
// products.js
router.get( (req, res) => {
var productQuery = Product.find({})
productQuery.exec(function(err, products){
res.json(products)
})
})
However, I am unable to hit the API, as all of my requests are getting picked up by that server.use function.
Is there any way to make sure that the routes under /api/ namespace are properly picked up? Should I just make a different server for the API?
Apparently, I needed to include an argument for the route. I had to change:
router.get( (req, res) => ...
to
router.get("/", (req, res) => ...

Node.js Express response is ended before Promise resolve

Executing following code on Node.js with Express, but it does not return anything. It looks like res.send does not work from within promise.then(); Looks like it already returned back to the caller before promise resolve(); What I'm doing wrong? According to examples, it should work. Thanks.
const express = require('express');
const app = express();
app.get('/test', (req, res, next) => {
res.send("Good"); // Works Just Fine , but I dont need to return here
getMessage().then(function(data){
console.log("I'm here"); // Message works
res.send("Good"); // Does not Work
}).catch(function(error){
res.send("Error");
});
});
function getMessage(){
return new Promise(function(resolve, reject){
setTimeout(function() {
resolve();
}, 3000);
});
}
app.listen(PORT, () => {
console.log("run");
});
Please add following code to your app: Refer Here
This app starts a server and listens on port 8080 for connections
app.listen(8080, function () {
console.log('Example app listening on port 8080!')
})
You need to listen on a port for the express server to run.
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
app.get('/test', (req, res, next) => {
getMessage().then(function(data){
res.send("Good");
}).catch(function(error){
res.send("Error");
});
});
function getMessage() {
return new Promise((resolve, reject) => {
setTimeout(function() {
resolve();
}, 3000);
});
}
app.listen(port, () => console.log(`App listening at http://localhost:${port}`));
The problem was in request timeout setting in Postman that I'm using for testing.
Remove the first
res.send('good');
or you could res.write() first
then the message should be concatenated when res.send()
res.write('good')
res.send() closes the connection between the server and client
therefore using res.write() will write the message and when res.send() is sent all the written messages (using res.write()) are sent back to the client called
IN SHORT
res.write() can happen several times
res.send() happens once only per request

Categories