Prisma and node.js complex module issues - node.js

I'm going to try my best to reproduce the issues in this case. I'll probably delete it if I can't get the point across well enough. My development environment includes dockerand node.js
I have 7 models within my prisma schema:
User
AuditLog
Finance
VaultTransaction
Empire
WarLogEntry
Structure
The prisma schema is laid out as such
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model User {
id String #id #default(uuid())
username String
mail String
audit AuditLog[]
empire Empire?
}
model AuditLog {
id String #id #default(uuid())
userId String #map("user_id")
user User #relation(fields: [userId], references: [id])
time DateTime
ip String
client String
i18nKey String #map("i18n_key")
}
model Finance {
id String #id #default(uuid())
gold Float #default(0)
vaultGold Float #default(0)
transactions VaultTransaction[]
empireId String #map("empire_id")
empire Empire #relation(fields: [empireId], references: [id])
}
model VaultTransaction {
id String #id #default(uuid())
financeId String #map("finance_id")
finance Finance #relation(fields: [financeId], references: [id])
i18nKey String #map("i18n_key")
amount Float
}
model Empire {
id String #id #default(uuid())
userId String #map("user_id")
user User #relation(fields: [userId], references: [id])
bannerId Int #default(0) #map("banner_id")
description String?
armory Json
population Json
level Int #default(0)
experience Int #default(0)
attributes Json
turnsAvailable Int #map("turns_available")
race String
class String
finance Finance?
attacks WarLogEntry[] #relation("empire_attacks")
defenses WarLogEntry[] #relation("empire_defenses")
structures Structure[]
}
model WarLogEntry {
id String #id #default(uuid())
battleType String #map("battle_type")
attackerID String #map("attacker_id")
attacker Empire #relation("empire_attacks", fields: [attackerID], references: [id])
defenderID String #map("defender_id")
defender Empire #relation("empire_defenses", fields: [defenderID], references: [id])
turns Int
details Json
}
model Structure {
id String #id #default(uuid())
empireId String #map("empire_id")
empire Empire #relation(fields: [empireId], references: [id])
i18nKey String #map("i18n_key")
category String
attributes Json
}
I have type files, resolver files, service files, and module files for each model, but prisma seems to be having some strange errors. I believe they are circular dependency errors, but I fixed the type files to make each complex module #Field optional so that should've fixed it.
My current errors node.js are as such (they change slightly depending on how i edit the files) It appears it is a prisma issue.
[10:14:54 AM] Starting compilation in watch mode...
src/model/empire/empire.service.ts:8:5 - error TS2322: Type '(Empire & { finance: Finance; attacks: WarLogEntry[]; defenses: WarLogEntry[]; structures: Structure[]; })[]' is not assignable to type 'Empire[]'.
Type 'Empire & { finance: Finance; attacks: WarLogEntry[]; defenses: WarLogEntry[]; structures: Structure[]; }' is not assignable to type 'Empire'.
The types of 'finance.gold' are incompatible between these types.
Type 'Decimal' is not assignable to type 'number'.
8 return prisma.empire.findMany({
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9 include: {
~~~~~~~~~~~~~~~~
...
14 },
~~~~~~~~
15 });
~~~~~~~
src/model/finance/finance.service.ts:8:5 - error TS2322: Type '(Finance & { transactions: VaultTransaction[]; })[]' is not assignable to type 'Finance[]'.
Type 'Finance & { transactions: VaultTransaction[]; }' is not assignable to type 'Finance'.
Types of property 'gold' are incompatible.
Type 'Decimal' is not assignable to type 'number'.
8 return prisma.finance.findMany({
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9 include: {
~~~~~~~~~~~~~~~~
...
11 },
~~~~~~~~
12 });
~~~~~~~
src/model/vault-transaction/vault-transaction.service.ts:8:5 - error TS2322: Type 'import("/home/paul/dukesthrone/DukesThrone/dt-backend/node_modules/.pnpm/#prisma+client#2.28.0_prisma#2.28.0/node_modules/.prisma/client/index").VaultTransaction[]' is not assignable to type 'import("/home/paul/dukesthrone/DukesThrone/dt-backend/src/model/types/vault-transaction.type").VaultTransaction[]'.
Type 'import("/home/paul/dukesthrone/DukesThrone/dt-backend/node_modules/.pnpm/#prisma+client#2.28.0_prisma#2.28.0/node_modules/.prisma/client/index").VaultTransaction' is not assignable to type 'import("/home/paul/dukesthrone/DukesThrone/dt-backend/src/model/types/vault-transaction.type").VaultTransaction'.
Types of property 'amount' are incompatible.
Type 'Decimal' is not assignable to type 'number'.
8 return prisma.vaultTransaction.findMany();
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[10:14:56 AM] Found 3 errors. Watching for file changes.
Here is the finance.service.ts file for model Finance. The return type includes transactions which is of type model VaultTransactions
#Injectable()
export class FinanceService {
async getFinances(): Promise<Finance[]> {
return prisma.finance.findMany({
include: {
transactions: true,
},
});
}
}
Update: im not sure if this is relevant, but i think it may be.
It's an issue tracker on github for circular dependcies in node.js.
https://github.com/MichalLytek/type-graphql/pull/237

