简体   繁体   中英

GraphQL (Apollo) how to pass datastructure in parameter?

I'd like to make a query with lot of input parameter:

 apollo_client.mutate({mutation: gql
  `mutation mutation($title: String!, $caption: String!, $id: Int!){
    mutatePmaHome(pmaData: {id:$id, title: $title, caption: $caption}) {
      pma{
        title
        caption
      }
    }
  }`,
  variables: {
    title: this.state.title,
    caption: this.state.caption,
    id: this.state.id
  },
}).then(console.log);

This is how look my query when I pass simple datastructure (Int, String) But my question is: How can I pass a dictionnary or object in the mutation query. I don't have any idea how can I type it.

The doc from graphQL say I must create an input object. But here I'm on js with apollo, so how can I pass a dictionnary on my query in order to not type all data (here 3, but it can be 10 to 20). I'd like a cleaner approach with an input type field, but I can't find a single example.

Thanks

The way I see it, there are two approaches you can take.

The safer approach is to create a type containing a key and a value. Then, your dictionary would be a list of keys and values. Instead of accessing a property as let value = dictionary[key] , you would then need to use let entry = dictionary.find((entry) => entry.key === key); let value = entry && entry.value let entry = dictionary.find((entry) => entry.key === key); let value = entry && entry.value .

// Before
{
  red: 1,
  blue: 2,
  green: 3,
}

// After
[
  {key: 'red', value: 1},
  {key: 'blue', value: 2},
  {key: 'green', value: 3},
]

Typedefs:

type IntegerProperty {
    key: ID!
    value: Int
}

type IntegerDictionary {
    values: [IntegerProperty!]!
}

A quicker but less typesafe approach is to use something like graphql-type-json or graphql-json-object-type . These permit you to include arbitrary JSON in your GraphQL query; however, you then lose the guarantees that GraphQL makes on the format of your data.

Swift 5.1, Apollo 0.21.0

The keys and values in your dictionary need to adhere to the Apollo JSONEncodable protocol:

public protocol JSONEncodable: GraphQLInputValue {
  var jsonValue: JSONValue { get }
}

You need to loop through your Dictionary and return each Object with a .jsonValue (JSONEncodable protocol).

[String : Any?] vs [String : String]

If you pass a Dictionary of [String : String] into Apollo, it will automagically work because String conforms to the JSONEncodable protocol. Both key and value are type String.

JSON is usually represented in Swift as [String : Any?] which means the key must be String, but the value can by Any object (Array, Bool, Double, Null, String, Dictionary).

Because Apollo doesn't know what the Any object is, it will cause a SIGABRT. This is because the value could be a custom class you wrote that is not JSON compatible.

You must cast the Any object to a class that conforms to the JSONEncodable protocol.

Since [String : Any?] by default cannot define the Any objects, the Generic JSON library does this by creating a new class to represent the JSON data.

The example below extends the JSONEncodable protocol to the GenericJSON class to ensure the value adheres to the JSONEncodable protocol Apollo requires for a mutation.

Building a Dictionary that adheres to the JSONEncodable Protocol

  1. Add the Generic JSON library to your pod file:

https://github.com/zoul/generic-json-swift

pod 'GenericJSON'

  1. Import the GenericJSON library and create an alias for your custom JSON GraphQL scalar in some ApolloExtensions.swift file. This alias will map to the GenericJSON library:
import GenericJSON

// CUSTOM JSON SCALAR

public typealias MyJsonScalar = JSON
  1. In the ApolloExtensions.swift file, add a JSONEncodable extension for the GenericJSON JSON:
extension JSON: JSONEncodable {

    public var jsonValue: JSONValue {

        if self.objectValue != nil {

            return jsonObject as JSONObject

        }

        if self.arrayValue != nil {

            var array : Array<JSONEncodable> = []

            for obj in self.arrayValue! {

                if obj.arrayValue != nil {

                    array.append(obj.jsonValue as! Array<JSONEncodable>)

                } else if obj.objectValue != nil {

                    array.append(obj.jsonValue as! JSONObject)

                } else {

                    array.append(obj.jsonValue as! JSONEncodable)

                }

            }

            return array as Array<JSONEncodable>

        }

        if self.stringValue != nil {

            return self.stringValue! as String

        }

        if self.doubleValue != nil {

            return self.doubleValue! as Double

        }

        if self.boolValue != nil {

            return self.boolValue! as Bool

        }

        if self.isNull {

            return "" as String

        }

        return "" as String

    }

    public var jsonObject: JSONObject {

        var jsonObject : JSONObject = JSONObject(minimumCapacity: self.objectValue!.count)

        for (key, value) in self.objectValue! {

            if value.arrayValue != nil {

                jsonObject[key] = value.jsonValue as! Array<JSONEncodable>

            } else if value.objectValue != nil {

                jsonObject[key] = value.jsonValue as! JSONObject

            } else {

                jsonObject[key] = value.jsonValue as! JSONEncodable

            }

        }

        return jsonObject

    }

}
  1. Create a JSON object from your dictionary and pass it into your GraphQL mutation:
func createJSONDictionary() {

    let myDictionary : [String: Any?] = ["foo" : "foo", "bar" : 2]

    do {

        let jsonData : Data = try JSONSerialization.data(withJSONObject: myDictionary, options: [])

        if let jsonObject = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [String : Any?] {

            let json: JSON = try JSON(jsonObject)

            self.myGraphQLMutation(json: json)

        } else {

            // casting error

        }

    } catch {

        // json error

    }

}

func myGraphQLMutation(json: JSON) {

    // apollo

    let apollo : ApolloClient = ApolloHelper.shared.client

    // myMutation

    let myMutation = MyMutation(json: json)

    // perform

    apollo.perform(mutation: myMutation, queue: DispatchQueue.global()) { result in

        switch result {

            case .success(let graphQLResult):

                // Deal with GraphQLResult and its data and/or errors properties here

                break

            case .failure(let error):

                // deal with network errors here

                return

        }

    }

}

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