How to start an Express server in best practice? - node.js

I have an Express REST API server written in TypeScript.
At first, I started server like this -
const initServer = async() => {
await connectDb();
await server.listen(secrets.port, secrets.hostname, () => {
logger.info(
`Running server at http://${secrets.hostname}:${secrets.port} in ${
secrets.env
} env and API version is ${secrets.apiVersion}`
);
});
}
initServer().catch(error => logger.error(`Init server went wrong with: ${error}`));
Then I read a blog post suggesting to use .then().catch() -
async function initServer() {
// Connect the database first
await connectDb()
.then(() =>
// then start the server
server.listen(secrets.port, secrets.hostname, () => {
logger.info(
`Running server at http://${secrets.hostname}:${secrets.port} in ${
secrets.env
} env and API version is ${secrets.apiVersion}`
);
})
)
.catch(err => {
logger.error(`Initializing server went wrong with: ${err}`);
process.exit(1);
});
}
Then I read another blog post saying "catch the error first" -
async function initServer() {
// Connect the database first
await connectDb()
// then start the server
.then(() => server.listen(secrets.port, secrets.hostname))
.catch(err => {
logger.error(`Initializing server went wrong with: ${err}`);
process.exit(1);
})
// then announce the server info
.finally(() => {
logger.info(
`Running server at http://${secrets.hostname}:${secrets.port} in ${
secrets.env
} env and API version is ${secrets.apiVersion}`
);
});
}
But I feel like I'm not doing it right. Please educate me what I'm doing wrong.
How should I start the server?

Related

Api works only on localhost

on my machine i get a json response when i consume my api and eveything is working fine , but the weird thing is my api not giving me data when i upload it online , i am using namecheap shared hosting , this is the result i get when try to consume api
this is my index.js code
app.get("/project/index/login", (req, res) => { (async () => {
try {
await client.login();
res.send("Login successful!");
} catch (err) {
res.send("Login failed!");
} })(); });
app.get("/project/index/user/:id", (req, res) => {
const id = req.params.id; (async () => {
try{
const data = await client.getInfo({ id: id })
res.json(data.data);
}catch(error){
res.send(error);
}
})(); });
app.listen(port, () => {
console.log(`Listening on port http://localhost:${port}...`);
});
It's probably because your API server is running only on localhost. Check your API server!. You must run the API server which has a domain.

Unit Test with Express / Mocha / Mongodb Memory Server

I would like to configure my project in order to run unit test for some API endpoints (that call the database). I'm using :
ExpressJS
MongoDB (no Mongoose)
Mocha / Chai
Mongodb Memory Server (to mock the DB)
// app.ts
export const app = express();
const port = process.env.PORT;
app.use("/my-route", myRoutes);
mongoConnect().then(() => {
app.listen(port, () => {
console.log(`Listening on port ${port}`);
});
});
// database.ts
export const mongoConnect = async () => {
try {
let MONGODB_URI = process.env.MONGODB_URI;
if (process.env.NODE_ENV === "test") {
const mongoServer = await MongoMemoryServer.create();
MONGODB_URI = mongoServer.getUri();
}
const client: MongoClient = await MongoClient.connect(MONGODB_URI);
_db = client.db("dbName");
_mongoClient = client;
if (process.env.NODE_ENV === "test") {
console.log("Connected to MongoDB Test");
} else {
console.log("Connected to MongoDB");
}
} catch (err) {
console.log("Error connecting to MongoDB:", err);
throw err;
}
};
export const getMongoClient = () => {
if (_mongoClient) {
return _mongoClient;
}
throw "Mongo client doesn't exist";
};
export const getDb = () => {
if (_db) {
return _db;
}
throw "No database found!";
};
// test.ts
let mongoClient: MongoClient;
let db: Db;
before(function (done) {
mongoConnect()
.then(() => {
db = getDb();
mongoClient = getMongoClient();
return db.createCollection("wordsCollection");
})
.then(() => {
db.collection("wordsCollection").insertMany(data);
})
.catch((err) => console.log(err))
.finally(() => done());
});
after(function (done) {
db.dropDatabase();
mongoClient.close().then(() => {
done();
});
});
it("test", async function () {
let res = await chai
.request(app)
.post("/my-route/hello")
.send({ excludeIds: [] });
expect(res.status).to.equal(200);
});
});
But it's not working...
If I call mongoConnect() in test.ts it console.log twice Connected to MongoDB Test. But if I don't call the function it throws me error because MongoClient is undefined.
I think await chai.request(app) already calls the database and server but I need to create Collection and Documents before. So I need to connect to the DB before the test.
Any help would be greatly appreciated.
I found a solution, I don't know if it's best practice but it works and is pretty easy, thanks to this post : https://stackoverflow.com/a/70285190/10547153.
I needed to add a condition in app.ts before making the connection to the database and the server in order to launch them only if it's called by Node itself.
if (require.main === module) {
mongoConnect().then(() => {
app.listen(port, () => {
console.log(`Listening on port ${port}`);
});
});
}
When a file is run directly from Node.js, require.main is set to its
module. That means that it is possible to determine whether a file has
been run directly by testing require.main === module.
Now I can connect to the mocked database from test.ts and only one connection will be triggered.

