DEV Community

loading...

Ugly Parts of GraphQL with Prisma

Pablo Díaz Márquez
Computer Scientist #DevLife
・2 min read

The Stack

First, this is a post of what I think are the disadvantages of graphql combined with Prisma and graphql-yoga.

First

For this model

type Company @db(name: "companies") {
  id: ID! @id
  active: Boolean! @default(value: true)
  name: String!
  createdBy: User! @relation(link: INLINE)
  createdAt: DateTime! @createdAt
  updatedAt: DateTime! @updatedAt
}

Here is how a mutation would be done.

async createCompany(parent, args, ctx, info) {
    // 1. Check if they are logged in
    if (!ctx.request.userId) {
      throw new Error('You must be logged in!');
    }
    // 2. create
    const company = await ctx.db.mutation.createCompany(
      {
        data: {
          ...args,
          active: true,
        }
      },
      info
    );
    return company;
  }

Now for adding the createdBy attribute, this should be added to data

 {
        data: {
          ...args,
          active: true,
          // notice the connect object
          createdBy: { connect: { id: ctx.request.userId } }
        }
 }

Adding the userId in the connect object feels a little bloated, but it is not a big deal, it is fine. This will add only the id in the document of company.

Now, what if we want to add the user as a whole subdocument?

Then the existing Company model cannot be extended and the current model of User should have the @embedded config.

type User @embedded {
  name: String!
  email: String! @unique
  password: String!
  resetToken: String
  resetTokenExpiry: Float
}

And here is when it gets complicated

Now we want to use the create company mutation

  {
        data: {
          ...args,
          active: true,
          user: {
              create: {
                 // here goes all the user attributes that are required
              }
          },
          createdBy: { connect: { id: ctx.request.userId } }
        }
      },

This time the user object should be wrapped around a create object, and guess what happens if the user has another subdocument?

  {
        data: {
          ...args,
          active: true,
          user: {
              create: {
                 // what if the user has a permissions subdocument ?
                 permissions: {
                     create: {
                          name: "ADMIN"
                     }
                 }
              }
          },
          createdBy: { connect: { id: ctx.request.userId } }
        }
      },

Each time a subdocument is added it has to be wrapped around a create object! That's really annoying. For complex models, this will bloat the creation of documents with a lot of create objects.

And here it is another thing, for each create object, when it is saved in the database, it will create an _id attribute for each level even if it is not specified in the model, so this is unnecessary.

Second

Reusing the first mutation defined

async createCompany(parent, args, ctx, info) {
    // 1. Check if they are logged in
    if (!ctx.request.userId) {
      throw new Error('You must be logged in!');
    }
    // 2. create
    const company = await ctx.db.mutation.createCompany(
      {
        data: {
          ...args,
          active: true,
        }
      },
      info
    );
    return company;
  }

When this is called from the frontend using the Apollo Client

This is how the mutation can be called

 // create new company
 const promise = mutate({
       mutation: COMPANY_MUTATION,
      variables: { name: inputValue.value }
 });

promise.then((item) => {
  // to access the data returned from the server.
  const name = item.data.createCompany.name;
});

So, the response is filled with an object called data and another object with the name of the method defined in the server. This also feels bloated because now you have to know the method name of the backend while accessing the data in the frontend.

End

This is list is short, but I really don't like them. What do you think?

Discussion (1)

Collapse
theodesp profile image
Theofanis Despoudis

I think I would not even touch GraphQL using plain Javascript. It looks fragile