Here's my Mongoose Schema for the three collections users, accounts and transactions respectively:
const userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
unique: true,
trim: true,
lowercase: true,
minLength: 3,
maxLength: 30
},
password: {
type: String,
required: true,
minLength: 6
},
firstName: {
type: String,
required: true,
trim: true,
maxLength: 50
},
lastName: {
type: String,
required: true,
trim: true,
maxLength: 50
}
});
const accountSchema = new mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
balance: {
type: Number,
minLength: 0,
required: true
}
})
const transactionSchema = new mongoose.Schema({
sender: {
_id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true
},
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
}
},
receiver: {
_id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true
},
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
}
},
amount: {
type: Number,
required: true
}
});
The transactionsSchema was defined as such with the hope that mongoose will figure out, due to the _id's ref: "User" property (don't judge me on this one, chatGPT advised me so. And that, you can judge me on), to automatically populate the firstName and lastName fields for the sender and receiver.
The transactionsSchema, as it is defined currently, does not allow the following money transfer logic to happen:
accountRouter.post("/transfer", authMiddleware, async (req, res) => {
const session = await mongoose.startSession();
session.startTransaction();
const { amount, to } = req.body;
//...some logic that's not relevant to this question
//...
// Perform the transfer
await AccountModel.updateOne({ user: req.body.userId }, { $inc: { balance: -amount } }).session(session);
await AccountModel.updateOne({ user: to }, { $inc: { balance: amount } }).session(session);
await TransactionModel.create({ sender: {_id: req.body.userId}, receiver: {_id: to}, amount: amount });
// Commit the transaction
await session.commitTransaction();
res.json({
message: "Transfer successful"
});
});
It throws an error that firstName and lastName are required (as is specified in the schema, i guess). I tried removing the required property and in that case, the transaction works...but no firstName or lastName for the sender/receiver exist in the newly created record.
so yeah, the question is: how do I only give the _ids of the sender and receiver in the request, and have the _id, firstName, lastName all be saved in the record created in the transactions collection?
purpose: I want to create a page where the user can view their recent transactions, and so it'll be good to have the names in the transactions collection itself, otherwise i'll have to make a separate DB call to fetch that.
Thank you so much for reaching this point. Peace be with you.
You can modify your your transactionSchema
so that sender
and receiver
are just keys whose value is an ObjectId
that corresponds to a valid User._id
.
const transactionSchema = new mongoose.Schema({
sender: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true
},
receiver: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true
},
amount: {
type: Number,
required: true
}
});
Now when you create the Transaction
document you can simply do:
await TransactionModel.create({
sender: req.body.userId,
receiver: to,
amount: amount
});
Whenever you make a query on the transactions
collection and you want to get the details of each of those sender
and receiver
documents you can use populate
like so:
const transaction = await TransactionModel.find()
.populate('sender')
.populate('receiver');
And if you only want the firstName
and lastName
you can do:
const transaction = await TransactionModel.find()
.populate({path: 'sender', select: 'firstName lastName'})
.populate({path: 'receiver', select: 'firstName lastName'});