Absence of the required field does not cause an error. typescript - node.js

In my nodejs project i have a controller
const addQuestion = async(req : Request, res : Response) => {
try {
const question: IQuestion = req.body;
if (!(await checkExist(question))) {
return res.status(409).send();
}
redisAsync.set(question.question, question.answer);
return res.status(200).send();
}
catch (err) {
return res.status(422).send();
}
}
And an interface
interface IQuestion {
question : string;
answer : string;
}
So, I expect to catch an error, when I pass to the controller JSON without question and/or answer fields, then give 422 code, but it not happens and code executes without required fields in object

While TypeScript is statically typed, it still executes as JavaScript that's dynamic. TypeScript doesn't check types at runtime. Therefore when you cast any value to an IQuestion, TypeScript expects you to make sure it is an IQuestion.
Basically, you need to manually verify the fields. A type guard / type predicate is ideal for this:
function isQuestion(question: any): question is IQuestion {
return typeof question === 'object'
&& typeof question.question === 'string'
&& typeof question.answer === 'string';
}
const addQuestion = async (req: Request, res: Response) => {
try {
const question: IQuestion = req.body;
if (!isQuestion(question)) {
return res.status(422).json({ error: 'Expected an IQuestion as body' });
}
if (!(await checkExist(question))) {
return res.status(409).send();
}
redisAsync.set(question.question, question.answer);
return res.status(200).send();
}
catch (err) {
return res.status(422).send();
}
}

Related

Type '(err: any) => void' has no properties in common with type 'QueryOptions' on Node route

