Referenced objects in DB returning null in GraphQL Query - mongodb

I have the following database model:
Each Mediablock contains a reference to exactly one UTS object and one Media object.
Each UTS object contains rawText and normalisedText
Each Media object contains a url and a timestamp
My schema.prisma looks like this:
datasource db {
provider = "mongodb"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model Mediablock {
id String #id #default(auto()) #map("_id") #db.ObjectId // gen a new, unique id
UTS UTS #relation(fields: [utsId], references: [id])
utsId String #unique #map("uts_id") #db.ObjectId
Media Media #relation(fields: [mediaId], references: [id])
mediaId String #unique #map("media_id") #db.ObjectId
}
model UTS {
id String #id #default(auto()) #map("_id") #db.ObjectId
rawText String
normalisedText String
createdAt DateTime #default(now())
Mediablock Mediablock?
}
// // Mediablocks contain a Video object and connect back to the Mediablock.
// // mediablockId must have #db.ObjectId to match up with Mediablock's id type
model Media {
id String #id #default(auto()) #map("_id") #db.ObjectId
url String
createdAt DateTime #default(now())
Mediablock Mediablock?
}
My resolvers look like this:
const { PrismaClient } = require('#prisma/client');
const prisma = new PrismaClient();
//This resolver retrieves mediabooks from the "mediabooks" array above.
module.exports = {
Query: {
allMediablocks: () => prisma.mediablock.findMany(),
allMedia: () => prisma.media.findMany(),
allUTS: () => prisma.uts.findMany(),
},
};
And my typedefs looks like this:
module.exports = `
type Mediablock {
id: ID!
uts: UTS
media: Media # can be null when the text is generated first
}
type UTS {
id: ID!
rawText: String!
normalisedText: String!
}
type Media {
id: ID!
url: String!
createdAt: String!
}
# The "Query" type is special: it lists all of the available queries that
# clients can execute, along with the return type for each. In this
# case, the "allMediablocks" query returns an array of zero or more Mediablocks (defined above).
type Query {
allMediablocks: [Mediablock]
allMedia: [Media]
allUTS: [UTS]
}
`;
My seed file looks like this:
const { PrismaClient } = require('#prisma/client');
const prisma = new PrismaClient();
const mediaData = [
{
UTS: {
create: {
rawText: 'Welcome',
normalisedText: 'welcome',
},
},
Media: {
create: {
url: 'https://www.youtube.com/watch?v=kq9aShH2Kg4',
createdAt: '2022-09-29T12:00:00.000Z',
}
}
}
];
async function main() {
console.log(`Started seeding ...`);
for (const d of mediaData) {
const mediablock = await prisma.Mediablock.create({
data: d,
});
console.log(`Created Mediablock with id: ${mediablock.id}`);
}
console.log(`\nSeeding complete.`);
}
main()
.then(async () => {
await prisma.$disconnect();
})
.catch(async (e) => {
console.error(e);
await prisma.$disconnect();
process.exit(1);
});
My problem is that when I attempt to query allMediablocks, I can't get any of the UTS or Media data.
query allMediaBlocks {
allMediablocks {
uts {
normalisedText
}
media {
url
}
}
}
// response
{
"data": {
"allMediablocks": [
{
"uts": null,
"media": null
}
]
}
}
I just get null values for both, when in fact, the database (MongoDB) contains references to both of these objects in other tables.
What am I doing wrong? Are my resolvers incorrect?
Is my schema structured incorrectly for MongoDB?

I've fixed it by changing my schema.prisma to be:
datasource db {
provider = "mongodb"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model Mediablock {
id String #id #default(auto()) #map("_id") #db.ObjectId // gen a new, unique id
utsId String #unique #map("uts_id") #db.ObjectId
uts UTS #relation(fields: [utsId], references: [id])
mediaId String #unique #map("media_id") #db.ObjectId
media Media #relation(fields: [mediaId], references: [id])
}
model UTS {
id String #id #default(auto()) #map("_id") #db.ObjectId
rawText String
normalisedText String
createdAt DateTime #default(now())
mediablock Mediablock?
}
// // Mediablocks contain a Video object and connect back to the Mediablock.
// // mediablockId must have #db.ObjectId to match up with Mediablock's id type
model Media {
id String #id #default(auto()) #map("_id") #db.ObjectId
url String
createdAt DateTime #default(now())
mediablock Mediablock?
}
My resolver to be:
const { PrismaClient } = require('#prisma/client');
const prisma = new PrismaClient();
//This resolver retrieves mediabooks from the "mediabooks" array above.
module.exports = {
Query: {
allMediablocks: () => prisma.mediablock.findMany({
include: {
media: true,
uts: true
}
}),
allMedia: () => prisma.media.findMany(),
allUTS: () => prisma.uts.findMany(),
},
};
And my typedefs to be:
module.exports = `
type Mediablock {
id: ID!
uts: UTS
media: Media # can be null when the text is generated first
}
type UTS {
id: ID!
rawText: String!
normalisedText: String!
}
type Media {
id: ID!
url: String!
createdAt: String!
}
# The "Query" type is special: it lists all of the available queries that
# clients can execute, along with the return type for each. In this
# case, the "allMediablocks" query returns an array of zero or more Mediablocks (defined above).
type Query {
allMediablocks: [Mediablock]
allMedia: [Media]
allUTS: [UTS]
}
`;
I also changed the way my seed function works:
const { PrismaClient } = require('#prisma/client');
const prisma = new PrismaClient();
const mediaData = [
{
uts: {
create: {
rawText: 'Welcome',
normalisedText: 'welcome',
createdAt: '2022-09-29T12:00:00.000Z',
},
},
media: {
create: {
url: 'https://www.youtube.com/watch?v=kq9aShH2Kg4',
createdAt: '2022-09-29T12:00:00.000Z',
}
}
}
];
async function main() {
console.log(`Started seeding ...`);
for (const d of mediaData) {
const mediablock = await prisma.Mediablock.create({
data: d,
});
console.log(mediablock);
}
console.log(`\nSeeding complete.`);
}
main()
.then(async () => {
await prisma.$disconnect();
})
.catch(async (e) => {
console.error(e);
await prisma.$disconnect();
process.exit(1);
});
Now, using the same GraphQL query:
query allMediaBlocks {
allMediablocks {
uts {
normalisedText
}
media {
url
}
}
}
I get the following (desired) response:
{
"data": {
"allMediablocks": [
{
"uts": {
"normalisedText": "welcome"
},
"media": {
"url": "https://www.youtube.com/watch?v=kq9aShH2Kg4"
}
}
]
}
}

Related

how to query object in mongo using prisma

I want to query by using where in object with prisma and mongo
Here is my prisma schema
model Member {
id String #id #default(auto()) #map("_id") #db.ObjectId
bank BankInfo
##map("members")
}
type BankInfo {
bankNo String
}
const member = await this.prisma.member.findFirst({
where: { bank: { bankNo: 'test' } },
});
I got an error
Error:
Invalid prisma.member.findFirst() invocation:
You can try to use equal operator ($eq) like that :
const member = await this.db.member.find({
bank: { $eq: { bankNo: 'test' } }
})

Prisma: how to delete a record where it may or may not have children

I am trying to figure out how to delete a record created in my graphql, apollo, prisma app, where the record I am deleting may have a has many relationship to another model.
I have a model called IssueGroups. IssueGroups have many issues.
I have a model with:
import * as Prisma from "#prisma/client"
import { Field, ObjectType } from "type-graphql"
import { BaseModel } from "../shared/base.model"
#ObjectType()
export class IssueGroup extends BaseModel implements Prisma.IssueGroup {
#Field()
title: string
#Field()
description: string
#Field(type => String)
issues: Prisma.Issue[];
}
A resolver with:
import { Arg, Mutation, Query, Resolver } from "type-graphql"
import { IssueGroup } from "./issueGroup.model"
import { IssueGroupService } from "./issueGroup.service"
import { IssueGroupInput } from "./inputs/create.input"
import { Inject, Service } from "typedi"
#Service()
#Resolver(() => IssueGroup)
export default class IssueGroupResolver {
#Inject(() => IssueGroupService)
issueGroupService: IssueGroupService
#Query(() => [IssueGroup])
async allIssueGroups() {
return await this.issueGroupService.getAllIssueGroups()
}
#Query(() => IssueGroup)
async issueGroup(#Arg("id") id: string) {
return await this.issueGroupService.getIssueGroup(id)
}
#Mutation(() => IssueGroup)
async createIssueGroup(#Arg("data") data: IssueGroupInput) {
return await this.issueGroupService.createIssueGroup(data)
}
// : Promise<IssueGroup[]> {
#Mutation(() => IssueGroup)
async updateIssueGroup(
#Arg("id") id: string,
#Arg("data") data: IssueGroupInput
) {
return await this.issueGroupService.updateIssueGroup(id, data)
}
#Mutation(() => IssueGroup)
async deleteIssueGroup(#Arg("id") id: string) {
return await this.issueGroupService.deleteIssueGroup(id)
}
}
// private readonly issueGroupService: IssueGroupService
// #Query(() => [IssueGroup])
// async issueGroups(): Promise<IssueGroup[]> {
// return this.issueGroupService.findAll()
// }
// #Query(() => IssueGroup)
// async issueGroup(#Arg("id") id: string): Promise<IssueGroup> {
// return this.issueGroupService.findOne(id)
// }
// #Mutation(() => IssueGroup)
// async createIssueGroup(
// #Arg("data") data: IssueGroupInput
// ): Promise<IssueGroup> {
// return this.issueGroupService.create(data)
// }
A service with:
import { prisma } from "../../lib/prisma"
import { Service } from "typedi"
import { IssueGroupInput } from "./inputs/create.input"
import { Resolver } from "type-graphql"
import { IssueGroup } from "./issueGroup.model"
#Service()
#Resolver(() => IssueGroup)
export class IssueGroupService {
async createIssueGroup(data: IssueGroupInput) {
return await prisma.issueGroup.create({
data,
})
}
async deleteIssueGroup(id: string) {
return await prisma.issueGroup.delete({ where: { id } })
}
async updateIssueGroup(id: string, data: IssueGroupInput) {
const issueGroup = await prisma.issueGroup.findUnique({ where: { id } })
if (!issueGroup) {
throw new Error("Issue not found")
}
return await prisma.issueGroup.update({ where: { id }, data })
}
async getAllIssueGroups() {
return (await prisma.issueGroup.findMany({orderBy: {title: 'asc'}}))
}
async getIssueGroup(id: string) {
return await prisma.issueGroup.findUnique({
where: {
id,
},
})
}
}
When I try to delete the issueGroup (which does not currently have any issues, I can see an error that points to issue.delete and says:
operation failed because it depends on one or more records that were
required but not found. Record to delete does not exist.
In my form I have:
import * as React from "react"
import { gql } from "#apollo/client"
import type { IssueGroupInput } from "lib/graphql"
import { QueryMode, Role, SortOrder, useAllIssueGroupsQuery, useDeleteIssueGroupMutation } from "lib/graphql"
const __ = gql`
query AllIssueGroups {
allIssueGroups {
id
title
description
}
}
mutation deleteIssueGroup($id: String!) {
deleteIssueGroup(id: $id) {
id
title
description
issues // I have tried both with and without issues in this list
}
}
`
export default function IssueGroups() {
const [deleteIssueGroup] = useDeleteIssueGroupMutation()
const { data, loading, refetch } = useAllIssueGroupsQuery({
fetchPolicy: "cache-and-network",
})
const allIssueGroups = data?.allIssueGroups
const onDeleteIssueGroup = (id: string) => {
return (
deleteIssueGroup({ variables: { id } }).then(() => refetch())
)
}
return (
<List>
{data?.allIssueGroups.map((issueGroup) => (
<ListItem key={issueGroup.id}>
{issueGroup.title}{issueGroup.description}
<Button onClick={() => onDeleteIssueGroup(issueGroup.id)}>Delete</Button>
</ListItem>
))}
</List>
)
}
I have seen this page of the prisma documentation and I think I have followed its advice for how to deal with an issue when the parent issueGroup is deleted.
model IssueGroup {
id String #id #default(dbgenerated("gen_random_uuid()")) #db.Uuid
title String
description String
issues Issue[]
createdAt DateTime #default(now()) #db.Timestamptz(6)
updatedAt DateTime #default(now()) #updatedAt #db.Timestamptz(6)
}
model Issue {
id String #id #default(dbgenerated("gen_random_uuid()")) #db.Uuid
title String
description String
issueGroup IssueGroup #relation(fields: [issueGroupId], references: [id], onDelete: SetNull, onUpdate: Cascade)
issueGroupId String #db.Uuid
subscribers UserIssue[]
createdAt DateTime #default(now()) #db.Timestamptz(6)
updatedAt DateTime #default(now()) #updatedAt #db.Timestamptz(6)
}
However, VS code thinks this is an error. It gives me an error message that says:
The onDelete referential action of a relation should not be set to
SetNull when a referenced field is required. We recommend either to
choose another referential action, or to make the referenced fields
optional.
My db is psql, which the prisma docs suggest, should allow SetNull
PostgreSQL PostgreSQL is the only database supported by Prisma that
allows you to define a SetNull referential action that refers to a
non-nullable field. However, this raises a foreign key constraint
error when the action is triggered at runtime.
For this reason, when you set postgres as the database provider in the
(default) foreignKeys relation mode, Prisma warns users to mark as
optional any fields that are included in a #relation attribute with a
SetNull referential action. For all other database providers, Prisma
rejects the schema with a validation error.
I can see from this page of the docs that lists cannot be optional, so I dont think I can follow the advice in the relational page of the docs and make the issueGroup issues[] as optional.
How can i delete the issue (if there were any) at the same time as I delete the issue group?
Just make issueGroup optional
model IssueGroup {
...
issues Issue[]
...
}
model Issue {
...
issueGroup IssueGroup? #relation(fields: [issueGroupId], references: [id], onDelete: SetNull, onUpdate: Cascade)
issueGroupId String? #db.Uuid
...
}
The actual message from prisma is saying that
Prisma warns users to mark as optional any fields that are included in
a #relation attribute with a SetNull referential action
Your relationship has onDelete: SetNull - so mark it as optional

Why fields in same hierarchy get deleted when updating in Prisma?

I am trying to implement updation using prisma.
here is my data structure.
{
"name":"toy",
"data":{
"sports":{
"currentState":"false",
"oldState":"true"
}
}
Here is my logic to update currentState from false to true.
const updatedSource = await prisma.sources.update({
where: {
name: 'toy'
},
data: {
data: {
update:{
sports: {
currentState: "true"
}
}
}
},
})
Here is the schema file:
type SourcesData {
sports SourcesDataState
}
type SourcesDataState {
currentState String
oldState String
}
model sources {
id String #id #default(auto()) #map("_id") #db.ObjectId
data SourcesData
name String #unique
}
The above logic updates the currentState field but deletes the oldState field from the database.
Please guide me on how to update currentState in a way that oldState data persists.

Relation type in Model is not defined

I am new to Prisma.
I am trying to use Prisma with Nextjs.
But I am facing problems with one to many relations.
I have a schema like below.
model Thread {
id Int #id #default(autoincrement()) #unique
title String
kakikoes Kakiko[]
}
model Kakiko {
id Int #id #default(autoincrement()) #unique
thread Thread #relation(fields: [threadId], references: [id])
threadId Int
}
// Kakiko means "Post" in Japanese.
There is "one to many Relation" which towards "kakiko" from Thread.
I can get datas from DB and I can send datas to browser, But My TS compiler says "TS2339: Property 'kakikoes' does not exist on type 'Thread'."
await prisma.thread.findUnique({
where: {
id: threadId
},
include: {
kakikoes: true
}
})
{thread.kakikoes.map(kakiko => <p>{kakiko.body}</p>)}
I checked type definition, and kakikoes not defined.
/**
* Model Thread
*
*/
export type Thread = {
id: number
itaId: number
title: string
}
How can I solve this?
Thank you.
Added (2022/04/03)
I use Intelij Idea Ultimate.
Type is defined like this.
My code is working correctly on My browser And Nextjs dev Tool doesnt say Any Errors nor Any Warnings.
The model is correct. I have tried it and this is the working solution.
schema.prisma file
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Thread {
id Int #id #unique #default(autoincrement())
title String
kakikoes Kakiko[]
}
model Kakiko {
id Int #id #unique #default(autoincrement())
thread Thread #relation(fields: [threadId], references: [id])
threadId Int
}
main.ts file
import { PrismaClient } from '#prisma/client';
const prisma = new PrismaClient();
async function main() {
// ... you will write your Prisma Client queries here
await prisma.kakiko.create({
data: {
thread: {
create: {
title: 'test',
},
},
},
});
const thread = await prisma.thread.findUnique({
where: {
id: 1,
},
include: {
kakikoes: true,
},
});
console.log('thread', thread);
}
main()
.catch((e) => {
throw e;
})
.finally(async () => {
await prisma.$disconnect();
});
Here is the response
thread { id: 1, title: 'test', kakikoes: [ { id: 1, threadId: 1 } ] }
Attaching the image as well:
I would also suggest restarting your vs code typescript compiler which could cause this issue.

Unknown argument error when creating record

I'm encountering some interesting behaviour when using Prisma ORM. It is related to Prisma's generated types, and I've been skimming the docs trying to find out more, but there doesn't seem to be much info about generated types in there (please correct me if I'm mistaken). Here's the behaviour:
Say I have a model with two 1-1 relations (Profile in the example below):
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model User {
id Int #id #default(autoincrement())
name String
profile Profile?
}
model Profile {
id Int #id #default(autoincrement())
name String
userId Int?
user User? #relation(fields: [userId], references: [id])
photoId Int?
photo Photo? #relation(fields: [photoId], references: [id])
}
model Photo {
id Int #id #default(autoincrement())
url String
profile Profile?
}
The following code works when creating a new profile:
const user = await prisma.user.create({ data: { name: "TestUser" } });
const profile = await prisma.profile.create({
data: {
name: "TestProfile",
user: { connect: { id: user.id } },
photo: { create: { url: "http://example.com/img" } },
},
});
... but this fails with an error:
const user = await prisma.user.create({ data: { name: "TestUser" } });
const profile = await prisma.profile.create({
data: {
name: "TestProfile",
userId: user.id,
photo: { create: { url: "http://example.com/img" } },
},
});
The error is:
Unknown arg userId in data.userId for type ProfileCreateInput. Did you mean user? Available args:
type ProfileCreateInput {
  name: String
  user?: UserCreateNestedOneWithoutProfileInput
  photo?: PhotoCreateNestedOneWithoutProfileInput
}
Why is the second create-profile code invalid?
Prisma essentially generates two type definitions for a create query. This is implemented with a XOR type, which ensures that only one definition out of two is fully specified and passed to the query:
export type ProfileCreateArgs = {
/* ... */
data: XOR<ProfileCreateInput, ProfileUncheckedCreateInput>;
}
The definitions are called checked and unchecked, the former using nested fields and the latter using raw ids:
export type ProfileCreateInput = {
id?: number;
/* ... */
user?: UserCreateNestedOneWithoutProfileInput;
photo?: PhotoCreateNestedOneWithoutProfileInput;
}
export type ProfileUncheckedCreateInput = {
id?: number;
/* ... */
userId?: number;
photoId?: number;
}
Which basically means that you either provide all references as connect, create etc. relations (checked) or as raw ids (unchecked). You cannot mix the styles, this is not supported.