Archived student lesson

Add Update mutations

Part of End to End React with Prisma 2

Objective: Make mutations which will allow us to update feeds and bundles.

Now that we can create feeds/bundles and search for them to our hearts content, we now need to support updating them. We will do this with 2 new mutations: updateFeed and updateBundle.

utils/api/resolvers.ts

import { verifyOwnership } from './verifyOwnership'

const resolvers = {
  // other definitions

  Mutation: {
    // many other mutations
    updateFeed: async (
      parent,
      { data: { id, ...feedUpdate } },
      { prisma, user }
    ) => {
      const feed = await prisma.feed.findOne({
        where: { id },
        include: { author: true },
      })
      await verifyOwnership(feed, user)
      return prisma.feed.update({ where: { id }, data: { ...feedUpdate } })
    },
    updateBundle: async (
      parent,
      { data: { id, ...bundleUpdate } },
      { prisma, user }
    ) => {
      const bundle = await prisma.bundle.findOne({
        where: { id },
        include: { author: true },
      })
      await verifyOwnership(bundle, user)
      return prisma.bundle.update({ where: { id }, data: { ...bundleUpdate } })
    },
  },
}

How these work is first we will look up the feed or bundle. We then will call this function verifyOwnership which will check whether the item’s user matches the current user. We only want a user to be able to mess around with their own items so in the event that someone made a nefarious API call to the backend where they tried to modify someone else’s item, we will kick them out of the mutation before any changes are made.

Create the following function which does the checking- note the throw new Error is what actually will kick us out of this resolver in the event that the users don’t match.

utils/api/verifyOwnership.ts

export const verifyOwnership = (item, user) => {
  const { author } = item
  if (author.auth0 !== user.auth0) {
    throw new Error('Access denied, user does not own this item.')
  }
}

Turning to the type definitions now, we will add the updateFeed and updateBundle to the mutations section and create a set of types centered around the FeedUpdateInput and BundleUpdateInput. Since these types have tags and feeds nested inside the update, we will also need to create types for those to allow us to create new tags/feeds, connect to already existing ones and disconnect from ones we don’t want associated with that particular item anymore.

utils/api/typeDefs.ts

  input FeedUpdateInput {
    id: String
    url: String
    name: String
    tags: NestedFeedTagUpdateInput
  }
  input NestedFeedTagUpdateInput {
    create: [FeedTagCreateInput]
    connect: [FeedTagWhereUniqueInput]
    disconnect: [FeedTagWhereUniqueInput]
  }
  input BundleUpdateInput {
    id: String
    name: String
    description: String
    tags: NestedBundleTagUpdateInput
    feeds: NestedBundleFeedUpdateInput
  }
  input NestedBundleTagUpdateInput {
    create: [BundleTagCreateInput]
    connect: [BundleTagWhereUniqueInput]
    disconnect: [BundleTagWhereUniqueInput]
  }
  input NestedBundleFeedUpdateInput {
    create: [FeedCreateInput]
    connect: [FeedWhereUniqueInput]
    disconnect: [FeedWhereUniqueInput]
  }

  # other definitions

  type Mutation {
    # many other mutations
    updateBundle(data: BundleUpdateInput): Bundle
    updateFeed(data: FeedUpdateInput): Feed
}

We can verify that the updateBundle mutation works by running this mutation:

mutation updateBundleMutation($data: BundleUpdateInput) {
  updateBundle(data: $data) {
    ...BundleFragment
    feeds {
      ...FeedFragment
      bundles {
        ...BundleFragment
      }
    }
  }
}
fragment FeedFragment on Feed {
  id
  name
  url
  likes {
    ...AuthorFragment
  }
  tags {
    ...FeedTagFragment
  }
  author {
    ...AuthorFragment
  }
}
fragment BundleFragment on Bundle {
  id
  name
  description
  tags {
    ...BundleTagFragment
  }
  author {
    ...AuthorFragment
  }
  likes {
    ...AuthorFragment
  }
}
fragment AuthorFragment on User {
  id
  auth0
  picture
  nickname
}
fragment BundleTagFragment on BundleTag {
  id
  name
}
fragment FeedTagFragment on FeedTag {
  id
  name
}

with the following parameters:

{
  "data": {
    "id": "10",
    "name": "Updated Bundle Title"
  }
}

We should see that the name of the bundle has now been updated. You can also run this on the updateFeed mutation and confirm that it updates there too.