I have the following route in a Node controller that is giving me an error that prevents Node from running
public async deletePost(req: Request, res: Response) {
const { id } = req.params;
const deletedPost = await BlogPostModel.findByIdAndDelete(id, err => {
if (err) {
res.status(400).send.send('Error deleting post');
}
});
// needs to send error if post not found (400 status code)
res.status(200).send(deletedPost);
}
I get an error for the err => { section of my code saying:
Type '(err: any) => void' has no properties in common with type 'QueryOptions'
I don't fully understand this error, but it sounds like its requiring I type out the argument in the error handling callback function. However, I've also tried (err:any)=> and that doesn't work as well. Would anyone be able to fill me in on how to correctly use a callback function for error handling here?
Seems like the second argument must be of type QueryOptions, but you pass a function instead
It's not how you should handle errors, you can't mix promises and callbacks
public async deletePost(req: Request, res: Response) {
const { id } = req.params;
try {
const deletedPost = await BlogPostModel.findByIdAndDelete(id);
} catch (err) {
return res.status(400).send.send("Error deleting post");
}
res.status(200).send(deletedPost);
}

Fastify error handler returns wrong error if I call reply.code() before returning the error object

I have an async error handler that looks like this
export default async function processError(
err: FastifyError,
req: FastifyRequest,
res: FastifyReply
): Promise<FastifyError | TErrorReply> {
console.log({ err: err.stack });
let processedError: TErrorReply = err;
if (isDatabaseError(err))
processedError = processDatabaseError(req, res, err);
if (isSchemaValidationError(err))
processedError = processSchemaValidationError(req, res, err);
if (process.env.NODE_ENV === 'development')
processedError = {
...processedError,
stack: err.stack || new Error().stack,
};
console.log({ processedError });
res.code(Number(err?.code || err?.statusCode || 500));
return processedError;
}
I then use it like this
fastify.setErrorHandler(processError);
Inside processSomethingError functions I set a specific code and return a specific error, here is an example.
function processDatabaseError(
req: FastifyRequest,
res: FastifyReply,
err: DatabaseError
): TErrorReply {
const t = getFixedT('en', 'errors');
switch (err.code) {
case '23505': {
const match = err.detail?.match(/\((.+?)\)/);
if (match) {
const key = match[1];
res.code(409);
return {
fields: { [key]: 'duplicated' },
};
}
}
}
res.code(400);
return {
message: t<string, TTranslationKeys['errors']>('fallback'),
};
}
My problem is that if I have
res.code(Number(err?.code || err?.statusCode || 500));
inside processError, just before returning the processedError, it ignores the processedError and instead sends the original error, which is represented by the err argument inside processError function.
If I remove the res.code.. - everything works fine and the actual processedError is returned.
Why is it working like this?
I just want to set the code or the error I want to return, so in headers it shows the right status code.
It turned out that err?.code is a string, so it would throw an error inside res.code function since it wants a number.

Firebase functions issue when using res

I get a strange error at the async (req, res) => { part of the code. The error message is
"Argument of type '(req: Request, res: Response) =>
Promise<Response>' is not assignable to parameter of type '(req:
Request, resp: Response) => void | Promise'. Type
'Promise<Response>' is not assignable to type 'void |
Promise'. Type 'Promise<Response>' is not assignable to
type 'Promise'. Type 'Response' is not assignable to type
'void'.ts(2345)"
I have noticed that the issue comes up specifically when I use res with the dot operator. I used to deploy code like this to Firebase Functions without problems. I setup the development environment from scratch and am using eslint instead of tslint. Are there changes in typescript or any of the lints that is prohibiting to compile and deploy the code? Maybe a setting in my eslint?
The only thing I suspect is that I need to define the return type of the function. But cannot find anyway to define the return type.
exports.fetchQuestionSet2 = functions.https.onRequest(async (req, res) => {
const a = 0;
console.log(a);
const test = req;
console.log(test);
console.log(res);
if (req.method === "PUT") {
return res.status(403).send();
} else {
return res.send({});
}
});
The issue was that I was using the return statement before anytime I was using res. By removing the return keywords the issue is now gone.
exports.fetchQuestionSet2 = functions.https.onRequest(async (req, res) => {
const a = 0;
console.log(a);
const test = req;
console.log(test);
console.log(res);
if (req.method === "PUT") {
res.status(403).send();
} else {
res.send({});
}
});

TypeError: obj.hasOwnProperty is not a function when calling Graphql mutation

I get a strange error and can't figure out what I am doing wrong. I wrote a graphql mutation to call an api:
domainStuff: async (parent, { command, params }, { models }) => {
console.log("Params:", params);
const result = await dd24Api(command, params);
return result;
}
This it the function I call:
export default async (command, parameter) => {
const args = _.merge({ params: parameter }, auth);
// Eleminate copying mistakes
console.log(typeof args);
const properCommand = command + "Async";
const result = await soap
.createClientAsync(apiWSDL)
.then(client => {
return client[properCommand](args)
.then(res => {
console.log(res[command + "Result"]);
return res[command + "Result"];
})
.catch(err => {
console.log(err);
return err;
});
})
.catch(err => console.log(err));
return result;
with this query variables:
{
"command": "CheckDomain",
"params": {"domain": "test.it"}
}
The console.log shows me that args is an object, but I get this error (from the first catch block):
TypeError: obj.hasOwnProperty is not a function
How can that be? After all I checked whether it is an object and it is. More strange, if I give a hardcoded object into the query, this for example:
domainStuff: async (parent, { command, params }, { models }) => {
console.log("Params:", params);
const result = await dd24Api(command, {domain: "test.com"});
return result;
}
then it works perfectly fine. What am I doing wrong? Thx for any help in advance.
EDIT: I am using "graphql-server-express": "^0.8.0" and "graphql-tools": "^1.0.0"
If you are using graphql-js there are a lot of places where new objects are created using Object.create(null) which is different from {} you can read an explanation about that here Creating Js object with Object.create(null)?
But essentially an object created with Object.create(null) has no hasOwnProperty method
You can try it in node with
const obj1 = Object.create(null)
console.log(typeof obj1.hasOwnProperty)
// 'undefined'
const obj2 = {}
console.log(typeof obj2.hasOwnProperty)
// 'function'
Another method you can use for determining if an object has a key that will work on an object created with Object.create(null) is
function hasKey(obj, key) {
return Object.keys(obj).indexOf(key) !== -1
}
You can convert it to a standard Object by:
data = JSON.parse(JSON.stringify(data));
Okay, after an extensive period of trial and error I was able to fix the problem. Changing the position of the objects I pass into the .merge function was the solution. The working code is:
const args = _.merge(auth, { params: parameter });
Both are objects, but it seems like the object coming from the graphql variables did not have the required hasOwnProperty method. Simply speaking, instead of copying the "good object" (auth) into the bad one (parameter), I copy the bad one into the good one which has the needed function.
Hopefully this helps someone else who also experiences this error.

Node code not blocking?

I have a constructor which fetches data from a DynamoDB using promisified dynogels to populate part of the object's properties.
So after instantiating an instance of that object, the property is not populated, here is an extract of the code:
export class QueryAuthoriser {
authPerms: [AuthPerms];
constructor (data: string) {
AuthPermsDDB.scan().execAsync().then ( (perms) => {
perms.Items.forEach(element => {
this.authPerms[element.name] = <AuthPerms> element.attrs
})
}).catch (err => {
console.log ('%%%%%%%%%%%%%% Err loading authPerms: ', err)
})
}
authFieldAccess (fieldName: string, args?:any): Promise<boolean> {
return new Promise ((resolve, reject) => {
console.log ('________________ authFieldAccess called for: ', fieldName)
console.log ('________________ this.authPerms entry: ', this.authPerms[fieldName])
resolve (true)
})
[...]
}
So when authFieldAccess method is called, the field this.authPerms is undefined. How can I fix this?
Thanks, I'm learning node and typescript the hard way :O
You generally don't want to carry out an asynchronous operation in a constructor because it complicates creating an object and then knowing when the async operation is done or if it had an error because you need to allow the constructor to return the object, not a promise that would tell you when the async operation was done.
There are several possible design options:
Option #1: Don't do any async operation in the constructor. Then, add a new method with an appropriate name that does the async operation and returns a promise.
In your case, you could make the new method be scan() that returns a promise. Then, you'd use your object by creating it and then calling scan and then using the returned promise to know when the data is valid.
I don't know TypeScript myself, so I'll give a modified version of your code, but the concept is the same either way whether it's TypeScript or plain Javascript:
export class QueryAuthoriser {
authPerms: [AuthPerms];
constructor (data: string) {
}
scan () {
return AuthPermsDDB.scan().execAsync().then ( (perms) => {
perms.Items.forEach(element => {
this.authPerms[element.name] = <AuthPerms> element.attrs
})
}).catch (err => {
console.log ('%%%%%%%%%%%%%% Err loading authPerms: ', err)
})
}
}
// usage
let obj = new QueryAuthoriser(...);
obj.scan(...).then(() => {
// the object is full initialized now and can be used here
}).catch(err => {
// error here
})
Option #2: Initiate the async operation in the constructor and use a promise in the instance data for the caller to know when everything is done.
export class QueryAuthoriser {
authPerms: [AuthPerms];
constructor (data: string) {
this.initialScan = AuthPermsDDB.scan().execAsync().then ( (perms) => {
perms.Items.forEach(element => {
this.authPerms[element.name] = <AuthPerms> element.attrs
})
}).catch (err => {
console.log ('%%%%%%%%%%%%%% Err loading authPerms: ', err)
})
}
}
// usage
let obj = new QueryAuthoriser(...);
obj.initialScan.then(() => {
// the object is full initialized now and can be used here
}).catch(err => {
// error here
});
Option #3: Use a factory function that returns a promise that resolves to the object itself.
export createQueryAuthorizer;
function createQueryAuthorizer(...) {
let obj = new QueryAuthorizer(...);
return obj._scan(...).then(() => {
// resolve with the object itself
return obj;
})
}
class QueryAuthoriser {
authPerms: [AuthPerms];
constructor (data: string) {
}
_scan () {
return AuthPermsDDB.scan().execAsync().then ( (perms) => {
perms.Items.forEach(element => {
this.authPerms[element.name] = <AuthPerms> element.attrs
})
}).catch (err => {
console.log ('%%%%%%%%%%%%%% Err loading authPerms: ', err)
})
}
}
// usage
createQueryAuthorizer(...).then(obj => {
// the object is fully initialized now and can be used here
}).catch(err => {
// error here
});
My preference is for option #3 for several reasons. It captures some shared code in the factory function that every caller has to do in the other schemes. It also prevents access to the object until it is properly initialized. The other two schemes just require documentation and programming discipline and can easily be misused.

Resources