Fastify async try catch does not resolve - node.js

I have simple route:
fastify.post('/subscribe', {
schema: subscribeSchema,
handler: async (req, reply) => {
try {
const events = req.body.events;
const allEventsProcessingData = await Promise.all(events.map(async (ev) => {
return {
event: ev,
feedEventsData: await getFeedEventData(ev),
feedMainMarketsData: await getFeedEventMainMarketsData(ev),
}
}));
// process HTML by events data
// allEventsProcessingData
const compiledHTML = '';
return reply.send(compiledHTML);
} catch (e) {
console.log('e', e);
return reply.send(HTTP_RESPONSES.FAIL);
}
}
});
When error happens, the response does not send, why is it? And how to send it? Tried this https://github.com/fastify/fastify/issues/1864#issuecomment-534233381, but it did not work.

Related

Cannot Getting data from url query parameters. from a node rest api

I have this code to make a get, post, put and delete request,
const express = require("express");
const TutorialModel = require("../models/tutorialModel");
const router = express.Router();
router.post("/tutorials", async (req, res) => {
try {
const tutorial = new TutorialModel(req.body);
const createTutorial = await tutorial.save();
res.status(201).send(createTutorial);
} catch (e) {
res.status(400).send(e);
}
});
router.get("/tutorials", async (req, res) => {
try {
const TutorialsData = await TutorialModel.find();
res.status(200).send(TutorialsData);
} catch (e) {
res.status(404).send(e);
}
});
router.get("/tutorials/:id", async (req, res) => {
try {
const _id = req.params.id;
const TutorialData = await TutorialModel.findById(_id);
if (!TutorialData) {
res.status(404).send();
} else {
res.send(TutorialData);
}
} catch (e) {
res.status(500).send(e);
}
});
router.get("/tutorials/:title", async (req, res) => {
try {
const _title = req.params.title;
const TutorialData = await TutorialModel.find({ title: _title });
if (!TutorialData) {
res.status(404).send();
} else {
res.send(TutorialData);
}
} catch (e) {
res.status(500).send(e);
}
});
router.put("/tutorials/:id", async (req, res) => {
try {
const _id = req.params.id;
const updateTutorial = await TutorialModel.findByIdAndUpdate(
_id,
req.body,
{ new: true }
);
res.send(updateTutorial);
} catch (e) {
res.status(400).send(e);
}
});
// Deleting student data by its Id
router.delete("/tutorials/:id", async (req, res) => {
try {
const _id = req.params.id;
const deleteTutorial = await TutorialModel.findByIdAndDelete(_id);
if (!_id) {
return res.status(400).send();
} else {
res.send(deleteTutorial);
}
} catch (e) {
res.status(500).send(e);
}
});
router.delete("/tutorials", async (req, res) => {
try {
const deleteTutorial = await TutorialModel.remove();
if (!deleteTutorial) {
return res.status(400).send();
}
else {
res.send(deleteTutorial);
}
} catch (e) {
res.status(500).send(e);
}
});
module.exports = router;
I've successfully made all request including get request with 'id' But when I try to make get request using 'title' parameter I'm getting data of get request of "/tutorials" not of "tutorials/:title". What is the issue? Can anyone tell me please?
The route GET "/tutorials/:id" will catch all your GET requests like /tutorials/something. It does not distinguish if you pass an id or a title.
:id is used to tell Express to capture the something path in the URL in req.params.id. That's all.
If you want to have another route to get tutorials by title, you should use another form. For example, GET "/tutorialsByTitle/:title".

how to wrap axios function and let parent to wait?

