I have table of tickets and each ticket has a price. I have set the base in database as USD, let say someone wants to purchase a ticket from a country other than US, I want to show prices to users in their local currency. What would be the appropriate approach. First I though I could convert the prices of tickets to local currency at run time, but this operation would be costly as tickets can be large in numbers.
For currency conversion I am using Open Exchange Rates API, I need to provide prices based on the currency provided through req.query.
I cant't show the actual code here but I have a sample:
const express = require('express');
const Product = require('../models/Product');
const { fetchRates } = require('../services/fx_rates')
const router = new express.Router();
router.post('/products', async (req, res) => {
const product = new Product({
...req.body
});
try {
await product.save();
res.status(201).send(product);
} catch (e) {
res.send(400).send(e);
}
});
router.get('/products', async (req, res) => {
try {
const products = await Product.find()
// if (req.query.to) {
// for (let product of products) {
// fetchRates(product.price, 'USD', to)
// .then(price => {
// product.price = price
// })
// .catch(e => console.log(e))
// }
// }
console.log(products)
res.status(200).send({ products: products })
} catch (e) {
res.status(500).send();
}
});
module.exports = router
One approach can be to get the conversion rate with respect to USD for the required local currency and then convert the prices using the code instead of API. You can use the open exchange latest.json endpoint for it.
Here is an example.
const rp = require('request-promise');
async function getConversionRate(localCurrency, base = 'USD'){
const options = {
uri: 'https://openexchangerates.org/api/latest.json',
qs: {
app_id: 'YOUR_APP_ID',
base: base,
symbols: localCurrency,
},
json: true
};
const result = await rp(options);
/*
{
disclaimer: "https://openexchangerates.org/terms/",
license: "https://openexchangerates.org/license/",
timestamp: 1449877801,
base: "USD",
rates: {
INR: 71.43
}
}
*/
return result.rates[localCurrency];
}
router.get('/products', async (req, res) => {
try {
const [products, conversionRate] = await Promise.all([Product.find(), getConversionRate(req.query.to)])
const convertedProducts = products.map(p => {
p.price = p.price * conversionRate;
return p;
});
console.log(convertedProducts)
res.status(200).send({ products: convertedProducts })
} catch (e) {
res.status(500).send();
}
});
module.exports = router;
You can use nodejs-currency-converter npm.
Related
I'm currently working on a MERN application with following/follower function for the users. I decided to create separate schemas for following and follower relationships detached from user schema.
Follower schema
const mongoose = require('mongoose');
const findOrCreate = require('mongoose-findorcreate');
const ObjectId = mongoose.Schema.Types.ObjectId;
const followerSchema = mongoose.Schema({
userId: {
type: ObjectId,
ref: 'User'
},
follower: {
type: [ObjectId],
ref: 'User'
}
});
followerSchema.plugin(findOrCreate);
const Follower = mongoose.model('Follower', followerSchema);
module.exports = { followerSchema, Follower };
Following schema
const mongoose = require('mongoose');
const findOrCreate = require('mongoose-findorcreate');
const ObjectId = mongoose.Schema.Types.ObjectId;
const followingSchema = mongoose.Schema({
userId: {
type: ObjectId,
ref: 'User'
},
following: {
type: [ObjectId],
ref: 'User'
}
});
followingSchema.plugin(findOrCreate);
const Following = mongoose.model('Following', followingSchema);
module.exports = { followingSchema, Following };
The problem however starts in my service where followings are created as supposed to. However, for followers mongoose create 6-8 documents at once with userIds that don't even exist in my db.
Here's the code of the followerService (it's the first function)
const { Follower } = require('../models/followerModel');
const { Following } = require('../models/followingModel');
const { User } = require('../models/userModel');
const mongoose = require('mongoose');
exports.changeFollowStatus = async (user, userId) => {
console.log({ userId: userId, user: user._id });
const newFollowing = await Following.findOrCreate({ userId: user._id }, (err, following, created) => {
console.log({following: following});
if (!err && !isFollowing(following, userId)) {
following.following.push(mongoose.Types.ObjectId(userId));
following.save();
User.findByIdAndUpdate(mongoose.Types.ObjectId(userId), {$inc: {follower: 1}});
} else {
const followingIndex = following.following.indexOf(mongoose.Types.ObjectId(userId));
following.following.splice(followingIndex, 1);
following.save();
User.findByIdAndUpdate(mongoose.Types.ObjectId(userId), { $inc: { follower: -1 } });
}
});
const newFollower = await Follower.findOrCreate({ userId: mongoose.Types.ObjectId(userId) }, (err, follower, created) => {
console.log({follower: follower});
if (!err && !isFollower(follower, user._id)) {
follower.follower.push(user._id);
follower.save();
User.findByIdAndUpdate(user._id, { $inc: { following: 1 } });
} else {
const followerIndex = follower.follower.indexOf(user._id);
follower.follower.splice(followerIndex, 1);
follower.save();
User.findByIdAndUpdate(user._id, { $inc: { following: -1 } });
}
});
};
exports.showFollowings = async (userId) => {
const followings = await Following.findOrCreate({ userId: mongoose.Types.ObjectId(userId) });
return followings.following;
};
exports.showFollowers = async (userId) => {
const followers = await Follower.findOrCreate({ userId: mongoose.Types.ObjectId(userId) });
return followers.follower;
};
const isFollowing = (newFollowing, userId) => {
return newFollowing.following.includes(mongoose.Types.ObjectId(userId));
};
const isFollower = (newFollower, userId) => {
return newFollower.follower.includes(userId);
}
Now, my following adding code and follower adding code look almost identical, but for some reason for followers, mongoose creates many more documents. The strange thing is that there is a follower document with the correct userId, but many other with random userIds get created which doesn't happen with followings which works as supposed to.
I also checked whether I pass the correct values and everything seems to be fine. But just for reference, here's the controller function from which I pass the values.
exports.changeFollowingStatus = async (req, res, next) => {
passport.authenticate('jwt', async (err, user, info) => {
if (err) {
console.error({ authError: err });
};
if (info !== undefined) {
console.error({ authError: info.message });
res.status(403).send(info.message);
} else {
console.log({params: req.params});
const userToFollow = req.params.id;
await FollowerService.changeFollowStatus(user, userToFollow);
res.status(200).send({ message: 'success' })
}
})(req, res, next);
};
Could anyone help me with this bug or at least navigate me towards the right direction? I can't seem to find solution to my problem. Thank you all in advance!
index.js file
var users = [];
let addUser = (userId, socketId) => {
!users.some((user) => user.userId === userId) &&
users.push({ userId, socketId });
};
let removeUser = (socketId) => {
users = users.filter((item) => item.socketId !== socketId);
};
const getUser = (userId) => {
console.log("inside function", users);
return users.find((item) => item.userId === userId);
};
io.on("connection", (socket) => {
socket.on("addUser", async (userId) => {
await addUser(userId, socket.id);
io.emit("getUsers", users);
console.log(users) // print array of users like this
// [{userId:'userId',socketId: 'socket id'}]
});
socket.on("disconnect", () => {
removeUser(socket.id);
io.emit("getUsers", users);
});
});
const socketIoObject = io;
const usersInObject = users;
module.exports.ioObject = { socketIoObject, usersInObject };
controller file
exports.createNotifications = async (req, res) => {
try {
const { userId, title, type = "default", isPublic } = req.body;
if (!title) {
return res.status(401).send("Data is required");
}
const notification = await notificationsModel.create({
userId,
title,
type,
isPublic: userId ? false : true,
});
console.log("socket", socket.ioObject.usersInObject); // return empty
// array [] !!!!
return res.status(200).send("sent");
} catch (err) {
return res.status(400).send(err.message);
}
};
why I can't get the users list in the controller, I got an empty array !!
I need to share the users list in all files to can get the user by function getUser to get the socketId of a specific user to can send a notification to him
Maybe, you import socket in controller file incorrect
i have a table:
CREATE TABLE IF NOT EXISTS bands (
id serial PRIMARY KEY,
name VARCHAR UNIQUE NOT NULL,
creationDate DATE not NULL,
years DATE not NULL
);
I only want to pass name and creation date. what i want is that years will return currentdate - creationDate. The problem is that I do not really know where i should correctly change my code, because im using Node project.
My code looks like this:
const express = require("express");
const app = express();
const pool = require("./db");
app.use(express.json());
// Routes
app.post("/bands", async (req, res) => {
try {
const { name, creationDate } = req.body;
const newBand = await pool.query(
"INSERT INTO bands (name, creationDate,years) VALUES ($1, $2) RETURNING *",
[name, creationDate]
);
res.json(newBand);
} catch (err) {
console.error(err.message);
}
});
app.get("/bands", async (req, res) => {
try {
const allBands = await pool.query("SELECT * FROM bands");
res.json(allBands);
console.log(allBands);
} catch (err) {
console.error(err.message);
}
});
app.get("/bands/:bandsName", async (req, res) => {
console.log(req.params);
const { bandsName } = req.params;
try {
const todo = await pool.query("SELECT * FROM bands WHERE name = $1", [
bandsName,
]);
res.json(todo.rows[0]);
} catch (err) {
console.error(err.message);
}
});
app.put("/bands/:id", async (req, res) => {
try {
const { id } = req.params;
const { name, creationDate } = req.body;
const updateTodo = await pool.query(
"UPDATE band SET name = $1, creationDate = $2 WHERE id = $3",
[name, creationDate, id]
);
res.json("Udało się, zaaktualizowane");
} catch (err) {
console.error(err.message);
}
});
app.delete("/bands/:id", async (req, res) => {
try {
const { id } = req.params;
const deleteTodo = await pool.query("DELETE FROM bands WHERE id = $1", [
id,
]);
res.json("Usunięto");
} catch (err) {
console.error(err.message);
}
});
app.listen(3000, () => {
console.log("server is listening on port 3000");
});
Can anyone tell me where should i change my code so "years" will automatically calculate without me having to put the data in postman?
const { name, creationDate } = req.body;
const d = new Date(creationDate );
const year = d.getFullYear();
Create a date object and retrieve the year would do it
When i call Create Option api it is working fine but when i call list api get error: Cannot read property 'count' of undefined Express (Node + MongoDB) API.here is my Option Controller File code.
i have Log DB.ProductDoption ,getting result but count function not working.
const _ = require('lodash');
const Joi = require('joi');
exports.create = async (req, res, next) => {
try {
const validateSchema = Joi.object().keys({
name: Joi.string().required(),
key: Joi.string().required(),
description: Joi.string().allow(['', null]).optional(),
options: Joi.array().items(Joi.object().keys({
key: Joi.string().required(),
displayText: Joi.string().required()
})).required()
});
const validate = Joi.validate(req.body, validateSchema);
if (validate.error) {
return next(PopulateResponse.validationError(validate.error));
}
const key = Helper.String.createAlias(req.body.key);
console.log(DB.ProductDoption);
const count = await DB.ProductDoption.count({ key });
if (count || validate.value.key === '_custom') {
return next(PopulateResponse.error({
message: 'Please add unique name for key'
}));
}
const option = new DB.ProductDoption(validate.value);
await option.save();
res.locals.option = option;
return next();
} catch (e) {
return next(e);
}
};
exports.list = async (req, res, next) => {
const page = Math.max(0, req.query.page - 1) || 0; // using a zero-based page index for use with skip()
const take = parseInt(req.query.take, 10) || 10;
try {
const query = Helper.App.populateDbQuery(req.query, {
text: ['name', 'key', 'description']
});
const sort = Helper.App.populateDBSort(req.query);
const count = await DB.ProductDoption.count(query);
const items = await DB.ProductDoption.find(query)
.collation({ locale: 'en' })
.sort(sort).skip(page * take)
.limit(take)
.exec();
res.locals.optionList = {
count,
items
};
next();
} catch (e) {
next(e);
}
};
collection.count is deprecated, and will be removed in a future version. Use Collection.countDocuments or Collection.estimatedDocumentCount instead
FIXED: USER storageEngine: "wiredTiger"
I use Mocha / Chai / Supertest and Mongodb-Memory-Server to test my app. But's I received error: Transaction numbers are only allowed on storage engines that support document-level locking
In real database and test by postman, it's working well.
My code:
In database.js
const mongoose = require('mongoose')
const { MongoMemoryReplSet } = require('mongodb-memory-server')
mongoose.set('useFindAndModify', false);
const connect = async () => {
try {
let url = process.env.MONGO_URL
let options = {
//Something
}
if (process.env.NODE_ENV === 'test') {
const replSet = new MongoMemoryReplSet();
await replSet.waitUntilRunning();
const uri = await replSet.getUri();
await mongoose.connect(uri, options)
//log connected
} else {
await mongoose.connect(url, options)
//log connected
}
} catch (error) {
//error
}
}
I have two model: Company and User. I made a function to add a member to company with used transaction. My code
const addMember = async (req, res, next) => {
const { companyId } = req.params
const { userId } = req.body
const session = await mongoose.startSession()
try {
await session.withTransaction(async () => {
const [company, user] = await Promise.all([
Company.findOneAndUpdate(
//Something
).session(session),
User.findByIdAndUpdate(
//Something
).session(session)
])
//Something if... else
return res.json({
message: `Add member successfully!`,
})
})
} catch (error) {
//error
}
}
Here's router:
router.post('/:companyId/add-member',
authentication.required,
company.addMember
)
Test file:
const expect = require('chai').expect
const request = require('supertest')
const app = require('../app')
describe('POST /company/:companyId/add-member', () => {
it('OK, add member', done => {
request(app).post(`/company/${companyIdEdited}/add-member`)
.set({ "x-access-token": signedUserTokenKey })
.send({userId: memberId})
.then(res => {
console.log(res.body)
expect(res.statusCode).to.equals(200)
done()
})
.catch((error) => done(error))
})
})
And i received error: Transaction numbers are only allowed on storage engines that support document-level locking'
How can I fix this?
Add retryWrites=false to your database uri. Example below:
mongodb://xx:xx#xyz.com:PORT,zz.com:33427/database-name?replicaSet=rs-xx&ssl=true&retryWrites=false