When developing an API using nodeJS and express, 2 js file involved as below
When I pm2 start main.js, doSth() in api.js works as I expected, however, when I pm2 stop main.js,
cleanSth() not working, anyone can advise how can I achieved my expected result (cleanSth when I pm2 stop)?
api.js
const express = require('express')
doSth()
const routes = (app) => { ... ... }
process.on('beforeExit', cleanSth()})
module.exports = routes
main.js
const express = require('express') const app = express()
const a = require('./api.js') a(app)
As stated in docs https://nodejs.org/api/process.html#process_event_beforeexit
The 'beforeExit' event is not emitted for conditions causing explicit termination, such as calling process.exit() or uncaught exceptions.
So if you only do some synchronous work in cleanSth then use exit event instead of beforeExit
Or you may want to have a look at this answer https://stackoverflow.com/a/64028857/3284355
I'm trying to create an integration test using Jest for my Express app. I think I have a conceptual misunderstanding as my tests are behaving strangely. My goal is to test the following scenario. I'm hitting a specific endpoint using Supertest, and I want to check whether an error handler middleware is called if there is a mocked error. I want to check whether the error handler is not called, if there is no error present. I have the following test file:
test.js
const request = require('supertest')
describe('Error handler', () => {
let server
let router
beforeEach(() => {
jest.resetModules()
jest.resetAllMocks()
})
afterEach(async () => {
await server.close()
})
it('should be triggered if there is a router error', async () => {
jest.mock('../../routes/')
router = require('../../routes/')
router.mockImplementation(() => {
throw new Error()
})
server = require('../../server')
const res = await request(server)
.get('')
.expect(500)
.expect('Content-Type', /json/)
expect(res.body.error).toBe('Error')
expect(res.body.message).toBe('Something went wrong!')
expect(res.body.status).toBe(500 )
})
it('should not be triggered if there is no router error', async () => {
server = require('../../server')
const res = await request(server)
.get('')
.expect(201)
.expect('Content-Type', /text/)
})
})
What I think is happening is the following. Before each test I reset all modules, because I don't want to have the cached version of my server from the first require, I want to overwrite it. I also reset all mocks, so when the second test runs, no mock is used, no fake error is forced, so the middleware is not called and I'm getting back a vanilla 200 result.
After this is done, I start testing the scenario when there is an error. I mock the routes file that exports my routes so I can force a fake error. Then I require the server, this way, I suppose, it's loading the server up with the fake, error throwing route. Then I wait for the response with Supertest, and assert that I indeed got an error back - hence the error handler middleware has been triggered and worked.
The afterEach hook is called, the server is closed, then the beforeEach hook initializes everything, again. Now I have my vanilla implementation without the mock. I require my server, hit the homepage with a get request, and I get back the correct response.
The strange thing is that for some reason the second test seems to not exit gracefully. If I change my implementation from async - await in the second test, to specify the done callback, and then if I call it at the end of the test, it seems to be working.
I tried a lot of possible permutations, including putting the mocking part to the beforeEach hook, starting the server before / after mocking, and I got weird results. I feel like I have conceptual misunderstandings, but I don't know where, because there are so many moving parts.
Any help to make me understand what is wrong would be greatly appreciated
EDIT:
I thought that most parts can be considered a black box, but now I realize that the fact that I'm trying to create an app using Socket.IO makes the setup process a bit more convoluted.
I don't want Express to automatically create a server for me, because I want to use socketIO. So for now I only create a function with the appropiate signature, and that is 'app'. This can be given as an argument to http.Server(). I configure it with options and the middlewares that I want to use. I do not want to call app.listen, because that way Socket.IO could not do its own thing.
config.js
const path = require('path')
const express = require('express')
const indexRouter = require('./routes/')
const errorHandler = require('./middlewares/express/errorHandler')
const app = express()
app.set('views', path.join(__dirname + '/views'))
app.set('view engine', 'ejs')
app.use(express.static('public'))
app.use('', indexRouter)
app.use(errorHandler)
module.exports = app
In server.js I require this app, and then I create a HTTP server using it. After that, I feed it to 'socket.io', so it is connected to the proper instance. In server.js I do not call server.listen, I want to export it to a file that actually starts up the server (index.js) and I want to export it to my tests, so Supertest can spin it up.
server.js
// App is an Express server set up to use specific middlewares
const app = require('./config')
// Create a server instance so it can be used by to SocketIO
const server = require('http').Server(app)
const io = require('socket.io')(server)
const logger = require('./utils/logger')
const Game = require('./service/game')
const game = new Game()
io.on('connection', (socket) => {
logger.info(`There is a new connection! Socket ID: ${socket.id}`)
// If this is the first connection in the game, start the clock
if (!game.clockStarted) {
game.startClock(io)
game.clockStarted = true
}
game.addPlayer(socket)
socket.on('increaseTime', game.increaseTime.bind(game))
})
module.exports = server
If I understand everything correctly, basically the same thing happens, expect for a few additional steps in the example that you provided. There is no need to start the server, and then use Supertest on it, Supertest handles the process of starting up the server when I use request(server).get, etc.
EDIT 2
Right now I'm not sure whether mocking like that is enough. Some mysterious things leaves the Supertest requests hanging, and it might be that somewhere along the way it can not be ended, although I do not see why would that be the case. Anyway, here is the router:
routes/index.js
const express = require('express')
const router = express.Router()
router.get('', (req, res, next) => {
try {
res.status(200).render('../views/')
} catch (error) {
next(error)
}
})
router.get('*', (req, res, next) => {
try {
res.status(404).render('../views/not-found')
} catch (error) {
next(error)
}
})
module.exports = router
The order of requiring and mocking is correct but the order of setting up and shutting down a server probably isn't.
A safe way is to make sure the server is available before doing requests. Since Node http is asynchronous and callback-based, errors cannot be expected to be handled in async functions without promisification. Considering that server.listen(...) was called in server.js, it can be:
...
server = require('../../server')
expect(server.listening).toBe(true);
await new Promise((resolve, reject) => {
server.once('listening', resolve).once('error', reject);
});
const res = await request(server)
...
close is asynchronous and doesn't return a promise so there's nothing to await. Since it's in a dedicated block, a short way is to use done callback:
afterEach(done => {
server.close(done)
})
In case errors are suppressed in error listener, server.on('error', console.error) can make troubleshooting easier.
Supertest can handle server creation itself:
You may pass an http.Server, or a Function to request() - if the server is not already listening for connections then it is bound to an ephemeral port for you so there is no need to keep track of ports.
And can be provided with Express instance instead of Node server, this eliminates the need to handle server instances manually:
await request(app)
...
I'm trying to understand how the route order works in nodeJS.
I have just one route in my app.js:
const formsCreatorRoute = require('./routes/formsCreator');
app.use('/formsCreator', formsCreatorRoute);
Then, in my formsCreator.js route folder I set two routes:
const express = require('express');
const router = express.Router();
const formsCreatorController = require('../controllers/formsCreator');
router.get('/:pID/:uID', formsCreatorController.load);
router.get('/getForm/:fID/:pID', formsCreatorController.getFormByID);
module.exports = router;
The controller just export two test functions: load and getFormByID.
However, when I run http://localhost:3000/formsCreator/9/215 it should go to the load method in controller (first route), but it goes to the getFormByID. Why is it happen? Will it always run the last route?
Thanks
I'm trying to create a development environment for a small sharepoint application. The application queries a couple of Sharepoint 2013 lists and builds a custom view of the data. Rather than publishing to sharepoint every time I make a change I would like to use NodeJS and Express 4 to mock up the api. I don't need to mock up any of other CRUD activities; just the GET requests.
I started out using the following:
const express = require('express')
const fs = require('fs');
const path = require('path');
const csv = require('csvtojson');
const app = express();
function openAndSend(file, res){
csv().fromFile(path.join(__dirname,file))
.then(jsonarray)=>{
res.setHeader('Content-Type','application/json');
res.send({d:{results:jsonarray}});
});
}
app.get('/dataset',(req,res)=>{
openAndSend('./data/dataset.csv', res);
});
app.get('/people',(req,res)=>{
openAndSet('./data/people.csv', res);
});
How can I use express js to mock up something like
/api/lists/getbytitle("Data Set")/items and /api/lists/getbytitle("People")/items
I tried changing the app.get functions to app.get('/api/lists/getbytitle("Data%20Set")/items', ....); which did not work. However removing get getbytitle("...")" routed correctly.
I was able to solve the issue using express Router and a regex expression for the path/route. The most challenging part was discovering that special characters needed to be encoded.
var router = express.Router();
router.get(/getbytitle\((%22|')Data%20Set(%22|')\)\/items\/?$/i,(req,res,next)=>{
openAndSend('./data/dataset.csv', res);
});
router.get(/getbytitle\((%22|')people(%22|')\)\/items\/?$/i,(req,res,next)=>{
openAndSend('./data/people.csv', res);
});
app.use('/_api/lists',router);
In my Node.js project I am using router file that imports controller files that have actual implementation of the methods. Like below.
app.js
'use strict';
var express = require('express');
var app = express();
var apiRoutes = express.Router();
routes.mainApi(apiRoutes);
app.use('', apiRoutes);
apiRoutes
'use strict';
var controller = require('../controller/apiController');
var uploadController = require('../controller/uploadController');
var fileUpload = require('express-fileupload');
module.exports.mainApi = function (apiRouter) {
apiRouter.post('/login', controller.login);
}
apiController
exports.login = function (req, res) {
// My code for login
};
I know when a api request comes then first app.js is executed. It further sends the request to apiRoutes as I called it in file. In apiRoutes when it finds
apiRouts.post(./login,controller.login) then it calls login function from controller. But I want to know that I am not passing req and res parameters anywhere in the login function then how they are being passed. Basically how this type of calling works.
Thanks in advance.
this function controller.login
as a callback parameter of r.post('path', callback)
and when controller.login is actual called by callback(request, response )
you will get req and res