Prisma: how to write transaction where results from one query are used by another query - postgresql

I'm working on a project with Next.js and Prisma. In one of my API routes, I have a three queries. The results of the first and second queries are used in the third query. I'd like to do all three operations as a transaction and then return the data from the first query in the response.
I'm familiar with using prisma.$transaction but I don't know how to write it in this case where results #1 and #2 are used by query #3. Here are the queries as they are written now. Thanks in advance!
const { boardId } = req.body
const { description, status, title } = req.body.task
const createTask = await prisma.task.create({
data: {
board: boardId,
description,
status,
title
}
})
const statusArray = await prisma.board.findUnique({
where: {
id: boardId
},
select: {
[status]: true
}
})
const updateBoardStatusArray = await prisma.board.update({
where: {
id: boardId
},
data: {
[status]: {
set: [...statusArray[status], createTask.id]
}
}
})
// return data from first query
res.status(201).json({task: createTask})

Here you go:
const { boardId } = req.body;
const { description, status, title } = req.body.task;
const [createTask] = await prisma.$transaction(async (prisma) => {
const createTask = await prisma.task.create({
data: {
board: boardId,
description,
status,
title,
},
});
const statusArray = await prisma.board.findUnique({
where: {
id: boardId,
},
select: {
[status]: true,
},
});
const updateBoardStatusArray = await prisma.board.update({
where: {
id: boardId,
},
data: {
[status]: {
set: [...statusArray[status], createTask.id],
},
},
});
return [createTask, statusArray, updateBoardStatusArray];
});
// return data from first query
res.status(201).json({ task: createTask });
You can learn more about Interactive Transaction here

Related

How find in mongoose by an array property

I have defined my Conversation scheme like this:
const { Schema, model } = require("mongoose");
const ConversationSchema = Schema(
{
members: {
type: Array,
},
},
{ timestamps: true }
);
module.exports = model("Conversation", ConversationSchema);
My problem is that when I want to create a conversation model I search first if there is already a conversation.
const newConversation = async (req, res = response) => {
try {
const { senderId, receiverId } = req.body;
const conversation = await Conversation.find({
members: { $in: [senderId, receiverId] },
});
if (conversation.length === 0) {
const dbConversation = new Conversation({
members: [senderId, receiverId],
});
await dbConversation.save();
return res.status(201).json({
ok: true,
conversation: dbConversation
});
} else {
return res.status(403).json({
ok: false,
msg: "Conversation already exist",
});
}
} catch (err) {
return res.status(500).json({
ok: false,
msg: "Please contact with administrator",
});
}
};
senderId and receivedId are the ids of the users that are in that conversation, but it doesn't work.
How can I make it check if there is already a conversation with both ids?
Per the comments, we came to understand that the thing that wasn't working about the current code was always taking the code path that returned the message that the "Conversation already exist". This meant that the following query was always returning data:
const conversation = await Conversation.find({
members: { $in: [senderId, receiverId] },
});
The logic here does not match the logic implied in the question. This syntax uses the $in operator to find documents whose members array has at least one of the values passed to it (here the senderId and the receiverId).
To instead find documents where both of those people are present in the members array, you want to use the $all operator instead:
const conversation = await Conversation.find({
members: { $all: [senderId, receiverId] },
});
Working Mongo Playground example here.

How to create dynamic query in mongoose for update. i want to update multiple data(Not all) with the help of Id

If I'm doing this, the field which I don't want to update is showing undefined. Any solution? (Like generating dynamic query or something)
exports.updateStudentById = async (req, res) => {
try {
const updateAllField = {
first_name: req.body.first_name,
last_name: req.body.last_name,
field_of_study: req.body.field_of_study,
age: req.body.age,
};
const data = await student_master.updateOne(
{ _id: req.body._id },
{ $set: updateAllField }
);
res.json({ message: "Student Data Updated", data: data });
} catch (error) {
throw new Error(error);
}
};
You can go for a dynamic query creation .Example
const requestBody = {
first_name: "John",
last_name: "Cena",
field_of_study: ""
}
const query={};
if(requestBody.first_name){
query["first_name"]=requestBody.first_name
}
if(requestBody.last_name){
query["last_name"]=requestBody.last_name
}
Check for the fields that are present in req.body and create a dynamic query
and when updating using mongoose use this
const data = await student_master.updateOne(
{ _id: req.body._id },
{ $set: query }
);
In this way only those fields would be updated which are present in your req.body

Adding multiple items to mongodb

