Accessing/changing another entity when performing a db action

I'm new to NodeJS, prisma and the likes and am having some trouble implementing an action. When I create a Student, they need to get a number assigned to them. If there are already 10 students in the class, the next created student should get number 11. I'm not sure how to best do this. This us my current code:
export const createStudent: CreateStudent<CreateStudentPayload, Student> = async (args, context) => {
if (!context.user) {
throw new HttpError(401);
}

return context.entities.Student.create({
data: {
name: args.name,
number: 11,
class: { connect: { teacherId: context.user.id }}
}
})
}
export const createStudent: CreateStudent<CreateStudentPayload, Student> = async (args, context) => {
if (!context.user) {
throw new HttpError(401);
}

return context.entities.Student.create({
data: {
name: args.name,
number: 11,
class: { connect: { teacherId: context.user.id }}
}
})
}
I tried calling the getClass query from the createStudent action and passing the cotext along, but it is not of the correct type. Should I send along the classId from the frontend, even though the frontend does not need to know about it? Should I just use the prismaClient and the full context over the wasp context?
17 Replies
kapa.ai
kapa.ai2mo ago
Hi @procrastinator_bas. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- To assign a unique number to each student, you can first fetch the current number of students in the class and then assign the next number to the new student. You can use the context.entities.Student.count() function to get the current number of students. Here is how you can modify your createStudent action:
export const createStudent: CreateStudent<CreateStudentPayload, Student> = async (args, context) => {
if (!context.user) {
throw new HttpError(401);
}

const currentStudentCount = await context.entities.Student.count({
where: { class: { teacherId: context.user.id }}
});

return context.entities.Student.create({
data: {
name: args.name,
number: currentStudentCount + 1,
class: { connect: { teacherId: context.user.id }}
}
})
}
export const createStudent: CreateStudent<CreateStudentPayload, Student> = async (args, context) => {
if (!context.user) {
throw new HttpError(401);
}

const currentStudentCount = await context.entities.Student.count({
where: { class: { teacherId: context.user.id }}
});

return context.entities.Student.create({
data: {
name: args.name,
number: currentStudentCount + 1,
class: { connect: { teacherId: context.user.id }}
}
})
}
In this code, context.entities.Student.count() is used to count the current number of students in the class. The where clause is used to filter the students by the class. Then, the new student is assigned the number currentStudentCount + 1. Regarding your question about whether to send the classId from the frontend, it depends on your application's requirements. If the frontend does not need to know about the classId, and the classId can be determined from the context.user.id (as in your current code), then there is no need to send it from the frontend. As for using the Prisma Client over the Wasp context, the Wasp context is designed to provide a simplified interface for common database operations. However, if you need more control or need to use a feature that Wasp doesn't provide, you can directly use the Prisma Client in your Wasp server code (source (https://wasp-lang.dev/docs/data-model/entities#working-with-entities)). ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @procrastinator_bas, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
procrastinator_bas
Good bot. 🙂
Filip
Filip2mo ago
Hey @procrastinator_bas, the bot's answer makes sense to me! Did it work?
procrastinator_bas
It is a working solution to my question, but after analyzing further the requirements have changed (don't they always?). If I have a class of 10 students, delete student 3 and add a new one, that student will get a number that is already assigned. Numbers do not need to be re-used after deleting a student and a number opens up.. So I do believe I need to store the latest assigned number in the class table and get that number when adding a new student. @kapa.ai , do you want to give it another try with the new requirements?
kapa.ai
kapa.ai2mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- Sure, with the updated requirements, you can indeed store the latest assigned number in the class table. When a new student is added, you can increment this number and assign it to the new student. Here is how you can modify your createStudent action:
export const createStudent: CreateStudent<CreateStudentPayload, Student> = async (args, context) => {{
if (!context.user) {{
throw new HttpError(401);
}}

// Fetch the class with the teacherId from the context
const classEntity = await context.entities.Class.findUnique({{
where: {{ teacherId: context.user.id }}
}});

if (!classEntity) {{
throw new Error('Class not found');
}}

// Increment the latest assigned number in the class
const newStudentNumber = classEntity.latestAssignedNumber + 1;

// Update the class with the new latest assigned number
await context.entities.Class.update({{
where: {{ teacherId: context.user.id }},
data: {{ latestAssignedNumber: newStudentNumber }}
}});

// Create the new student with the new number
return context.entities.Student.create({{
data: {{
name: args.name,
number: newStudentNumber,
class: {{ connect: {{ teacherId: context.user.id }}
}}
}})
}}
export const createStudent: CreateStudent<CreateStudentPayload, Student> = async (args, context) => {{
if (!context.user) {{
throw new HttpError(401);
}}

// Fetch the class with the teacherId from the context
const classEntity = await context.entities.Class.findUnique({{
where: {{ teacherId: context.user.id }}
}});

if (!classEntity) {{
throw new Error('Class not found');
}}

// Increment the latest assigned number in the class
const newStudentNumber = classEntity.latestAssignedNumber + 1;

// Update the class with the new latest assigned number
await context.entities.Class.update({{
where: {{ teacherId: context.user.id }},
data: {{ latestAssignedNumber: newStudentNumber }}
}});

// Create the new student with the new number
return context.entities.Student.create({{
data: {{
name: args.name,
number: newStudentNumber,
class: {{ connect: {{ teacherId: context.user.id }}
}}
}})
}}
In this code, context.entities.Class.findUnique() is used to fetch the class with the teacherId from the context. Then, the latestAssignedNumber in the class is incremented and updated using context.entities.Class.update(). Finally, the new student is created with the new number using context.entities.Student.create(). Please note that this code assumes that you have a Class entity in your data model with a latestAssignedNumber field. If your data model is different, you may need to adjust the code accordingly. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @procrastinator_bas, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
procrastinator_bas
@kapa.ai Is this all in 1 transaction? If any part of these database calls fail, I would want all of it to roll back, so I don't skip numbers unnecessarily.
kapa.ai
kapa.ai2mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- Yes, you can use Prisma's transaction API to ensure that all database operations are performed in a single transaction. If any part of the transaction fails, all changes made within the transaction are rolled back. Here is how you can modify your createStudent action to use a transaction:
export const createStudent: CreateStudent<CreateStudentPayload, Student> = async (args, context) => {{
if (!context.user) {{
throw new HttpError(401);
}}

// Fetch the class with the teacherId from the context
const classEntity = await context.entities.Class.findUnique({{
where: {{ teacherId: context.user.id }}
}});

if (!classEntity) {{
throw new Error('Class not found');
}}

// Increment the latest assigned number in the class
const newStudentNumber = classEntity.latestAssignedNumber + 1;

// Perform the update and create operations in a transaction
return context.entities.$transaction([
context.entities.Class.update({{
where: {{ teacherId: context.user.id }},
data: {{ latestAssignedNumber: newStudentNumber }}
}}),
context.entities.Student.create({{
data: {{
name: args.name,
number: newStudentNumber,
class: {{ connect: {{ teacherId: context.user.id }}
}}
}})
]);
}}
export const createStudent: CreateStudent<CreateStudentPayload, Student> = async (args, context) => {{
if (!context.user) {{
throw new HttpError(401);
}}

// Fetch the class with the teacherId from the context
const classEntity = await context.entities.Class.findUnique({{
where: {{ teacherId: context.user.id }}
}});

if (!classEntity) {{
throw new Error('Class not found');
}}

// Increment the latest assigned number in the class
const newStudentNumber = classEntity.latestAssignedNumber + 1;

// Perform the update and create operations in a transaction
return context.entities.$transaction([
context.entities.Class.update({{
where: {{ teacherId: context.user.id }},
data: {{ latestAssignedNumber: newStudentNumber }}
}}),
context.entities.Student.create({{
data: {{
name: args.name,
number: newStudentNumber,
class: {{ connect: {{ teacherId: context.user.id }}
}}
}})
]);
}}
In this code, context.entities.$transaction() is used to perform the update and create operations in a single transaction. The operations are passed as an array to $transaction(). If any operation fails, all changes made within the transaction are rolled back. Please note that this code assumes that you have a Class entity in your data model with a latestAssignedNumber field. If your data model is different, you may need to adjust the code accordingly. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @procrastinator_bas, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
procrastinator_bas
@kapa.ai My IDE says context.entities.$transaction does not exist. Is that syntax correct? Or should I directly use prisma? If so, how do I import it?
kapa.ai
kapa.ai2mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- I apologize for the confusion. The context.entities object does not have a $transaction method. You should use the Prisma Client directly for transactions. You can import it using import prisma from '@wasp/dbClient'. Here is how you can modify your createStudent action to use a transaction with the Prisma Client:
import prisma from '@wasp/dbClient'

export const createStudent: CreateStudent<CreateStudentPayload, Student> = async (args, context) => {{
if (!context.user) {{
throw new HttpError(401);
}}

// Fetch the class with the teacherId from the context
const classEntity = await context.entities.Class.findUnique({{
where: {{ teacherId: context.user.id }}
}});

if (!classEntity) {{
throw new Error('Class not found');
}}

// Increment the latest assigned number in the class
const newStudentNumber = classEntity.latestAssignedNumber + 1;

// Perform the update and create operations in a transaction
return prisma.$transaction([
context.entities.Class.update({{
where: {{ teacherId: context.user.id }},
data: {{ latestAssignedNumber: newStudentNumber }}
}}),
context.entities.Student.create({{
data: {{
name: args.name,
number: newStudentNumber,
class: {{ connect: {{ teacherId: context.user.id }}
}}
}})
]);
}}
import prisma from '@wasp/dbClient'

export const createStudent: CreateStudent<CreateStudentPayload, Student> = async (args, context) => {{
if (!context.user) {{
throw new HttpError(401);
}}

// Fetch the class with the teacherId from the context
const classEntity = await context.entities.Class.findUnique({{
where: {{ teacherId: context.user.id }}
}});

if (!classEntity) {{
throw new Error('Class not found');
}}

// Increment the latest assigned number in the class
const newStudentNumber = classEntity.latestAssignedNumber + 1;

// Perform the update and create operations in a transaction
return prisma.$transaction([
context.entities.Class.update({{
where: {{ teacherId: context.user.id }},
data: {{ latestAssignedNumber: newStudentNumber }}
}}),
context.entities.Student.create({{
data: {{
name: args.name,
number: newStudentNumber,
class: {{ connect: {{ teacherId: context.user.id }}
}}
}})
]);
}}
In this code, prisma.$transaction() is used to perform the update and create operations in a single transaction. The operations are passed as an array to $transaction(). If any operation fails, all changes made within the transaction are rolled back. Please note that this code assumes that you have a Class entity in your data model with a latestAssignedNumber field. If your data model is different, you may need to adjust the code accordingly. You can find more information about using the Prisma Client for transactions in the Wasp documentation (https://wasp-lang.dev/docs/data-model/entities#using-entities-directly). ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @procrastinator_bas, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
procrastinator_bas
@kapa.ai Is import prisma from '@wasp/dbClient' correct? I get this error: Cannot find module '@wasp/dbClient' or its corresponding type declarations. Where should prisma be located? I'm using wasp 0.14.
kapa.ai
kapa.ai2mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- I apologize for the confusion. The correct way to import Prisma Client in Wasp is import { PrismaClient } from '@prisma/client'. The '@wasp/dbClient' module does not exist. Here is how you can modify your createStudent action to use a transaction with the Prisma Client:
import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

export const createStudent: CreateStudent<CreateStudentPayload, Student> = async (args, context) => {{
if (!context.user) {{
throw new HttpError(401);
}}

// Fetch the class with the teacherId from the context
const classEntity = await context.entities.Class.findUnique({{
where: {{ teacherId: context.user.id }}
}});

if (!classEntity) {{
throw new Error('Class not found');
}}

// Increment the latest assigned number in the class
const newStudentNumber = classEntity.latestAssignedNumber + 1;

// Perform the update and create operations in a transaction
return prisma.$transaction([
context.entities.Class.update({{
where: {{ teacherId: context.user.id }},
data: {{ latestAssignedNumber: newStudentNumber }}
}}),
context.entities.Student.create({{
data: {{
name: args.name,
number: newStudentNumber,
class: {{ connect: {{ teacherId: context.user.id }}
}}
}})
]);
}}
import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

export const createStudent: CreateStudent<CreateStudentPayload, Student> = async (args, context) => {{
if (!context.user) {{
throw new HttpError(401);
}}

// Fetch the class with the teacherId from the context
const classEntity = await context.entities.Class.findUnique({{
where: {{ teacherId: context.user.id }}
}});

if (!classEntity) {{
throw new Error('Class not found');
}}

// Increment the latest assigned number in the class
const newStudentNumber = classEntity.latestAssignedNumber + 1;

// Perform the update and create operations in a transaction
return prisma.$transaction([
context.entities.Class.update({{
where: {{ teacherId: context.user.id }},
data: {{ latestAssignedNumber: newStudentNumber }}
}}),
context.entities.Student.create({{
data: {{
name: args.name,
number: newStudentNumber,
class: {{ connect: {{ teacherId: context.user.id }}
}}
}})
]);
}}
In this code, prisma.$transaction() is used to perform the update and create operations in a single transaction. The operations are passed as an array to $transaction(). If any operation fails, all changes made within the transaction are rolled back. Please note that this code assumes that you have a Class entity in your data model with a latestAssignedNumber field. If your data model is different, you may need to adjust the code accordingly. You can find more information about using the Prisma Client for transactions in the Prisma documentation (https://www.prisma.io/docs/concepts/components/prisma-client/transactions). ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @procrastinator_bas, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
procrastinator_bas
@kapa.ai , Almost correct. 🙂 I can't return the prsima.transaction, it does not fit the correct return type. This altered code does work:
const [updatedClass, newStudent] = await prisma.$transaction([
context.entities.Class.update({
where: { teacherId: context.user.id },
data: { lastAssignedNumber: newStudentNumber }
}),
context.entities.Student.create({
data: {
name: args.name,
number: newStudentNumber,
class: { connect: { teacherId: context.user.id }}
}
})
]);

return newStudent;
const [updatedClass, newStudent] = await prisma.$transaction([
context.entities.Class.update({
where: { teacherId: context.user.id },
data: { lastAssignedNumber: newStudentNumber }
}),
context.entities.Student.create({
data: {
name: args.name,
number: newStudentNumber,
class: { connect: { teacherId: context.user.id }}
}
})
]);

return newStudent;
kapa.ai
kapa.ai2mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- I'm glad you found a solution that works for you! Your updated code correctly uses Prisma's transaction API to ensure that the class update and student creation are performed in a single transaction. If any part of the transaction fails, all changes made within the transaction are rolled back. Here is your updated createStudent action:
import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

export const createStudent: CreateStudent<CreateStudentPayload, Student> = async (args, context) => {
if (!context.user) {
throw new HttpError(401);
}

// Fetch the class with the teacherId from the context
const classEntity = await context.entities.Class.findUnique({
where: { teacherId: context.user.id }
});

if (!classEntity) {
throw new Error('Class not found');
}

// Increment the latest assigned number in the class
const newStudentNumber = classEntity.latestAssignedNumber + 1;

// Perform the update and create operations in a transaction
const [updatedClass, newStudent] = await prisma.$transaction([
context.entities.Class.update({
where: { teacherId: context.user.id },
data: { lastAssignedNumber: newStudentNumber }
}),
context.entities.Student.create({
data: {
name: args.name,
number: newStudentNumber,
class: { connect: { teacherId: context.user.id }}
}
})
]);

return newStudent;
}
import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

export const createStudent: CreateStudent<CreateStudentPayload, Student> = async (args, context) => {
if (!context.user) {
throw new HttpError(401);
}

// Fetch the class with the teacherId from the context
const classEntity = await context.entities.Class.findUnique({
where: { teacherId: context.user.id }
});

if (!classEntity) {
throw new Error('Class not found');
}

// Increment the latest assigned number in the class
const newStudentNumber = classEntity.latestAssignedNumber + 1;

// Perform the update and create operations in a transaction
const [updatedClass, newStudent] = await prisma.$transaction([
context.entities.Class.update({
where: { teacherId: context.user.id },
data: { lastAssignedNumber: newStudentNumber }
}),
context.entities.Student.create({
data: {
name: args.name,
number: newStudentNumber,
class: { connect: { teacherId: context.user.id }}
}
})
]);

return newStudent;
}
In this code, prisma.$transaction() is used to perform the update and create operations in a single transaction. The operations are passed as an array to $transaction(). If any operation fails, all changes made within the transaction are rolled back. The new student is then returned from the action. Please note that this code assumes that you have a Class entity in your data model with a lastAssignedNumber field. If your data model is different, you may need to adjust the code accordingly. You can find more information about using the Prisma Client for transactions in the Prisma documentation (https://www.prisma.io/docs/concepts/components/prisma-client/transactions). ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @procrastinator_bas, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
Filip
Filip2mo ago
Ok, @procrastinator_bas, I see you're working with the bot. If you end up needing me me, tag me 😄
procrastinator_bas
Haha, thanks. I was able to resolve it. I gave the bot the final correct code, but I'm not sure it's gonna do something with it 😉 This issue is resolved. 👍
MEE6
MEE62mo ago
Wohooo @procrastinator_bas, you just became a Waspeteer level 2!
Filip
Filip2mo ago
Haha, yeah, not sure if it's gonna help it Thanks for trying anyway 😄
Want results from more Discord servers?
Add your server