Testing sockets with Mocha

I'm starting to learn how to test and I'm having some troubles.
I'm trying to run tests on a socket server but I'm not able to send any message, just connecting an receiving sometimes.
Here is what I have:
import { expect } from 'chai';
import { io as Client } from 'socket.io-client';
import { httpServer } from '../src/loader/app.js';
let server;
let socketUrl;
describe('Sockets test cycle', () => {
before(async function () {
server = await startServer();
socketUrl = `http://localhost:${server.address().port}`;
});
after(function () {
server.close();
});
describe('SOCKET', () => {
it('Should receive a message', async () => {
const client = new Client(socketUrl);
const message = {
text: 'Test',
username: 'test#test',
};
client.on('messages', (arg, done) => {
console.log('Receiving message');
expect(arg).to.eql(message);
client.disconnect();
done();
});
//Only with this await I'm able to connect to server.
//await new Promise((r) => setTimeout(r, 1000));
client.on('connect', () => {
client.emit('new-message', message);
console.log('Sending message');
});
});
});
});
async function startServer() {
return new Promise((resolve, reject) => {
const PORT = 0;
const server = httpServer.listen(PORT, () => {
console.log(`Server listening on port ${server.address().port}`);
resolve(server);
});
server.on('error', (error) => {
console.log(`Error on server: ${error}`);
reject(error);
});
});
}
EDIT:
Here is the output:
[2022-04-03T23:03:58.979] [INFO] default - Starting Products Router
[2022-04-03T23:03:58.981] [INFO] default - Starting Carts Router
[2022-04-03T23:03:58.982] [INFO] default - Starting Users Router
[2022-04-03T23:03:58.982] [INFO] default - Starting Orders Router
[2022-04-03T23:03:58.984] [INFO] default - Starting MessageSocketRouter
Sockets test cycle
Server listening on port 53178
SOCKET
✔ Should receive a message
1 passing (20ms)
If I add the await in the middle with 1 second for waiting, I'm able to connect, but that's all I could do.
Could you give me any idea or any hint? I've been reading the oficial documentation but I'm kind of lost.
Thanks!

Multiple await in single async function in nodejs

I am trying to connect to to db then run my server. So I am using aync await like below:
startApp = async()=>{
try {
await mongoose.connect('mongodb+srv://tanvirgeek:<password>#cluster0-e7kwf.mongodb.net/test?
retryWrites=true&w=majority',)
console.log('connected to db');
await app.listen(5000);
console.log('server connected on port 5000')
} catch (error) {
console.log(error);
}
}
app.get('/',(req,res)=>{
res.send("hello world");
})
startApp();
I intentionally gave a wrong db connect URL without a password to get error in the console. In the console I am getting no error and this message:server connected on port 5000.
My desired output is error message in the console, without this successful server running message. How can I achieve this?
you are adding a ',' between your path connection and ')'. In mongoose doc:
mongoose.connect('mongodb+srv://tanvirgeek:<password>#cluster0-e7kwf.mongodb.net/test?
retryWrites=true&w=majority', { useNewUrlParser: true }).
catch(error => handleError(error));
// Or:
try {
await mongoose.connect('mongodb+srv://tanvirgeek:<password>#cluster0-e7kwf.mongodb.net/test?
retryWrites=true&w=majority', { useNewUrlParser: true });
} catch (error) {
handleError(error);
}
source mongoose

Is there any API in express to stop http server in code?

I'm setting up a express server in electron.How can I stop it in a button handler?
can't find a express API for this.
// create a express server
ipcMain.on('create-http-server', (event, data) => {
const port = data.note;
isPortAvailable( data.note ).then(() => {
const test = httpApp.listen( port );
event.reply('create-http-server-reply',{ result:'success'} );
})
.catch( err => {
event.reply('create-http-server-reply',{ result:'failed',err:err } );
})
})
ipcMain.on('stop-http-server',(event,data) => {
// how to stop express server here?
console.log( data )
})

Resources