miragejs / graphql

A library for handling GraphQL requests with Mirage JS
MIT License
74 stars 12 forks source link

When updating data, an error occurs if there is a relationship #73

Open nomin-sasabuchi opened 8 months ago

nomin-sasabuchi commented 8 months ago

Prerequisites

Operation has been confirmed for data acquisition, creation, and updating when there is no relationship

Problem

If you request an update for a schema that has the following relationships, the following error will occur. Why? If I exclude the related property (status: ScheduleState!), it works, so I think it's a problem with the relation. Do I need to write my own resolver in such a case?

Error: Cannot return null for non-nullable field Schedule.status.: {"response":{"errors":[{"message":"Cannot return null for non-nullable field Schedule.status.","locations":[{"line":11,"column":5}],"path":["updateSchedule","status"]}],"data":{"updateSchedule":null},

Code

//schema
type Mutation {
  updateSchedule(id: ID!,params: UpdateScheduleInput): Schedule
}

type Schedule {
  id: ID!
  startAt: DateTime
  endAt: DateTime
  status: ScheduleState!
}

type ScheduleState {
  id: ID!
  name: String!
}

input UpdateScheduleInput {
  id: ID!
  startAt: DateTime
  endAt: DateTime
  statusId: ID
}

//query
export const updateSchedule = gql`
  mutation updateSchedule($id: ID!, $params: UpdateScheduleInput) {
    updateSchedule(id: $id, params: $params) {
      id
      startAt
      endAt
      status {
        id
        name
      }
    }
  }
`

Tried

By the way, I tried the following resolver, but it didn't work

import {
  createGraphQLHandler,
  mirageGraphQLFieldResolver,
} from '@miragejs/graphql'
import { createServer } from 'miragejs'

import schema from './generated-mock-schema'

export default createServer({
  seeds(server) {
    const status0 = server.create('ScheduleState', {
      id: '0',
      name: 'making',
    })
    const status1 = server.create('ScheduleState', {
      id: '1',
      name: 'confirm',
    })
    server.create('Schedule', {
      id: '1',
      startAt: '2023-12-01 11:05:09',
      endAt: '2023-12-01 13:05:09',
      status: status0,
    })
    server.create('Schedule', {
      id: '2',
      startAt: '2023-12-01 13:05:09',
      endAt: '2023-12-01 15:05:09',
      status: status1,
    })
  },
  routes() {
    const resolvers = {
      Mutation: {
        updateSchedule(obj, args, context, info) {
          const { params } = args

          const schedules = context.mirageSchema.db.schedules.find(args.id)
          const statusData = context.mirageSchema.db.scheduleStates.find(
            schedules.statusId
          )
          const records = {
            ...schedules,
           status: statusData,
            ...params,
          }
          return context.mirageSchema.db.schedules.update(args.id, records)
        },
      },
    }
    this.post(
      import.meta.env.VITE_SCHEDULE_API_URL,
      // @ts-ignore
      createGraphQLHandler(schema, this.schema, { resolvers })
    )
  },
})
jneurock commented 8 months ago

You’re definitely on the right track with the resolver. Mirage GraphQL tries to auto-resolve certain queries, but it’s pretty limited, and in your case the auto-resolution falls short.

It’s not obvious, but you’ll need to decorate the records from Mirage a bit to satisfy what GraphQL expects. For example, each object in the response needs to have a __typename field.

You can import a function from Mirage GraphQL that will decorate the records accordingly:

import { adaptRecords } from '@miragejs/graphql/orm/records';

// ...

export default createServer({
  routes() {
    const resolvers = {
      Mutation: {
        updateSchedule(obj, args, context, info) {
          // ...
          return adaptRecords(context.mirageSchema.db.schedules.update(args.id, records));
        }
      }
    };

    // ...
  },
});