here's I've my axios function wrapped within a custom function:
async function getOrder(orderId) {
// ...
await axios({
method: 'post',
url: endpoint + "/mymethod",
data: request
}).then(function (response) {
var data = response.data.result;
return data;
}).catch(function (error) {
return { "error": error };
});
}
but if than i call that function:
router.get('/getOrder/:id', (req, res) => {
let result = getOrder(req.params.id);
res.json(result);
})
it returns nothing (since its async and don't wait the .then()).
what's the best way to wrap axios/async function and call from outside?
I think you're missing await in your codes:
router.get('/getOrder/:id', async (req, res) => {
let result = await getOrder(req.params.id);
res.json(result);
})
====================
[New Update]
for me in API function:
async getOrder(orderId) {
try {
const response = await axios.post(endpoint + "/mymethod")
return response.data
} catch (error) {
return { "error": error }
}
}
and get the result:
router.get('/getOrder/:id', async (req, res) => {
let result = await getOrder(req.params.id);
res.json(result);
})
===========
[New Update2] here is my sample async/await function with axios
const axios = require("axios")
async function getOrder(orderId) {
try {
const response = await axios.get("http://google.com")
return response.data
} catch (error) {
return { "error": error }
}
}
async function main() {
let result = await getOrder();
console.log(result, "##")
}
main()
====================
[New Update3] new Promise with axios:
const axios = require("axios")
async function getOrder(orderId) {
// try {
// const response = await axios.get("http://google.com")
// return response.data
// } catch (error) {
// return { "error": error }
// }
return await new Promise((resolve, reject) => {
axios({
method: 'get',
url: "http://google.com"
}).then(function (response) {
var data = response.data
resolve(data)
}).catch(function (error) {
reject({ "error": error })
});
})
}
async function main() {
let result = await getOrder();
console.log(result, "##")
}
main()
I think you are missing an await so you do not return a promise but wait for the result of getOrder to be passed in res.json :
router.get('/getOrder/:id', async (req, res) => {
let result = await getOrder(req.params.id);
res.json(result);
})

How to mock function using node-tap on fastify inject

I want to make 100% coverage on this function with node-tap but I can't mock the error part, it always throw
Cannot find module 'create' Require stack: - /home/mptr8/Code/Projects/me/fastify-example/fastify-postgres/test/integration.js
But I have create function on my query.js file, what do I do wrong here? Why it doesn't invoke the method?
t.mock("../query.js", {
create: () => {
throw new Error();
},
});
I also try this combination, because query.js are dependent on db.js. Now the mock error gone but still I'm not getting the error throw from my fastify.inject.
t.mock("../db.js", {
"../query.js": {
create: () => { throw new Error() },
},
});
app.post("/", async (request, reply) => {
try {
const { body } = request;
const book = create(body.title);
reply.send(book);
} catch (error) {
// this part are not covered
reply.code(500).send({ message: "internal server error" });
}
});
here are my complete code. You can see the full code on this github repository.
// server.js
const fastify = require("fastify");
const {
migration,
create,
} = require("./query");
const db = require("./db");
function build(opts = {}) {
const app = fastify(opts);
migration();
app.post("/", async (request, reply) => {
try {
const { body } = request;
const book = create(body.title);
reply.send(book);
} catch (error) {
reply.code(500).send({ message: "internal server error" });
}
});
app.addHook("onClose", (_instance, done) => {
db.close();
done();
});
}
module.exports = build;
// db.js
const { Pool } = require("pg");
const pool = new Pool({
connectionString:
"postgresql://postgres:postgres#localhost:5432/fastify_postgres?schema=public",
});
module.exports = {
query: (text, params) => pool.query(text, params),
close: () => pool.end(),
};
// query.js
const db = require("./db");
async function migration() {
await db.query(`
CREATE TABLE IF NOT EXISTS books (
id serial PRIMARY KEY,
title varchar (255) NOT NULL
)
`);
}
async function create(title) {
return await db.query("INSERT INTO books (title) VALUES ($1)", [title]);
}
module.exports = { migration, create };
// test.js
const tap = require("tap");
const fastify = require("../server");
tap.test("coba", async (t) => {
const app = await fastify();
t.test("should success create books", async (t) => {
const response = await app.inject({
method: "POST",
url: "/",
payload: {
title: "Hello,World!",
},
});
t.equal(response.statusCode, 200);
});
t.test("should throw error", async (t) => {
const app = await fastify();
// it doesn't throw the error :((
t.mock("../query.js", {
create: () => {
throw new Error();
},
});
const response = await app.inject({
method: "POST",
url: "/",
payload: {
title: "Hello,World!",
},
});
t.equal(response.statusCode, 500);
// call app close on last test child to close app and db properly
app.close();
});
});
You should use the returned value by the t.mock function:
const build = t.mock({
"../server": {
"./query.js": {
create: () => { throw new Error() },
}
}
})
const app = await build({})

Callback error handler didn't stop execute function

I'm trying to develop an API post, in middle execution I have validation such as check name already in use or not. I set error handler callback, it successfully send response 'Already registered', but when I checked to CLI, it show error
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
I dont know whats wrong, I use this error handler in the past and it seems look ok.
Here is my code in router:
createUserAccount: async function (req, res) {
const programData = req.body;
try {
await service.create(programData, function (code, err, result) {
if (err) {
if(code === 409){
res.status(HTTPSTATUS.CONFLICT).send(err.message);
} else {
res.status(HTTPSTATUS.BAD_REQUEST).send(err.message);
}
} else {
res.status(HTTPSTATUS.CREATED).json(result);
}
})
} catch (e) {
console.log(e)
res.status(HTTPSTATUS.BAD_REQUEST).json("Failed.");
}
Here is my function in my service:
const config = require('#configs/config.json')
const sequelize = require('sequelize');
const SEQUELIZE = new sequelize(config[env]);
module.exports = {
createAccount: async (name, password, callback) => {
try {
let check,
institution_id;
const checkName = await Profile.count(
{
where: {
name: name
}
}
);
//result checkName = 1
if(checkName > 0){
//then successfully execute this condition and
return callback(409, 'Already registered.', null);
//this show in console ----> POST /API/v1/user/profile 409 616.152 ms - 31
}
await Login.create({
username: email,
password: password
}).then(resLogin => {
const response = {
id: resLogin.id,
}
callback(201, null, response);
}).catch( error => {
callback(400, error, null);
})
} catch (e) {
callback(400, e, null);
}
},
create: async (payload, callback) => {
let loginID = null;
let {
profile,
address
} = payload;
let {
name,
email,
password
} = profile;
try {
await module.exports.createAccount(name, password, function (code, error, result) {
if(error){
const res = {message: error};
//what I need is the execution is end in here
return callback(code, res, null);
}
loginID = result.id;
});
//but the fact is it still execute this whole function if got callback error from createAccount()
let transaction = await SEQUELIZE.transaction();
await Address.create(address, {transaction})
.then( async resAddress => {
await transaction.commit();
return callback(201, null, resProfile);
}).catch(async e => {
return callback(400, e, null);
})
} catch (e) {
console.log(e);
callback(e, null);
}
};

fastify-mongodb : "mongo" is undefined when accessed

I'm attempting to use Fastify and fastify-monogdb.
Currently I have the following...
In my /src/index.js
const routes = require("./routes");
const fastify = require("fastify")({
logger: true
});
routes.forEach((route, index) => {
fastify.route(route);
});
fastify.register(require("fastify-mongodb"), {
url: "mongodb://localhost:27017/parkedcars"
});
const startFastify = async () => {
try {
await fastify.listen(3333);
fastify.log.info(`server listening on ${fastify.server.address().port}`);
} catch (err) {
fastify.log.error(err);
process.exit(1);
}
};
startFastify();
In my /routes/index.js I have a route...
const carController = require("../controllers/carController");
{
method: "POST",
url: "/api/create/parkedcar",
handler: carController.createParkedCar
}
And finally in my /controllers/carController...
const fastify = require("fastify")();
exports.createParkedCar = async (req, reply) => {
try {
let car = { ...req.body };
const db = fastify.mongo.db
*// will do insert here*
return car;
} catch (err) {
throw boom.boomify(err);
}
};
When I attempt to call:
const db = fastify.mongo.db
I get an error that says...
"Cannot read property 'db' of undefined"
What am I doing wrong here?
How is mongo undefined at this point?
Doesn't "fastify.register" make this accessible to me?
You need to do the require("fastify")() only in your once per application because it is a factory and not a singleton, so every time you run the require you are creating a brand new HTTP server!
The magic is to use the .register in a proper way and/or using function instead of arrow function in the handler.
For your use case you could change the carController:
exports.createParkedCar = function handler (req, reply) {
let car = { ...req.body };
const db = this.mongo.db
*// will do insert here*
db.insert(...)
.then(() => reply.send(car))
.catch((err) => reply.send(boom.boomify(err)))
}
because all the function handlers, in fastify, are bound to the fastify server instance (like this aFunction.bind(fastify)). The arrow functions can't be binded.
Another options is to use the register:
// /routes/index.js
module.exports = (fastify, opts, next) => {
fastify.route({
method: "POST",
url: "/api/create/parkedcar",
handler: async (req, reply) => {
try {
let car = { ...req.body };
const db = fastify.mongo.db
*// will do insert here*
return car;
} catch (err) {
throw boom.boomify(err);
}
});
next() // dont forget it
}
For more info checkout the docs

Resources