I'm making REST APIS with Express.js
I have the following express route:
/api/customer
I added multiple query params to the route like this:
/api/customer?name=jake
/api/customer?country=america
/api/customer?name=jake&country=america
/api/customer?name=jake&limit=10
In my controllers I handle all of these with If and there are so many cases I feel that this method would not scale, is there a better way of handling this ?
This is the code for my controller, I'm using Sequelize to query the database:
async function getAllCustomer(queryLimit, page) {
const customers = await Customer.findAll({
limit: queryLimit ? parseInt(queryLimit) : null,
offset: page ? parseInt(queryLimit) * parseInt(page) : null
});
return customers;
}
async function getCustomerByFirstName(name, queryLimit, page) {
return await Customer.findAll({
where: {
firstName: name,
}
})
}
async function getCustomerByAddress(address) {
return await Customer.findAll({
where: {
customerAddress: address
}
})
}
async function getCustomerByNameAddress(name, address) {
return await Customer.findAll({
where: {
[Op.and]: [
{firstName: name},
{customerAddress: address}
]
}
})
}
async function getCustomer(req, res) {
const page = req.query.page;
const queryLimit = req.query.limit;
const name = req.query.name;
const address = req.query.address;
let customers;
/* User want to find first names */
if (name && !address) {
const names = name.split(",")
customers = await getCustomerByFirstName(names, queryLimit, page)
res.status(200).send(customers)
return;
}
/* User want to find addresses */
if (!name && address) {
const addresses = address.split(",")
customers = await getCustomerByAddress(addresses, queryLimit, page)
res.status(200).send(customers)
return;
}
/* User want to mix both */
if (name && address) {
const names = name.split(",")
const addresses = address.split(",")
customers = await getCustomerByNameAddress(names, addresses, queryLimit, page)
res.status(200).send(customers)
return;
}
if (!name && !address) {
customers = await getAllCustomer(queryLimit, page)
res.status(200).send(customers)
return;
}
}
You could do something like this:
async function getCustomer(req, res) {
const page = req.query.page;
const queryLimit = req.query.limit;
const name = req.query.name;
const address = req.query.address;
let query = { };
if(name) {
query.firstName = name;
}
if(address) {
query.address = address;
}
let customers = await getCustomers(query, queryLimit, page);
res.status(200).send(customers)
return;
}
async function getCustomers(query, queryLimit, page) {
const customers = await Customer.findAll({
where: query,
limit: queryLimit ? parseInt(queryLimit) : null,
offset: page ? parseInt(queryLimit) * parseInt(page) : null
});
return customers;
}
BTW, in your code, the functions getCustomerByFirstName, getCustomerByAddress and getCustomerByNameAddress are expecting to receive name and address as string parameter, but you are passing names and addresses array. This might lead to errors...
Related
im a total newbie in js (typescript, mongoDB, node.)
i just found that my code is not behaving as i expected, im getting 6 registers on the mongoDB instead of just one, it should check if the register exists and then update it, i dont know if it is something related to the await / async or i am doing something wrong, thanks in advace, here is my code.
fields.forEach((value) => {
try {
const mongoConnection = new DocumentDbRepository();
let checksIfExists = await mongoConnection.getValue(key, information[uniqueValue]);
if(checksIfExists==null){
let insert = await mongoConnection.insertValue(information);
console.log(insert);
}
if(checksIfExists?.passValue===information.passValue){
console.log('---------update---------');
let sons = Object.values(information.ticketToRide);
information.ticketToRide = sons;
let update = await mongoConnection.updateRegister(information, checksIfExists._id);
console.log(update);
} else {
console.log('---------insert---------');
let sons = Object.values(information.ticketToRide);
information = sons;
let insert = await mongoConnection.insertValue(information);
console.log(insert);
}
} catch (error) {
console.log(error)
}
}
async getValue(uniqueValue: any, keyValue:any) {
if (this._connection == null) {
await this.connect();
}
const db = this._connection.db(DocumentDbRepository.DbName);
const ticketToRide = db.collection("ticketToRide");
const query = {};
query[uniqueValue] = ''+keyValue+'';
const passInfo = await ticketToRide.findOne(query);
return passInfo;
}
async insertValue(information: any) {
if (this._connection == null) {
await this.connect();
}
const db = this._connection.db(DocumentDbRepository.DbName);
const ticketToRide = db.collection("ticketToRide");
let check = await ticketToRide.insertOne(
information
)
return check;
}
First, you don't need to create a connection inside the loop.
Second, mongodb has an update() or updateMany() method that has a special option { upsert: true }. If it is passed, insert will happen automatically.
Usage example:
Person.update( { name: 'Ted' }, { name: 'Ted', age : 50 }, { upsert: true })
So I'm sending data properly to mongo and data (user input information), which is correctly held in backend. In console I'm getting interceptor that tells me that data is received from Mongo DB, but how to properly get those properties (user's email, title of photo and url blob) or 'data'? So it can be seen as individual data (email, title...) and not as the whole object like it can be seen in console now.
--THIS IS IN MY VUE--
dohvatiObjavu(){
this.objava = Objave.dohvati_objavu();
console.log("Current post " + this.objava);
}
},
-- THIS IS IN SERVICES--
[let Objave = {
async dohvati_objavu() {
let response = await Service.get(/galerija)
let data = response.data;
console.log("Current posts in services: "+data.naslov)
return {
id: data._id,
email: data.email,
naslov: data.naslov,
noviOpisSlike: data.noviOpisSlike,
slika: data.slikaReference,
}
},
}
--THIS IS IN BACKEND--
app.get ('/galerija', async (req , res) => {
let db = await connect();
let cursor = await db.collection('galerija').find();
let results = await cursor.toArray();
res.json(results);
});
-- MY CONSOLE--
Objave.dohvati_objavu(); is an async function. So you should also await this inside your Vue method dohvatiObjavu().
I created a simplified working example, based on your code:
const Objave = {
dohvati_objavu: async function() {
// mock Service.get(/galerija) with Promise.resolve
const data = await Promise.resolve({
id: 'mockId',
email: 'mockEmail',
naslov: 'mockNaslov',
noviOpisSlike: 'mockNoviOpisSlike',
slika: 'mockSlika',
});
return {
id: data._id,
email: data.email,
naslov: data.naslov,
noviOpisSlike: data.noviOpisSlike,
slika: data.slikaReference
}
}
}
const MyVueComponent = class {
objava = undefined;
// DOES NOT WORK
dohvatiObjavu() {
this.objava = Objave.dohvati_objavu();
console.log("[dohvatiObjavu] Current post ", this.objava);
}
// WORKS
async dohvatiObjavu2() {
this.objava = await Objave.dohvati_objavu(); // <!-- await
console.log("[dohvatiObjavu2] Current post ", this.objava);
}
}
const component = new MyVueComponent()
component.dohvatiObjavu();
component.dohvatiObjavu2();
I'm stuck in async hell:
function convertToDomainUsers(dbUsers: Array<UserDB>): Array<UserDomain> {
// iterate each DB user and convert them to domain user types
const domainUsers: Array<UserDomain> = dbUsers.map( async (dbUser: UserDB) => {
// first convert the DB user to a Domain User
const domainUser: UserDomain = newUserDomainModel(dbUser);
// Now we need to get their user links from the DB
const dbUserLinks: Array<UserLinkDB> = await findDBUserLinks(dbUser.user_id);
// convert their user links to Domain user links
const domainUserLinks: Array<UserLinkDomain> = convertToUserLinks(dbUserLinks);
// now merry up the domain user links to the domain user
domainUser.links = domainUserLinks;
return domainUser;
});
return domainUsers;
}
function newUserDomainModel(user: UserDB): UserDomain {
const domainUser: UserDomain = {
username: user.user_username,
firstName: user.user_name_first,
lastName: user.user_name_last
};
return domainUser;
}
async function findDBUserLinks(userId: bigint): Promise<Array<UserLinkDB>> {
const dbUserLinks: Array<UserLinkDB> = await getUserLinks(userId);
return dbUserLinks;
}
async function getUserLinks(id: bigint): Promise<Array<UserLinkDB>> {
setDB();
await client.connect();
const query = `
select
link_url,
social_type_id
from user_links
WHERE user_id = ${id}`;
const res = await client.query(query);
const links: Array<UserLinkDB> = res.rows;
return Promise.resolve(links);
}
Error (happening on const domainUsers: in the convertToDomainUsers function):
TS2322: Type 'Promise<UserDomain>[]' is not assignable to type 'UserDomain[]'. Type 'Promise<UserDomain>' is missing the following properties from type 'UserDomain': username, firstName, lastName, fullName, and 6 more
comments were added for the sake of making this stack post easier to follow. I don't normally write comments, they're cruft.
Calling:
const domainUsers = await Promise.all(convertToDomainUsers(dbUsers));
Working implementation:
function convertToDomainUsers(dbUsers: Array<UserDB>): Array<Promise<UserDomain>> {
const domainUsers: Array<Promise<UserDomain>> = dbUsers.map( async (dbUser: UserDB) => {
const domainUser: UserDomain = newUserDomainModel(dbUser);
const dbUserLinks: Array<UserLinkDB> = await findDBUserLinks(dbUser.user_id);
const domainUserLinks: Array<UserLinkDomain> = convertToUserLinks(dbUserLinks);
domainUser.links = domainUserLinks;
return domainUser;
});
return domainUsers;
}
I have a app.get which inside of it is quite a bit of logic. Which everything works great aside from some of the logic being called twice for some reason. I have noticed when I was saving something to by db that it would save two rows.
So I put a console.log in that area and sure enough it was logging it twice.
Any reason why this is happening?
app.get('/shopify/callback', (req, res) => {
const { shop, hmac, code, state } = req.query;
const stateCookie = cookie.parse(req.headers.cookie).state;
if (state !== stateCookie) {
return res.status(403).send('Request origin cannot be verified');
}
if (shop && hmac && code) {
// DONE: Validate request is from Shopify
const map = Object.assign({}, req.query);
delete map['signature'];
delete map['hmac'];
const message = querystring.stringify(map);
const providedHmac = Buffer.from(hmac, 'utf-8');
const generatedHash = Buffer.from(
crypto
.createHmac('sha256', config.oauth.client_secret)
.update(message)
.digest('hex'),
'utf-8'
);
let hashEquals = false;
try {
hashEquals = crypto.timingSafeEqual(generatedHash, providedHmac)
} catch (e) {
hashEquals = false;
};
if (!hashEquals) {
return res.status(400).send('HMAC validation failed');
}
// DONE: Exchange temporary code for a permanent access token
const accessTokenRequestUrl = 'https://' + shop + '/admin/oauth/access_token';
const accessTokenPayload = {
client_id: config.oauth.api_key,
client_secret: config.oauth.client_secret,
code,
};
request.post(accessTokenRequestUrl, { json: accessTokenPayload })
.then((accessTokenResponse) => {
const accessToken = accessTokenResponse.access_token;
// DONE: Use access token to make API call to 'shop' endpoint
const shopRequestUrl = 'https://' + shop + '/admin/shop.json';
const shopRequestHeaders = {
'X-Shopify-Access-Token': accessToken,
}
request.get(shopRequestUrl, { headers: shopRequestHeaders })
.then((shopResponse) => {
const response = JSON.parse(shopResponse);
const shopData = response.shop;
console.log('BEING CALLED TWICE...')
res.render('pages/brand_signup',{
shop: shopData.name
})
})
.catch((error) => {
res.status(error.statusCode).send(error.error.error_description);
});
})
.catch((error) => {
res.status(error.statusCode).send(error.error.error_description);
});
} else {
res.status(400).send('Required parameters missing');
}
});
Using Express Node.JS along with Mongoose, and I'm trying to add pagination to my get request, however the order in which I'm getting messages is invalid. I get the correct messages with createdAt: -1/createdAt: 'desc', -createdAt, but in reverse order. (Changing to : 1 or 'asc' gives me the oldest messages, and thats not what I need)
const fetchMessages = async (req, res, chatId) => {
try {
const page = req.query.page || 0;
const limit = 25;
const take = req.query.take || limit;
const filter = { chatId: { $eq: chatId } };
let query = await Message.find(filter)
.skip(take * page)
.limit(200)
.sort('-createdAt');
// query = query.sort({ createdAt: 1 });
return res.status(200).json({ data: query });
} catch (e) {
console.log(e);
return res.status(500).json({ message: e });
}
};
Solved it..
const fetchMessages = async (req, res, chatId) => {
try {
// Required pagination param
const page = req.query.page || 0;
// Optional pagination params
const limit = req.query.limit || 25;
const take = req.query.take || limit;
const filter = { chatId: { $eq: chatId } };
let query = await Message.find(filter)
.sort('-createdAt')
.skip(take * page)
.limit(limit);
// Making newest messages come first
query = query.reverse();
return res.status(200).json({ data: query });
} catch (e) {
console.log(e);
return res.status(500).json({ message: e })