How to return both errors and data with Apollo server? - apollo-server

I know Apollo can return a combination of errors and data, the client documentation even covers this: and I believe this can happen automatically when field resolvers run into issues, but how can I manually return mixed payloads? I have use cases in my project where users will send large amounts of data to mutate in the form of an input array. Some members of the array might be malformed and I want to throw validation errors for those but I still want the other changes to go through. An example pseudo request and response pair would be:
// input
{
mutateEntities: [
entityAMutationInput,
entityBMutationInput,
entityCMutationInput,
entityDMutationInput,
]
}
// response
{
data: {
mutateEntities: [
entityAMutationPayload,
entityBMutationPayload,
]
},
// members of the error array seem to refer to individual GQL operations in Apollo so
// there would only be one here
errors: [{
...errorMetadata
extensions: {
validationErrors: [
entityCValidationError,
entityDValidationError
]
}
}]
}
I realise that I have the option of defining errors as a GQL type and including them as part of the payload data but our FE team already has error handling that expects errors to appear in payload.errors and not, for example, payload.data.validationErrors or some other custom field. I want my errors to appear in payload.errors.extensions.validationErrors to minimise the impact on the front end. I know how to do this if I throw a custom error but, if I throw errors, then I won't have any data under payload.data.mutateEntities.

Related

How should I reference grandparent properties in a GraphQL query, where I don't define the intermediate resolver?

I am building a GraphQL Schema that has a Type Pod which contains 3 nested objects.
type Pod {
metadata: Metadata
spec: Spec
status: Status
}
My data source is an external API which returns an array of this data. In fact, I defined my schema around this API response. I have included a trimmed down version below.
[
{
"metadata": {
"name": "my-app-65",
"namespace": "default",
},
"spec": {
"containers": [
{
"name": "hello-world",
"image": "container-image.io/unique-id",
}
],
},
// more like-objects in the array
]
However, for each of these container objects, inside the array. I would like to add some extra information which this initial API call does not provide. However, I can query this information separately if I provide the name & namespace properties on the parent's metadata.
/container/endpoint/${namespace}/${name}
Returns...
[ {
name: "hello-world",
nestObj: {
//data
}
},
]
And I would like to add this nested Obj to the original space when that data is queried.
However, I don't have a clean way to access the pod.metadata.name inside the resolver for Container.
Currently my resolvers look like....
Query: {
pods: async () => {
//query that returns array of pod objects
return pods
}
},
Container: {
name: "hello-world",
nestedObj: async (parent, args, context, info) => {
//query that hits the second endpoint but requires name & namespace.
//however, I don't have access to those values.
}
}
Perfect world solution: I could access parent.parent.metadata.name inside the Container Resolver
Current Approach (brute force, repetitively add the property to the children)
Loop through every nested container objs in every Pod and add the podName & namespace as properties there.
pods.forEach(pod => pod.spec.containers.forEach(container => {
container.podName = pod.metadata.name;
container.namespace = pod.metadata.namespace;
}))
This feels very much like a hack, and really bogs down my query times. Especially considering this data won't always be requested.
I have two intuitive ideas but don't know how to implement them (I'm a bit new to graphQL).
Implement a Pod resolver that would then pass this data in through the rootValue as described here: https://github.com/graphql/graphql-js/issues/1098
Access it somewhere inside the info object
The problem the first one, is that my data source only sends me the data as an array, not individual pods and I'm unsure how to pass that array of data into resolvers for individual components.
The problem with second, is that object is very dense. I tried accessing it via the path. But path seems to only store the type, not the actual data.
It's also possible I'm just implementing this completely wrong and welcome such feedback.
Thanks for any guidance, suggestions, or resources.

In Relay.js, what is the `Client Mutation Identifier`?