Your return type is incorrect as it's not just returning Finance data but also transactions inside Finance.
The type should be:
import { Prisma } from '#prisma/client'
type GetFinances = Prisma.FinanceGetPayload<{
include: { transactions: true }
}>
#Injectable()
export class FinanceService {
async getFinances(): Promise<GetFinances[]> {
return prisma.finance.findMany({
include: {
transactions: true,
},
});
}
}

Related

Prisma ORM | MongoDB - how to group documents by mutiple attributes and list the rest of them

I'm developing a quite simple backend application with Express, using Prisma ORM to connect to a MongoDB database.
The events collection is filled with documents such as:
{
"_id": string,
"class_code": string
"class_type": string,
"created_by": string,
"end_period": date,
"end_time": time,
"has_to_be_allocated": boolean
"pendings": int,
"preferences": {
"accessibility": boolean
"air_conditioning": boolean,
"building": string,
"projector": boolean
},
"start_period": date,
"start_time": time,
"subject_code": string,
"subject_name": stirng,
"subscribers": int,
"updated_at": timestamp,
"vacancies": int,
"week_day": string,
"building": string,
"classroom": string
}
My prisma schema is:
type Preferences {
accessibility Boolean?
air_conditioning Boolean?
building String?
projector Boolean?
}
model events {
id String #id #default(auto()) #map("_id") #db.ObjectId
class_code String
subject_code String
subject_name String
week_day String
class_type String
start_period String
end_period String
start_time String
end_time String
has_to_be_allocated Boolean
pendings Int
subscribers Int
vacancies Int
created_by String
updated_at String
preferences Preferences?
professor String?
classroom String?
building String?
}
Multiple different documents could have the same class_code and subject_code, but different week_day, start_time, end_time, building and classroom attributes.
My intention is to perform a single query so that I end up with a list of objects like such:
{
"subject_name":"Subject name",
"subject_code":"Subject code",
"class_code":"1",
"professor":"professor name",
"start_period":"2023-03-13",
"end_period":"2023-07-15",
"schedule":[
{
"id":"1",
"week_day":"monday",
"start_time":"09:20",
"end_time":"11:00",
"building":"building 1",
"classroom":"C2-03"
},
{
"id":"2",
"week_day":"thursday",
"start_time":"07:30",
"end_time":"09:10",
"building":"building 2",
"classroom":"C2-08"
},
{
"id":"3",
"week_day":"friday",
"start_time":"07:30",
"end_time":"09:10",
"building":"building 3",
"classroom":"C2-04"
}
]
}
That is, I want to group the documents by subject_code and class_code simultaneously, while listing the rest of the differing information between the documents as schedule.
I could always fetch all documents and algorithmically create the object I need, but that would be rather inefficient (and lame).
Any thoughts on how to perform such task? I've been playing around with Prisma's groupBy API but without any success - needless to say, I'm new to it.
So far all I've got is a route which filters the database by subject_code and class_code, and that works fine:
const classInfo = await prisma.events.findMany({
where: {
subject_code:
equals: subjectCode as string,
},
class_code: {
endsWith: fullClassCode,
},
},
});
I then map the retrieved object to the format I need:
const classDetail = {
subjectName: classInfo?.[0]?.subject_name,
subjectCode: classInfo?.[0]?.subject_code,
classCode: getAbbreviatedClassCode(classInfo?.[0]?.class_code),
professor: classInfo?.[0]?.professor,
startPeriod: classInfo?.[0]?.start_period,
endPeriod: classInfo?.[0]?.end_period,
schedule: classInfo.map((event: Event) => ({
id: event?.id,
weekDay: mapWeekDays(event?.week_day),
startTime: event?.start_time,
endTime: event?.end_time,
building: event?.building,
classroom: event?.classroom,
})),
};
What I need is to list all combinations of subject_code and class_code while obtaining objects like the one I mentioned above.
Ok I am no expert in prisma or mongodb but you can try the following (If this basic query works, just modify it to include the additional fields):
const agg = [
{
'$group': {
'_id': {
'class_code': '$class_code',
'subject_code': '$subject_code'
},
'schedule': {
'$push': {
'week_day': '$week_day',
'start_time': '$start_time'
}
}
}
}
]
const result = await prisma.events.aggregateRaw({
pipeline: agg,
});
References:
https://www.mongodb.com/docs/manual/reference/operator/update/push/
https://www.mongodb.com/docs/manual/reference/operator/aggregation/group/#pivot-data
What does your events prisma schema look like? (IE, how is it defined in the schema.prisma file?
Also, is the schedule a separate collection or are these entries simply embedded documents?

Defining a simple union return type in a graphql schema

I’m actually unsure why, but I can’t seem to fix this seemingly simple problem. I have a graphql schema as follows:
import { gql } from 'apollo-server-core';
export const querySchema = gql`
type AffiliateBalance {
pendingBalance: Float!
qualifiedBalance: Float!
}
type Query {
me: User!
users(input: PaginationInput): [User!]!
user(id: Int!): User!
referrals(input: GetReferralsInput): GetReferralsResponse!
affiliateTransactions(
limit: Int
skip: Int
type: TransactionType
): GetAffiliateTransactionsResponse!
affiliatePerformanceMetrics: AffiliatePerformanceMetricsResponse!
affiliateSessions(limit: Int, skip: Int): GetAffiliateSessionsResponse!
affiliateSessionMetrics: AffiliateSessionMetricsResponse!
affiliateBalancePending: Float!
affiliateBalanceQualified: Float!
affiliateBalance: Float! | AffiliateBalance!
}
`;
I have recently added an affiliateBalance query, which can either return a number to 2DP (e.g. 47.28), or this object:


`{ pendingBalance: some2DPNumber, qualifiedBalance: some2DPNumber }`
But I am getting this error:
return new _GraphQLError.GraphQLError(`Syntax Error: ${description}`, {
^
GraphQLError: Syntax Error: Expected Name, found "|"
What am I doing wrong here? And if it is supposedly a syntax error with the "|" symbol, how can it actually be fixed? The result of the affiliateBalance query needs to be a union, hence the symbol.
I’m using Apollo Server with node. Thank you
UPDATE:
The error I get with the code in my question as requested:
/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/graphql/error/syntaxError.js:15 return new _GraphQLError.GraphQLError(Syntax Error: ${description}, { ^ GraphQLError: Syntax Error: Expected Name, found "|". at syntaxError (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/graphql/error/syntaxError.js:15:10) at Parser.expectToken (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/graphql/language/parser.js:1413:40) at Parser.parseName (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/graphql/language/parser.js:107:24) at Parser.parseFieldDefinition (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/graphql/language/parser.js:845:23) at Parser.optionalMany (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/graphql/language/parser.js:1510:28) at Parser.parseFieldsDefinition (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/graphql/language/parser.js:831:17) at Parser.parseObjectTypeDefinition (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/graphql/language/parser.js:803:25) at Parser.parseDefinition (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/graphql/language/parser.js:171:23) at Parser.many (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/graphql/language/parser.js:1529:26) at Parser.parseDocument (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/graphql/language/parser.js:121:25) { path: undefined, locations: [ { line: 22, column: 30 } ], extensions: [Object: null prototype] {} }
Then if I change it to this:
import { gql } from 'apollo-server-core';
export const querySchema = gql`
type AffiliateBalance {
pendingBalance: Float!
qualifiedBalance: Float!
}
union RawAffiliateBalance = Float | AffiliateBalance
type Query {
me: User!
users(input: PaginationInput): [User!]!
user(id: Int!): User!
referrals(input: GetReferralsInput): GetReferralsResponse!
affiliateTransactions(
limit: Int
skip: Int
type: TransactionType
): GetAffiliateTransactionsResponse!
affiliatePerformanceMetrics: AffiliatePerformanceMetricsResponse!
affiliateSessions(limit: Int, skip: Int): GetAffiliateSessionsResponse!
affiliateSessionMetrics: AffiliateSessionMetricsResponse!
affiliateBalancePending: Float!
affiliateBalanceQualified: Float!
affiliateBalance: RawAffiliateBalance!
}
`;
I get this error:
/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/graphql/type/validate.js:59
throw new Error(errors.map((error) => error.message).join('\n\n'));
^
Error: Union type RawAffiliateBalance can only include Object types, it cannot include Float.
at assertValidSchema (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/graphql/type/validate.js:59:11)
at assertValidExecutionArguments (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/graphql/execution/execute.js:194:35)
at execute (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/graphql/execution/execute.js:113:3)
at generateSchemaHash (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/apollo-server-core/src/utils/schemaHash.ts:25:25)
at ApolloServer.generateSchemaDerivedData (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/apollo-server-core/src/ApolloServer.ts:716:42)
at Object.schemaDerivedDataProvider (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/apollo-server-core/src/ApolloServer.ts:333:18)
at new SchemaManager (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/apollo-server-core/src/utils/schemaManager.ts:76:36)
at new ApolloServerBase (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/apollo-server-core/src/ApolloServer.ts:328:24)
at new ApolloServer (/Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/node_modules/apollo-server-express/src/ApolloServer.ts:55:1)
at /Users/nick/Documents/Coding/Projects/Practice/PERNTG/server/src/index.ts:20:48
I believe you should be able to define a union type (scroll down to find the section, they did not use ids for easy hashing in the documentation)
about like this:
type AffiliateBalance {
pendingBalance: Float!
qualifiedBalance: Float!
}
union RawAffiliateBalance = Float | AffiliateBalance
type Query {
//...
affiliateBalance: RawAffiliateBalance!
update
Since Apollo gql is strictly interpreting the graphql specification (see https://github.com/graphql/graphql-spec/issues/215 ) and this has been an unresolved but actively debated issue for at least 7 years now, I suggest you might try boxing your scalar to work around the issue in graphql.
type AffiliateBalance {
pendingBalance: Float!
qualifiedBalance: Float!
}
type RawBalance {
value: Float!
}
union RawAffiliateBalance = RawBalance | AffiliateBalance
type Query {
//...
affiliateBalance: RawAffiliateBalance!
This requires revising the response payload you were hoping to serve to clients, and that may be more work than intended. Another possibility (aimed to avoid changing clients) would be to wrap it like this and expose an adapter endpoint that is not graphql and just transforms the final value for you.

How do insert data into this schema?

Below is my prisma schema
model Allegations {
allegation_id String #id #db.VarChar(200)
faculty String? #db.VarChar(200)
department String? #db.VarChar(200)
course String? #db.VarChar(200)
instructor String? #db.VarChar(200)
instructorEmail String? #db.VarChar(200)
instructorPh String? #db.VarChar(200)
details String? #db.VarChar(200)
allegationDate DateTime? #db.DateTime(0)
violationDate DateTime? #db.DateTime(0)
Students Students[]
}
model Students {
id String #id #db.VarChar(200)
student_name String? #db.VarChar(200)
banner String? #db.VarChar(200)
allegation_id String? #db.VarChar(200)
Allegations Allegations? #relation(fields: [allegation_id], references: [allegation_id], onDelete: Restrict, onUpdate: Restrict, map: "Students_ibfk_1")
##index([allegation_id], map: "allegation_id")
}
So far I got his but getting errors. I also tried inserting seperately into allegations and students, but the insert in students doesn't go through.
prisma.allegations.create({
data: {
allegation_id:key,
faculty: Faculty,
department: Department,
course: Course,
instructor: Instructor,
instructorPh: PhoneNumber,
instructorEmail: InstructorEmail,
details: Details,
allegationDate: date,
violationDate: OffenceDate,
Students:{
create:{
student_name: 'kn',
banner: '555555'
},
}
}
});
Modifying your code snippet slightly, I was able to correctly insert a record into the database. Also, you have to specify the student ID because you do not have the #default attribute on the Student schema. Find the modified code below
const allegation = await prisma.allegations.create({
data: {
allegation_id:"1",
faculty: "Science",
department: "Biochemistry",
course: "BCH101",
instructor: "John Doe",
instructorPh: "+231345678",
instructorEmail: "johndoe#test.com",
details: "some details",
allegationDate: new Date('2022','07', '31'),
violationDate: new Date('2022','08', '01'),
Students:{
create:{
id: 'std1',
student_name: 'Test Student',
banner: '555555'
},
}
}
})

Trying to use nominal typing for id in typeorm typescript

I was following this article TypeORM Best Practices using Typescript and NestJS at Libeo.
I got to the point of using nominal typing in our entity ID
#PrimaryGeneratedColumn("uuid")
id!: string & { __brand: "userId" };
but cannot use it in find operation example
async getUserById(id: User['id']) {
const user = await this.findOne({
where: { id: 'id' },
});
return user;
},
I am having the following errors
Type '{ id: string; }' is not assignable to type 'FindOptionsWhere<User> | FindOptionsWhere<User>[] | undefined'.
Types of property 'id' are incompatible.
Type 'string' is not assignable to type 'boolean | FindOperator<any> | never[] | EqualOperator<never> | undefined'.
where: { id: 'id' },
Actually don't know what am doing wrong.
but if I remove the nominal part everything works very well
#PrimaryGeneratedColumn("uuid")
id!: string
I have also tried using just Primary Column
#PrimaryColumn("uuid")
id!: string & { __brand: "userId" };
Still not working. I think it has to do with the FindOption
enum UserIdBrand {
__type = 'User',
}
type UserId = string & UserIdBrand;
#hittingonme Thanks
Also using enum didn't work, had to declare it as a type UserId

Prisma Implicit M2N relationship doesn't apply pagination on nested object

have a model with stores and articles.
stores have many articles and articles many stores
As this is an implicit M2N, Prisma has generated automatically the Join table _articlesTohypermarches_stores
The max number of articles per store is 10K, but I want for example only the 10 first and not the whole 10K
I tried to apply pagination when querying the stores but the query sent to the _articlesTohypermarches_stores table has no LIMIT 10 thus resulting in fetching the 10K articles...
here are the query I'm testing, the model and the the queries sent to POSTGRES by Prisma, as well as the PRISMA Logs that showing the pb.
const test = await prisma.hypermarches_stores.findMany({
where: {
id: {
equals: 209,
},
},
select: {
stoDisplayDesc: true,
articles: {
take: 10, // <--- this is not taking into account
skip: 1,
select: {
title: true,
},
},
},
take: 2,
});
model articles {
id Int #id(map: "PK_0a6e2c450d83e0b6052c2793334") #default(autoincrement())
title String? #default("") #db.VarChar(3000)
prioriteId Int?
isAlerte Boolean? #default(false)
categorieId Int?
publicationStartDate DateTime? #default(dbgenerated("'2021-12-29 17:02:14.651'::timestamp without time zone")) #db.Timestamp(6)
publicationEndDate DateTime? #default(dbgenerated("'2021-12-29 17:02:14.651'::timestamp without time zone")) #db.Timestamp(6)
hasAction Boolean? #default(false)
briefDescription String? #default("") #db.VarChar(3000)
content String? #default("")
contact1 String? #default("") #db.VarChar(300)
contact2 String? #default("") #db.VarChar(300)
author String? #default("") #db.VarChar(300)
recordCreationDate DateTime #default(now()) #db.Timestamp(6)
recordUpdateDate DateTime #default(now()) #db.Timestamp(6)
ref_categories ref_categories? #relation(fields: [categorieId], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "FK_278d87b271a80d56e5d6cc0f888")
ref_priorites ref_priorites? #relation(fields: [prioriteId], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "FK_a55acd217d26e0d60f57b5f38f7")
articles_documents articles_documents[]
fonctions ref_fonctions[]
magasins hypermarches_stores[]
metiers ref_metiers[]
perimetres ref_perimetres[]
poles ref_poles[]
services ref_services[]
articles_statuts_contribution articles_statuts_contribution[]
##index([id], map: "IDX_0a6e2c450d83e0b6052c279333")
}
model hypermarches_stores {
id Int #id(map: "PK_7b1178bff8dc98cbd6d68fc2dd0") #default(autoincrement())
stoEan String #unique(map: "UQ_7fddfa16362979011e27b4d0b21") #db.VarChar(300)
articles articles[]
##index([id], map: "IDX_7b1178bff8dc98cbd6d68fc2dd")
}

Resources