I have this schema with
const userSchema = new mongoose.Schema(
{
skills: [{ name: { type: String, unique: true }, level: { type: Number } }],
and I am trying, after getting an array of objects from the client, to add all of them at once under a user in MongoDB
my old implementation when it was only an array is this one below. I have no idea how to go about it now tho. Could anyone help me?
const { email } = session;
const { skill } = req.body;
if (req.method === 'POST') {
try {
const user = await User.findOne({ email });
const updatedUser = await User.findOneAndUpdate(
{ email },
{ skills: [...user.skills, { name: skill }] }
);
const { email } = session;
// object from the client
const { skills } = req.body;
if (req.method === 'POST') {
try {
const updatedUser = await User.findOneAndUpdate(
{ email },
{ $set: {skills} }
);
the best is getting the array correctly formatted from front-end, if not use a map before call findOneAndUpdate

How to implement a PUT request in Vue 3

I am trying to implement a PUT request to the https://crudcrud.com/ REST API.
I have a list of users and when I click an update button, I would like to show a modal and allow the user to update any of the fields (name, email, image URL). The main concern is that I am struggling with how to format the PUT request.
This is my current solution
// template (UserCrud.vue)
<button #click="update(user._id)">Update</button>
// script
components: { Create },
setup() {
const state = reactive({
users: [],
})
onMounted(async () => {
const { data } = await axios.get(`/users`)
state.users = data
})
async function update(id) {
await axios.put(`/users/${id}`)
state.users = ???
}
return { state, destroy, addUser }
Here is some sample data:
[
{
"_id": "6012303e37711c03e87363b7",
"name": "Tyler Morales",
"email": "moratyle#gmail.com",
"avatar": "HTTP://linkURL.com
},
]
For reference, this is how I create a new user using the POST method:
export default {
components: { Modal },
emits: ['new-user-added'],
setup(_, { emit }) {
const isModalOpen = ref(false)
const state = reactive({
form: {
name: '',
email: '',
avatar: '',
},
})
async function submit() {
const { data } = await axios.post('/users', state.form)
emit('new-user-added', data)
state.form.email = ''
state.form.name = ''
state.form.avatar = ''
isModalOpen.value = false
}
return { isModalOpen, submit, state }
},
}
Check this repo for the complete repo: the files are UserCrud.vue & Create.vue
You should pass the user object as parameter then send it as body for the put request by setting the id as param :
<button #click="update(user)">Update</button>
...
async function update(user) {
let _user={...user,name:'Malik'};//example
await axios.put(`/users/${user._id}`,_user);
const { data } = await axios.get(`/users`)
state.users = data
}
You could use the same code of adding new user for the update by defining a property called editMode which has true in update mode and based on this property you could perform the right request
export default {
components: { Modal },
emits: ['new-user-added','user-edited'],
props:['editMode','user'],
setup(props, { emit }) {
const isModalOpen = ref(false)
const state = reactive({
form: {
name: '',
email: '',
avatar: '',
},
})
onMounted(()=>{
state.form=props.user;//user to edit
})
async function submit() {
if(props.editMode){
const { data } = await axios.put('/users/'+props.user._id, state.form)
emit('user-edited', data)
}else{
const { data } = await axios.post('/users', state.form)
emit('new-user-added', data)
state.form.email = ''
state.form.name = ''
state.form.avatar = ''
}
isModalOpen.value = false
}
return { isModalOpen, submit, state }
},
}

MongoDB + Express How to Optimize the code?

Hello everybody i have this bad code for me how i can optimize it ?
If i used SQL i can do used inner Queries in one query...
"User" it's only object from mongoose
getProfile: async (req, res) => {
const { id } = req.params;
try {
const {
image,
name,
gender,
about,
email,
phone,
address
} = await User.findById({ _id: id }).select('image name gender about email phone address');
const subscriptions = await Subscriber.countDocuments({ userId: id });
const subscribers = await Subscriber.countDocuments({ subscriberId: id });
const user = {
image,
name,
gender,
subscriptions,
subscribers,
about,
email,
phone,
address
};
res.json(user);
} catch (err) {
console.log(err);
}
}
PS.
I only study with this technologies
If i used spread operator of result of my query from User i have like this:
And that what i have in result
module.exports = {
getProfile: async (req, res) => {
const { id } = req.params;
try {
const [data, subscriptions, subscribers] = await Promise.all([
User.findById( { _id: id },
{
__v: false,
password: false,
date: false,
_id: false
},
),
Subscriber.countDocuments({ userId: id }),
Subscriber.countDocuments({ subscriberId: id })
])
const user = {
...data._doc,
subscriptions,
subscribers
}
res.json(user);
} catch (err) {
console.log(err);
}
}
Since all of your queries are independent, the best we can do is execute all of them parallelly with Promise.all(). Try something like this:
getProfile: async (req, res) => {
const { id = _id } = req.params;
try {
const getUser = User.findById({ _id }).select('image name gender about email phone address');
const getSubscriptions = Subscriber.countDocuments({ userId: id });
const getSubscriber = Subscriber.countDocuments({ subscriberId: id });
const [userData, subscriptions, subscribers] = await Promise.all([getUser, getSubscriptions, getSubscriber]);
const user = {
...userData,
subscriptions,
subscribers,
};
res.json(user);
} catch (err) {
console.log(err);
}
}
Hope this helps :)
You can embed subscriptions [array of documents] in the User model. But bear in mind that could put limitations on your api, if subscriptions might be accessed regardless of its user.