GraphQL Mutations and working with Prisma

Starting Github repository. Use start-mutations branch

Get the full GraphQL series here

In this post we’re going to look at working with GraphQL Mutations to perform CRUD operations on our MySQL database managed by Prisma.

Learn to code with our beginner friendly tutorials on programming, Linux, Docker, and more

So we have an empty database. Let’s setup some Mutations so we can do some CRUD with GraphQL!

We have two places we need to edit in our code. The “resolvers” object that defines how to retrieve, add, update, and delete the data, and the “typeDefs” that defines the “layout” of the server (allowable operations, parameters accepted, return values, etc)

Create a database record: CreateUser

The first thing we’ll add is a createUser operation. Edit your schema.graphql file by adding the Mutation root type below:

type Query {
  helloWorld: String!
  users: [User!]!
  user(userId: ID!): User
}

type Mutation {
  createUser(email: String!, firstName: String!): User
}
type User {
    id: ID!
    firstName: String!
    email: String!
}

The above Mutation creates a “createUser” operation that takes in a required “email” parameter of type String, and a required firstName paremeter of type String. it returns a User type meaning the return value of createUser() must look exactly like the type User (defined right below type Mutation).

Now let’s define how our server will actually create the record in our const resolvers in index.js Right after the Query: { … } put in the following code:

const resolvers = {
Query: {
//...
},
Mutation: {
  createUser: (parent, args, context, info) => {
    const newUser = context.prisma.user.create({
      data: {
        email: args.email,
        firstName: args.firstName,
      },
    });
    return newUser;
  },
},
// User below...

Now we create a new user with context.prisma.user.create. The arguments passed into the mutation (email and firstName) are accessed via the args parameter. If you go look at the schema.prisma file you’ll see that we need an “id”, “createdAt”, “firstName”, and “email”. Prisma automatically takes care of id and createdAt but doesn’t know by default what to do with email and firstName So we tell prisma to set those values to args.firstName and args.email

Finally, we return the newUser object because in our typeDefs in schema.graphql, the createUser function is expected to return a User object. Refresh your server and let’s give it a shot by entering the following query into the playground:

mutation {
  createUser(email: "joe@joe.com", firstName: "Joe") {
    email
    id
    firstName
  }
}

Note that I’m using a separate tab in the playground. This way I can switch between various queries quickly.

Delete Mutations in GraphQL & Prisma

Now that we have some records in our database we can setup the ability to delete them.

We already know everything we need to delete a record using Mutations and prisma. Think about what we need:

  • The id of the record to delete
  • a prisma function that deletes (We can check context for available functions by logging, breakpoint, or docs)
  • typeDefinition for deleteUser
  • resolver that actually does the deleting

Let’s get to it! deleteUser will “mutate” the database, so it belongs in Mutations. deleteUser needs an ID and will return the type of object it deletes… type User.

Put this code in your schema.graphql

// ... type Query {...}
type Mutation {
  createUser(email: String!, firstName: String!): User
  deleteUser(userId: ID!): User
}

Note: userId is just a parameter name. You could name it elephant, but that’d be confusing.

Now let’s make the resolver. Inside the “Mutation” root type, add the deleteUser function in src/index.js:

Mutation: {
  //... createUser: (...){...},
  deleteUser: (parent, args, context, info) => {
    return context.prisma.user.delete({
      where: { id: parseInt(args.userId) },
    });
  },

In the above code we run the delete() operation where the user’s id is equal to the userId passed in as an argument (args.userId), and return the deleted User. Returning the deleted User is nice because you can do stuff to the user. (like say “John has been destroyed!” in the UI) We’re using parseInt to make sure GraphQL doesn’t complain about ID being the wrong type. Adjust as necessary.

Ok now open a new tab in the GraphQL playground and enter the following Mutation to delete a user:

mutation {
  deleteUser(userId: 1) {
    id
    firstName
    email
  }
}

Challenge Time: See if you can delete a user by passing in the userId via Query variables at the bottom tab in the playground.

Update Mutations in GraphQL

Update mutations are just about the same thing, but we’re going to do it a little different to show you various ways of setting things up. Based on what we know now, you COULD do something like the following:

// Example only. We're not using this code.
type Mutation {
// other stuff
  updateUser(userId: ID!, firstName: String, email: String): User
}
// bla bla

const resolvers = {
// other stuff
  Mutation: {
    updateUser: (parent, args, context, info) => {
      console.log(args);
      return context.prisma.user.update({
        where: {
          id: parseInt(args.userId),
        },
        data: { firstName: args.firstName, email: args.email },
      });
  }
}

Interestingly the above code works. Both the firstName and email are optional parameters. I originally thought that leaving a parameter blank would cause that data to be updated as a blank field, but somewhere in the code it’s smart enough to only update if the field is populated. I’m too lazy to figure it out, but I suspect it’s an internal thing where JS drops the key value pair if the value is blank, OR Prisma recognizes a blank parameter and ignores it.

Despite the fact the above code works. Let’s try something different.

Input Types in GraphQL

Input types allow you to reuse bits of code in your Schema. If you find yourself re-using the same input parameters over and over you can stick them in an Input type and reuse it. They currently only work with primitive data types though. Edit your schema.graphql with the following changes:

type Mutation { 
  createUser(email: String!, firstName: String!): User
  deleteUser(userId: ID!): User
  updateUser(userId: ID!, input: UserInput!): User
}
input UserInput {
  firstName: String
  email: String
}

Now we can package all the User input fields into a nice little package. I left the id outside because it’s a required field and it’s used to identify the user rather than be part of the update. Do it however you want.

and in our const resolvers in index.js add the updateUser mutation:

Mutation: {
    updateUser: (parent, args, context, info) => {
      return context.prisma.user.update({
        where: {
          id: parseInt(args.userId),
        },
        data: { ...args.input },
      });
    },
    createUser: // ....

The above code now uses the single argument args.input to populate the data fields that are to be updated by Prisma. It does so with destructuring the input parameters into the data: { } value.

Ok we’re ready to restart the server and try an update. Run the below query in the playground (obviously find a userId that works):

mutation {
  updateUser(userId: 2, input: {firstName: "Jolene", email: "jolene@jolene.com"}) {
    id
    email
    firstName
  }
}

Because we’re passing in a parameter named “input” with the type of “UserInput” the argument has to look like UserInput.

Let’s Refactor and clean up a little

First let’s delete the hard coded array from a few tutorials back:

// Delete this
let users = [
  {
    id: "123",
    firstName: "Cindy",
    email: "cindy@cindy.com",
  },
  {
    id: "456",
    firstName: "Todd",
    email: "todd@todd.com",
  },
];

Also remove the helloWorld string in the schema.graphql , which looks like the line below:

  helloWorld: String!

Also remove the helloWorld Query in the resolvers:

helloWorld: () => `Hello World! What a day!`,

and edit the existing “user” function to grab data from the real database instead of our hard coded array. The way to find a single user is almost identical to how we delete a single user. Here’s the code to find by User id:

const resolvers = {
  Query: {
    users: async (parent, args, context, info) => {
      console.log(context);
      return context.prisma.user.findMany();
    },
    user: (parent, args, context, info) => {
      return context.prisma.user.findOne({
        where: {
          id: parseInt(args.userId),
        },
      });
    },
//...

We’ll move the resolvers into their own files later on. Let’s stop here, and do a quick Mutations Part Two for UpdateMany and DeleteMany.

The final code for this tutorial can be found in the done-mutations branch on Github

Source code for completed version of this part coming soon.

Want More Tutorials?

Get our best crash courses and tutorials

(Source Code Provided)

Leave a Comment

Your email address will not be published. Required fields are marked *

want more details?

Fill in your details and we'll be in touch

%d bloggers like this: