Lars Holdaas
Lars Holdaas

Reputation: 750

GraphQL data modelling - extended types (Prisma)

In my Prisma Data Model I started out with a basic User type like this:

type User {
  name: String!
  email: String! @unique
  password: String!
}

Now a User can have two roles: either as a candidate or as a user associated with an employer. If a candidate, the user should also have a set of applications and a set of qualifications, if associated with an employer it should have an access level and a reference to the employer.

First off, is there any way to extend basic types in GraphQL data modelling? If so, how would I go about doing it?

If there is not, I can see three different methods used, and I'm curious what are the pros and cons of each approach:

  1. Having two separate types CandidateUser and EmployerUser, each with the fields name, email, password. I see two problems with this approach: The @unique tag on email is not reliable, and I would have to write a custom verification to make sure the field is unique across both types; and having a single login-function that takes email and fetches the users corresponding data is no longer trivial: it needs to do a lookup in both tables.

Like this:

    type CandidateUser {
      name: String!
      email: String! @unique
      password: String!
      applications: [Application!]!
      qualifications: [Qualification!]!
    }
    type EmployerUser{
      name: String!
      email: String! @unique
      password: String!
      employer: Employer!
      accessRight: AccessRight!
    }
  1. Again two separate types, but with a RootUser containing name, email and password, and with CandidateUser and EmployerUser each having a one-to-one reference to a RootUser. This would enforce the @unique tag on the email field, but lookup would still be nontrivial.

    type RootUser{
      name: String!
      email: String! @unique
      password: String!
    }
    type CandidateUser {
      rootUser: RootUser!
      applications: [Application!]!
      qualifications: [Qualification!]!
    }
    type EmployerUser{
      rootUser: RootUser!
      employer: Employer!
      accessRight: AccessRight!
    }
    
  2. Extending User to have the fields within EmployerUser and CandidateUser as optional parameters. This is a pretty simple approach, but I would need custom handling to enforce requiring fields (as in, I can not mark for instance employer as required as that field would not exist for a Candidate).

    type User{
      name: String!
      email: String! @unique
      password: String!
      applications: [Application!]!
      qualifications: [Qualification!]!
      employer: Employer
      accessRight: AccessRight
    }
    

I really want to ask if there is a better way of solving this. I'm still pretty new to GraphQL and not the best data modeler to begin with, but I'd greatly appraciate any nudge in the right direction :)

And if I do not have any other choice but the three I listed, which one would make the most sense?

Upvotes: 0

Views: 1821

Answers (1)

u-ways
u-ways

Reputation: 7714

What you're trying to do is implementing an interface type:

An Interface is an abstract type that includes a certain set of fields that a type must include to implement the interface.

interface User {
  name: String!
  email: String! @unique
  password: String!
}

This means that any type that implements User needs to have these exact fields, with these arguments and return types. So now your Candidate type can implement User:

type Candidate implements User {
  name: String!
  email: String! @unique
  password: String!
  applications: [Application!]!
  qualifications: [Qualification!]!
}

Interfaces are useful when you want to return an object or set of objects, but those might be of several different types. Have a look at the interface abstract type documentation for more information.


Update:

Since this is a Prisma GraphQL question now, you should be aware that Prisma does not support Interfaces or Union Types as yet. Issue #83 and issue #165 discuss both respectively as feature requests.

However, there is this great article that discuss the workarounds for such approach:

GraphQL Interfaces (and Union Types) with Prisma and Yoga

Which boils down to 2 options:

  1. Storing all data with optional type-specific fields under one type (the interface) in Prisma, and then splitting the data back between the primitive types in the app server.
  2. Storing the data in each primitive type on Prisma, and stitching things for queries on the app server.

Upvotes: 2

Related Questions