简体   繁体   中英

Building a nested structure in graphql

I'm pretty new to GraphQL and I'm trying to solve a problem that my manager has presented to me.

I have the following data structure made available to me, via a 3rd party API (Which I have zero control over):

[
  {
    "id": 19,
    "date": "2016-10-24T13:59:19",
    "date_gmt": "2016-10-24T12:59:19",
    "slug: "data",
    "provider": {
      "name": "data",
      "logo": "data",
      "destination_url": "data",
      "coupon_label": "data",
      "coupon_text": "data",
      "coupon_code": "data",
      "coupon_url": "data",
    }
  }
]

I need to turn it into a GraphQL schema which looks like the following query:

{
  provider(slug: "slug") {
    id
    date
    slug
    name
    logo
    url
    coupon {
      label
      text
      code
      url
    }
  }
}

I've managed to sort most of it out with the code below, however, I can't work out how to group the coupon nodes into one.

I am guessing this need to be another custom type? If so this seems inefficient as coupon will never be used outside of the provider type so I wanted to know if there was a more 'best practice' way of doing it that I'm not aware of.

import { GraphQLObjectType, GraphQLInt, GraphQLString } from 'graphql'

const ProviderType = new GraphQLObjectType({
  name: 'Provider',
  fields: () => ({
    id: {
      type: GraphQLInt,
      description: 'The primary key for the provider'
    },
    slug: {
      type: GraphQLString,
      description: 'A unique string for the provider'
    },
    status: {
      type: GraphQLString,
      description: 'The the published status of the provider'
    },
    name: {
      type: GraphQLString,
      description: 'The name of the provider',
      resolve (parent) { return parent.provider.name }
    },
    logo: {
      type: GraphQLString,
      description: 'The full url of the provider logo',
      resolve (parent) { return parent.provider.logo }
    },
    url: {
      type: GraphQLString,
      description: 'The full url of the provider',
      resolve (parent) { return parent.provider.destination_url }
    },
  })
})

export default ProviderType

Update:

I've updated the code to the following but it's still not working, so my assumption must have been incorrect (or I implemented it incorrectly)

const ProviderType = new GraphQLObjectType({
  name: 'Provider',
  fields: () => ({
    id: {
      type: GraphQLInt,
      description: 'The primary key for the provider'
    },
    slug: {
      type: GraphQLString,
      description: 'A unique string for the provider'
    },
    status: {
      type: GraphQLString,
      description: 'The the published status of the provider'
    },
    name: {
      type: GraphQLString,
      description: 'The name of the provider',
      resolve (parent) { return parent.provider.name }
    },
    logo: {
      type: GraphQLString,
      description: 'The full url of the provider logo',
      resolve (parent) { return parent.provider.logo }
    },
    url: {
      type: GraphQLString,
      description: 'The full url of the provider',
      resolve (parent) { return parent.provider.destination_url }
    },
    coupon: {
      type: CouponType,
      description: 'The coupon information for the provider'
    }
  })
})

const CouponType = new GraphQLObjectType({
  name: 'Coupon',
  fields: () => ({
    label: {
      type: GraphQLString,
      description: 'The label for the coupon',
      resolve (parent) { return parent.provider.coupon_label }
    },
    text: {
      type: GraphQLString,
      description: 'The text for the coupon',
      resolve (parent) { return parent.provider.coupon_text }
    },
    code: {
      type: GraphQLString,
      description: 'The code for the coupon',
      resolve (parent) { return parent.provider.coupon_code }
    },
    url: {
      type: GraphQLString,
      description: 'The url for the coupon',
      resolve (parent) { return parent.provider.coupon_url }
    }
  })
})

Your schema is mostly correct, but you need a resolver on your coupon field in provider because it is a nested type. See launchpad example for interactive query https://launchpad.graphql.com/r995kzj5kn

and here is the code. I have removed your descriptions for brevity and added some test data

import {
  GraphQLObjectType,
  GraphQLSchema,
  GraphQLString,
  GraphQLInt,
  GraphQLList
} from 'graphql'

const data = [
  {
    "id": 19,
    "date": "2016-10-24T13:59:19",
    "date_gmt": "2016-10-24T12:59:19",
    "slug": "slug",
    "provider": {
      "name": "provider.name",
      "logo": "provider.logo",
      "destination_url": "provider.destination_url",
      "coupon_label": "provider.coupon_label",
      "coupon_text": "provider.coupon_text",
      "coupon_code": "provider.coupon_code",
      "coupon_url": "provider.coupon_url",
    }
  },
    {
    "id": 20,
    "date": "2016-10-24T13:59:19",
    "date_gmt": "2016-10-24T12:59:19",
    "slug": "slugplug",
    "provider": {
      "name": "provider.name",
      "logo": "provider.logo",
      "destination_url": "provider.destination_url",
      "coupon_label": "provider.coupon_label",
      "coupon_text": "provider.coupon_text",
      "coupon_code": "provider.coupon_code",
      "coupon_url": "provider.coupon_url",
    }
  }
]

const CouponType = new GraphQLObjectType({
  name: 'Coupon',
  fields: () => ({
    label: {
      type: GraphQLString,
      resolve (parent) { return parent.provider.coupon_label }
    },
    text: {
      type: GraphQLString,
      resolve (parent) { return parent.provider.coupon_text }
    },
    code: {
      type: GraphQLString,
      resolve (parent) { return parent.provider.coupon_code }
    },
    url: {
      type: GraphQLString,
      resolve (parent) { return parent.provider.coupon_url }
    }
  })
})

const ProviderType = new GraphQLObjectType({
  name: 'Provider',
  fields: () => ({
    id: { type: GraphQLInt },
    date: { type: GraphQLString },
    slug: { type: GraphQLString },
    status: { type: GraphQLString },
    name: {
      type: GraphQLString,
      resolve (parent) { return parent.provider.name }
    },
    logo: {
      type: GraphQLString,
      resolve (parent) { return parent.provider.logo }
    },
    url: {
      type: GraphQLString,
      resolve (parent) { return parent.provider.destination_url }
    },
    coupon: {
      type: CouponType,
      resolve(parent) {
        return parent
      }
    }
  })
})

const Query = new GraphQLObjectType({
  name: 'Query',
  fields: {
    provider: {
      type: new GraphQLList(ProviderType),
      args: {
        slug: { type: GraphQLString }
      },
      resolve (source, args) {
        return args.slug ?
          data.filter(({ slug }) => slug === args.slug) :
          data
      }
    }
  }
})

const schema = new GraphQLSchema({
  query: Query
});

alternately you can just modify the results in the root resolver before sending them down like the following. this would allow you to remove all the resolvers from your types except for coupon on provider which would just return parent.coupon

const Query = new GraphQLObjectType({
  name: 'Query',
  fields: {
    provider: {
      type: new GraphQLList(ProviderType),
      args: {
        slug: { type: GraphQLString }
      },
      resolve (source, args) {
        const filtered = args.slug ?
          data.filter(({ slug }) => slug === args.slug) :
          data
        return filtered.map(doc => {
          return {
            id: doc.id,
            date: doc.date,
            slug: doc.slug,
            name: doc.provider.name,
            logo: doc.provider.logo,
            url: doc.provider.coupon_url,
            coupon: {
              label: doc.provider.coupon_label,
              text: doc.provider.coupon_text,
              code: doc.provider.coupon_code,
              url: doc.provider.coupon_url
            }
          }
        })
      }
    }
  }
})

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM