getting undefined from async await in typescript node - node.js

Use case:
I am trying to insert a record inside the amazon QLDB using Node and typescript.
I am able to insert the record/document successfully and it returns me documentID in return.
there are 2 controllers: EntityController and CommonController
-EntityController extends CommonController
-EntityController has the code for getting req object converting it into the model object and the calling insert() function that has been extended from the CommonController.
problem
I am trying to propagate that documentID to all the way to my API call, but somehow I am getting undefined in the EntityController.
whereas I am able to print the documentID in CommonController.
I am not sure why I am getting undefined when I am clearly returning a value.
const CommonController = require("../template/controller");
import { Request, Response } from 'express';
const tableName:string = "entities";
const EntityModel = require("./model")
class EntityController extends CommonController {
async insertEntitiy(req:Request,res:Response) {
async insertEntitiy(req:any,res:any) {
console.log(req);
console.log("===========");
console.log(req.body);
let entity = new EntityModel();
entity.balance = req.body.balance;
entity.firstName = req.body.firstName;
entity.lastName = req.body.lastName;
entity.email = req.body.email;
try {
let documentIds = await this.insert(tableName,entity);
console.log("--------- inside insertEntity fiunction()---------");
console.log(documentIds);
console.log("------------------");
res.status(200).send(documentIds[0]);
} catch (error) {
console.error(`error in creating Entity: ${error}`);
res.status(500).send({ errMsg: `error in creating Entity: ${error}` });
}
}
}
module.exports = new EntityController();
import { createQldbWriter, QldbSession, QldbWriter, Result, TransactionExecutor } from "amazon-qldb-driver-nodejs";
import { Reader } from "ion-js";
import { error, log } from "../qldb/LogUtil";
import { getFieldValue, writeValueAsIon } from "../qldb/Util";
import { closeQldbSession, createQldbSession } from "../qldb/ConnectToLedger";
module.exports = class Conroller {
async insert(tablename:string, object:any): Promise<Array<string>> {
let session: QldbSession;
let result:Array<string>;
try {
session = await createQldbSession();
await session.executeLambda(async (txn) => {
result = await this.insertDocument(txn,tablename,object);
console.log("---------result inside insert fiunction()---------");
console.log(result);
console.log("------------------");
return (Promise.resolve(result));
})
} catch (e) {
error(`Unable to insert documents: ${e}`);
return(["Error"]);
} finally {
closeQldbSession(session);
}
}
/**
* Insert the given list of documents into a table in a single transaction.
* #param txn The {#linkcode TransactionExecutor} for lambda execute.
* #param tableName Name of the table to insert documents into.
* #param documents List of documents to insert.
* #returns Promise which fulfills with a {#linkcode Result} object.
*/
async insertDocument(
txn: TransactionExecutor,
tableName: string,
documents: object
): Promise<Array<string>> {
const statement: string = `INSERT INTO ${tableName} ?`;
const documentsWriter: QldbWriter = createQldbWriter();
let documentIds: Array<string> = [];
writeValueAsIon(documents, documentsWriter);
let result: Result = await txn.executeInline(statement, [documentsWriter]);
const listOfDocumentIds: Reader[] = result.getResultList();
listOfDocumentIds.forEach((reader: Reader, i: number) => {
documentIds.push(getFieldValue(reader, ["documentId"]));
});
console.log("---------documentIds---------");
console.log(documentIds);
console.log("------------------");
return (documentIds);
}
}
ouptut :
---------documentIds---------
[ '4o5UZjMqEdgENqbP9l7Uhz' ]
---------result inside insert fiunction()---------
[ '4o5UZjMqEdgENqbP9l7Uhz' ]
--------- inside insertEntity fiunction()---------
undefined

As #daniel-w-strimpel pointed out in the comments, your insert method returns only in the catch part.
Try this:
insert(tablename:string, object:any): Promise<Array<string>> {
let session: QldbSession;
let result: Array<string>;
try {
session = await createQldbSession();
return session.executeLambda(async (txn) => {
result = await this.insertDocument(txn,tablename,object);
console.log("---------result inside insert fiunction()---------");
console.log(result);
console.log("------------------");
return result;
})
} catch (e) {
error(`Unable to insert documents: ${e}`);
return(["Error"]);
} finally {
closeQldbSession(session);
}
}
...
In return session.executeLambda you return the Promise.
In return result; you return the actual value.
More on promises here: https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html

Related

Google functions firestore snapshot return after empty

I am using triggers for firestore on "Products" collection.
The trigger calls the "products" function. However, change.before.data() returns data before the update.
But the "change.after.data() is returned empty
Code:
// that's how it works:
export const produtosWrite = functions.firestore.document('estabelecimentos/{idLoja}/privado/estoque/produtos/{idProduto}')
.onWrite((change, context)=> {
functions.logger.debug("after:", change.after.data(), change.after.exists);
return true;
})
//that doesn't work:
export const produtosWrite = functions.firestore.document('estabelecimentos/{idLoja}/privado/estoque/produtos/{idProduto}')
.onWrite((change, context)=> {
if(change.after.exists) {
functions.logger.debug("after2:", change.after.data());
}
return true;
})
use an if statement that checks and returns data or not.
here's the code:-
export const produtosWrite = functions.firestore.document('/estabelecimentos/{idLoja}/privado/estoque/produtos/{idProduto}')
.onWrite((change, context)=> {
if (change.before.exists) {
functions.logger.debug("before:", change.before.data());
}
else (change.after.exists) {
functions.logger.debug("before (deleted):", change.before.data());
}
})

Cancel data insert inside beforeInsert event if it already exists in table

There is a need to insert multiple rows in a table. I am using TypeOrm which has .save() method.
.save() method is used to insert data in bulk, and it checks for the inserting primary key ID. If they exist it updates, otherwise inserts.
I want to do the checking on some other field shortLInk. Which is not possible with .save()
So I tried to do inside beforeInsert() event, things are fine but I need to cancel the isnertions if row is find.
Is there any way to achieve it? I couldn't find anything in documentation.
I can throw an error inside beforeInsert() but it will cancel whole insertions.
async shortLinks(links: Array<string>): Promise<Array<QuickLinkDto>> {
const quickLinks: Array<QuickLinkDto> = links.map((link) => ({
actualLink: link,
}));
return this.quickLinkRepository.save(quickLinks, {});
}
#Injectable()
export class QuickLinkSubscriber
implements EntitySubscriberInterface<QuickLink>
{
constructor(
datasource: DataSource,
#InjectRepository(QuickLink)
private readonly quickLinkRepository: Repository<QuickLink>,
) {
datasource.subscribers.push(this);
}
listenTo() {
return QuickLink;
}
async beforeInsert(event: InsertEvent<QuickLink>) {
const shortLink = await getShortLink(event.entity.actualLink);
const linkExists = await this.quickLinkRepository.findOne({
where: {
shortLink,
},
});
if (linkExists) {
// Discard the insertion if the row already exists
return delete event.entity; // throws error
}
event.entity.shortLink = shortLink;
}
}
When you use a transaction, all the operations are atomic, meaning either all of them are executed or none of them are. you can check for the existence of the shortLink before the insert and if it exists, you can cancel the whole transaction.
async shortLinks(links: Array<string>): Promise<Array<QuickLinkDto>> {
const quickLinks: Array<QuickLinkDto> = links.map((link) => ({
actualLink: link,
}));
const queryRunner = this.quickLinkRepository.manager.connection.createQueryRunner();
try {
await queryRunner.startTransaction();
const result = await this.quickLinkRepository.save(quickLinks, {});
await queryRunner.commitTransaction();
return result;
} catch (error) {
await queryRunner.rollbackTransaction();
throw error;
} finally {
await queryRunner.release();
}
}
#Injectable()
export class QuickLinkSubscriber
implements EntitySubscriberInterface<QuickLink>
{
constructor(
datasource: DataSource,
#InjectRepository(QuickLink)
private readonly quickLinkRepository: Repository<QuickLink>,
) {
datasource.subscribers.push(this);
}
listenTo() {
return QuickLink;
}
async beforeInsert(event: InsertEvent<QuickLink>) {
const shortLink = await getShortLink(event.entity.actualLink);
const linkExists = await this.quickLinkRepository.findOne({
where: {
shortLink,
},
});
if (linkExists) {
// Discard the insertion if the row already exists
throw new Error('The short link already exists');
}
event.entity.shortLink = shortLink;
}
}

Pagination in TypeORM, NestJS

I have multiple search condition in my form. if user does not enter anything then all the data should be return. if he gives some search input then only those matching record should be return.
Below code is working fine. the only thing is ,sometimes record are coming around 30-40 with filter condition as well so I have been given requirement to introduce pagination. we have to show 10 record at a time in page with or without filter condition.
Could you please guide me how can I introduce pagination in below code.
async findAll(queryCertificateDto: QueryCertificateDto): Promise<Certificate[]> {
const { certificateNo, requestStatus, protoColNo, noOfSubjects} =queryCertificateDto
const query = this.certificateRepository.createQueryBuilder('certificate');
if (certificateNo) {
query.andWhere('certificate.certificateNo=:certificateNo', { certificateNo });
}
if (requestStatus) {
query.andWhere('certificate.requestStatus=:requestStatus', {
requestStatus,
});
}
if (protoColNo) {
query.andWhere('certificate.protoColNo=:protoColNo', { protoColNo });
}
if (noOfSubjects) {
query.andWhere('certificate.noOfSubjects=:noOfSubjects', { noOfSubjects });
}
const certificates = await query.getMany();
return certificates;
}
export const getAllFaqs = () => async (req: Request, res: Response): Promise<void> => {
const {
query: { userType ,page ,perPage},
} = req;
const faqsPagesRepository = getCustomRepository(FaqsPageRepository);
let where: FindConditions<Faqs> = {};
if (userType) {
where = { ...where, userType };
}
const limit =Number(perPage);
const offset=(Number(page)-1)*limit;
const result = await faqsPagesRepository.findAndCount({
where,
take:limit,
skip:offset,
});
res.status(200).json({ result });
};

MongoDB aggregation with typescript

I have a User model and I want to perform generic aggregation. mean any array of object I pass to this function it executes.
this is the sample of getUser function
public async getUser(aggregate: object): Promise<ResponseDTO> {
let response = {} as ResponseDTO;
const [err, user] = await To(User.aggregate(aggregate));
if (user) {
response = { success: true, data: user, message: "User fround" };
}
else
response = { success: false, message: "User not fround" };
return response;
}
and I pass this as a Parameter
const query = [
{
$match: {
name:"Jon"
}
},
{
$project:{
_id:1
}
}
]
const userRes = await getUser(query);
But I'm not able to run the program it's giving me syntax error on getUser function
*(method) Model<any, any, any>.aggregate(pipeline?: any[] | undefined): Aggregate<any[]> (+1 overload)
Argument of type 'Aggregate<any[]>' is not assignable to parameter of type 'Promise'.
Type 'Aggregate<any[]>' is missing the following properties from type 'Promise': [Symbol.toStringTag], finally*
I tried to change object to any, Array or Array in getUser parameter
here is the SS of the Error
PS: I'm using node with typescript and IDE is VSCode
Mongoose queries have their own types, and you should use those types to avoid such errors.
You can import those types for anything you need directly from Mongoose package.
I strongly recommend using the Typegoose package, which helps to create fully typed MongoDB schemas. and by that allow you to use those types as a response, find and update queries and much more!
reference:
https://typegoose.github.io/typegoose/docs/guides/quick-start-guide
Example for your usage with correct type:
import { FilterQuery } from 'mongoose';
public async getUser(aggregate: FilterQuery<ResponseDTO>): Promise<ResponseDTO> {
let response = {} as ResponseDTO;
const [err, user] = await To(User.aggregate(aggregate));
if (user) {
response = { success: true, data: user, message: "User fround" };
}
else
response = { success: false, message: "User not fround" };
return response;
}
Mongoose provides PipelineStage interface for pipes We can use that right away
import { PipelineStage } from "mongoose";
public async getUser(aggregate: PipelineStage[]):
Promise<ResponseDTO> {
let response = {} as ResponseDTO;
const [err, user] = await User.aggregate(aggregate);
if (user) {
response = { success: true, data: user, message: "User found" };
}
else
response = { success: false, message: "User not found" };
return response;
}

sequelize ORM asynchronous method calls

How can I call methods asynchronously in sequelize ORM? (because I have to use returned value inside other methods).
user.dao.js:
var User = require('./user.model');
class UserDao {
constructor() {}
insert(user) {
var pk;
User.sync({ force: false }).then(() => {
User.create(user).then(function(user) {
console.log('Entry successful from dao: ' +
JSON.stringify(user));
//return generated pk
pk = user.id;
console.log('ID: ' + pk);
});
});
return pk;
}
user.test.js:
class UserDaoTest {
constructor() {
this.userDao = new UserDao();
this.compare = new UtilsObject();
}
/*
all CRUD method calls
*/
testAll() {
this.testInsert();
this.testUpdate();
//this.testDelete();
//this.testRead();
//this.compare();
}
/*
insert method
*/
testInsert() {
// composite form
var user = {
name: 'nisha',
email: 'nisha#gmail.com',
phoneNo: 8978,
picUrl: 'nisha',
description: 'SI',
status: 'active',
waitingTime: 10,
rating: 7
};
/*
calling insert user with above data
*/
var pk = this.userDao.insert(user);
console.log('pk value: ' + pk);
//var obj1 = this.userDao.readById(pk);
console.log('obj1 value: ' + user);
//this.testReadById(obj1);
}
testReadById(obj1) {
var obj2 = this.userDao.readById(obj1);
this.compare.compare(obj1, obj2);
this.testDelete(obj1);
}
}
export default UserDaoTest;
Here in user.test.js, in testInsert() method want to get the value of pk which is returned from insert() method of user.dao.js, but right now I am getting pk value as undefined.
Use a promise chain.
Suppose you need to get an entry for a particular user & do some operations on it.
Model.User.findById(someId)
.then((user) => {
// Do something with user.
})
You shouldn't be calling methods synchronously, NodeJs is not designed this way. It works with callbacks or promises.
Your code won't work because it is async code.
Watch the famous Youtube video about the event loop
But in short, if you will run the following example, which is like your code but without your logic:
var User = require('./user.model');
class UserDao {
constructor() {}
insert(user) {
var pk;
console.log('1');
User.sync({ force: false }).then(() => {
pk = 123;
console.log('3');
});
console.log('2');
return pk;
}
The variable pk will be undefined and your console will look like this:
1
2
3
If you want it to work, you should "wait" for the async functions like this:
var User = require('./user.model');
class UserDao {
constructor() {}
// #return Promise
insert(user) {
return User.sync({ force: false }).then(() => {
return User.create(user)
}).then((user) => {
console.log('Entry successful from dao: ' + JSON.stringify(user));
return user.id
})
}
And when you use it:
class UserDaoTest {
constructor() {
this.userDao = new UserDao();
this.compare = new UtilsObject();
}
/*
all CRUD method calls
*/
testAll() {
// if testInsert and testUpdate can run simultaneously you can keep it like this.
// Otherwise, use Promise.then as well
this.testInsert();
this.testUpdate();
}
/*
insert method
*/
testInsert() {
var user = {
// ...
};
/*
calling insert user with above data
*/
this.userDao.insert(user).then((userId) => {
// YOUR COMPARE CODE
}).then(done); // Where done is a function to let you test framework that you async code is done
}
}
export default UserDaoTest;
Another way of doing that is using the new async and await. That way you will get a code which is more readable and maintainable.
You can read more here

Resources