In the relay documentation here, it says that:
Relay uses a common pattern for mutations, where there are root fields on the mutation type with a single argument, input, and where the input and output both contain a client mutation identifier used to reconcile requests and responses.
But in the example they provided, the input and output looked like this respectively:
// IntroducedShipInput
{
"input": {
"shipName": "B-Wing",
"factionId": "1"
}
}
// IntroducedShipPayload
{
"introduceShip": {
"ship": {
"id": "U2hpcDo5",
"name": "B-Wing"
},
"faction": {
"name": "Alliance to Restore the Republic"
}
}
}
So what is the client mutation identifier? And why, and how does it get used to reconcile requests and responses?
I'm still not 100% sure what exactly happened to the "client mutation identifier," but having done some research, it appears to have been a requirement in previous versions of Relay. This PR apparently removed the requirement by replacing it with some other mechanism, but it's not clear to me what that other mechanism does. I left a comment requesting more clarification around the documentation, which appears to be out of date.
At any rate, the client mutation identifier appears to have been related to some assumptions about mutation idempotency in Facebook's implementation of GraphQL.

What is query_hash in instagram?

I was working for the first time on graphql, and I saw that Instagram hash their queries.
I searched something, but I don't know if it is correct. The hash is like a persistedquery stored in a cache memory?
Or am I wrong?
Example: this is my request payload
{
"operationName":"user",
"variables":{},
"query":"query user {\n users {\n username\n createdAt\n _id\n }\n}\n"
}
this is instagram:
query_hash: 60b755363b5c230111347a7a4e242001
variables: %7B%22only_stories%22%3Atrue%7D
(it is in urlencode mode).
Now, how could I hash my query? I'm using NodeJS as backend and react js as frontend.
I would like to understand how it works x)! Thank you guys!
The persisted query is used to improve GraphQL network performance by reducing the request size.
Instead of sending a full query which could be very long, you send a hash to the GraphQL server which will retrieve the full query from the key-value store using the hash as the key.
The key value store can be memcached, redis, etc
The Apollo Server comes with automated persisted queries out of the box. I recommended gives it a try. They have publish a blog about it. https://blog.apollographql.com/automatic-persisted-queries-and-cdn-caching-with-apollo-server-2-0-bf42b3a313de
If you want to build your own solution, you can use this package to do the hashing yourself https://www.npmjs.com/package/hash.js
query_hash (or query_id) does not hash the variables or the parameters, it hashes the payload.
Lets say your actual path is /graphql and your payload is
{
"user": {
"profile": [
"username",
"user_id",
"profile_picture"
],
"feed": {
"posts": {
"data": [
"image_url"
],
"page_size": "{{variables.max_count}}"
}
}
}
}
Then this graphql payload will be hashed and it becomes d4d88dc1500312af6f937f7b804c68c3. Now instead of doing that on /graphql you do /graphql/query/?query_hash=d4d88dc1500312af6f937f7b804c68c3. This way you hashed the payload, as in you hashed the "keys" that are required from the graphql. So when you pass variables as a param then the payload does not actually change, because the variables are constant as well, and you are changing them on the backend, and not in the payload.

How to check jsonPath for value to expect on different property

I have error messages that comes back in the following structure:
[{
"detail": "must be unique",
"source": {
"pointer": "name"
}
}]
I've omitted unnecessary bits for the sake of this example. In my test, I can do the following:
mockMvc.perform(post(URL)
.contentType(contentType)
.content(this.json(brand)))
.andExpect(status().isUnprocessableEntity())
.andExpect(jsonPath("$.[0].detail", is("must be unique")));
However, if I am testing that I get multiple errors back, $.[0].detail isn't always Name. So I need to first check what the $.[0].source.pointer is and then validate the detail message. I'm just unsure on how to go about doing that.

How to handle (failed) validation when creating multiple entities with a REST API

Let's say I have an endpoint user/1/results and I want to upload multiple results at a time.
So I send it JSON like:
{
"data": [
{
"date": "2014-02-14 03:15:41",
"score": 18649,
"time": 42892
},
{
"date": "2013-11-18 09:21:46",
"score": 7856,
"time": 23568.8
}]
}
Let's say time needs to be an integer, so the second entity fails validation.
What's the best thing to do:
Fail both, nothing saves, respond with error message..
Save first entity, respond with error message.
In either case, what would an error message look like? i.e. how/does it specify that it's the second entity that fails validation.
I think you should fail both and respond with an error message because it might be cumbersome again to track the remaining results.
Error message should give the details of failing location. for example if it fails at the second one then specify it in json response.

Resources