Build and Deploy a GraphQL API to the Edge with MySQL and PlanetScale — Part 6

Build and Deploy a GraphQL API to the Edge with MySQL and PlanetScale — Part 6

productDelete mutation

Now let's create the delete mutation resolver. We will first update the Grafbase Configuration to include the mutation productDelete.

This mutation uses the ProductByInput input type we created above for the updateProduct mutation. This mutation will also return a new type — ProdudctDeletePayload has a deleted field that returns a boolean to represent the success of the deletion request.

const productDeletePayload = g.type('ProductDeletePayload', {
  deleted: g.boolean()
})

g.mutation('productDelete', {
  args: {
    by: g.inputRef(productByInput)
  },
  resolver: 'products/delete',
  returns: g.ref(productDeletePayload).optional()
})

Next, create the file grafbase/resolvers/products/delete.ts and add the following:

import { connect } from '@planetscale/database'
import { GraphQLError } from 'graphql'
import { config, options } from '../../lib'

const conn = connect(config)

export default async function ProductsDelete(_, { by }) {
  let statement: string = ''
  let params: (string | number | boolean | {})[] = []
}

Here we import the necessary PlanetScale connection config, establish the connection and export a resolver function for ProductsDelete that contains the following variables:

  • statement — the SQL statement that will be executed

  • params — the SQL values (id or slug) that will be used

Inside the function ProductsDelete add the following:

Object.entries(by).forEach(([field, value]) => {
  if (
    value !== undefined &&
    value !== null &&
    (typeof value === 'string' || typeof value === 'number')
  ) {
    statement = `DELETE FROM Products WHERE ${field} = ?`
    params = [value]
  }
})

if (!statement) {
  throw new GraphQLError('ID or Slug must be provided')
}

This mostly follows the same patterns as above. If you were to use an ORM like Prisma or Drizzle the code wouldn't look so verbose.

Now we're ready to use the statement and params to execute against the database:

try {
  const results = await conn.execute(statement, params, options)

  if (results.rowsAffected === 1) {
    return { deleted: true }
  }

  return { deleted: false }
} catch (error) {
  return { deleted: false }
}

With that added, you should now have a delete.ts that looks something like this:

import { connect } from '@planetscale/database'
import { GraphQLError } from 'graphql'
import { config, options } from '../../lib'

const conn = connect(config)

export default async function ProductsDelete(_, { by }) {
  let statement: string = ''
  let params: (string | number | boolean | {})[] = []

  Object.entries(by).forEach(([field, value]) => {
    if (
      value !== undefined &&
      value !== null &&
      (typeof value === 'string' || typeof value === 'number')
    ) {
      statement = `DELETE FROM Products WHERE ${field} = ?`
      params = [value]
    }
  })

  if (!statement) {
    throw new GraphQLError('ID or Slug must be provided')
  }

  try {
    const results = await conn.execute(statement, params, options)

    if (results.rowsAffected === 1) {
      return { deleted: true }
    }

    return { deleted: false }
  } catch (error) {
    return { deleted: false }
  }
}

Now open Pathfinder and execute the following mutation:

mutation {
  productDelete(by: { id: "7" }) {
    deleted
  }
}

Remember to use a valid id or slug.

👉 Continue to Part 7