Search code examples
mongodbapolloapollo-serverprismaprisma-graphql

Referenced objects in DB returning null in GraphQL Query


I have the following database model:

  • Each Mediablock contains a reference to exactly one UTS object and one Media object.

enter image description here

  • Each UTS object contains rawText and normalisedText

enter image description here

  • Each Media object contains a url and a timestamp

enter image description here

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.

enter image description here

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.

enter image description here

What am I doing wrong? Are my resolvers incorrect? Is my schema structured incorrectly for MongoDB?


Solution

  • 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"
            }
          }
        ]
      }
    }