Avoid call to a nested resolver if all the response fields have been already fulfilled - graphql

I have a schema that receive a timeInterval iput on root query and the pass it to nested levels/resolvers. I'm trying to add validation to that input on root level, so if the validation fails then I should return and error and return null for other fields. The issue is that if I do this on root level then I don't know how to avoid graphql-tools calling nested resolvers (which fails because they don't have the timeInterval variable defined in the obj of each resolver).
Let me know know if you need and schema example and more details, thanks!

This is built in to how GraphQL.js, the reference implementation from Facebook, works. GraphQL-Tools is just a library on top that makes writing resolvers and schemas a bit nicer.
In GraphQL.js, the child resolvers are called whenever the parent resolver returns anything other than null or undefined, or if it throws an error.
So it sounds like in your case you are returning some data but with a single field missing, in which case GraphQL.js has no idea that it should avoid calling nested fields. Having more detail about your schema and resolvers would definitely help me come up with a specific solution.

Related

Will graphql run resolver with additional arguments

If graphql query schema is like this:
user(user_id: Int): User
Will apollo run resolver if in query will be additional (email) argument not defined in query schema?
I want iterate arguments in resolver, but not sure if it possible to pollute them.
P.S. If there is documentation of how apollo parcing arguments, will appreciate.
In GraphQL, a schema defines what fields are available to the client, including what arguments are available for that field and what the types of those arguments are. Any query submitted to a GraphQL service will first be validated before it's executed. If the query includes any extraneous arguments, it will fail validation and won't be executed. This is explained here in the spec:
Formal Specification
For each argument in the document
Let argumentName be the Name of argument.
Let argumentDefinition be the argument definition provided by the parent field or definition named argumentName.
argumentDefinition must exist.
Explanatory Text
Every argument provided to a field or directive must be defined in the set of possible arguments of that field or directive.
For a better idea of how GraphQL works, I would suggest taking at least a cursory look through the spec.

GraphQL field-level validation in AppSync

I have an AppSync API that's mostly backed by a DynamoDB store. Most of the resolvers are hooked up directly to the DynamoDB sources, not using lambdas.
Some of the fields should have validation constraints, such as length or a regexp. In one particular case I would like to require that a state field contain an ISO 3166-2 value like US-NY. (GraphQL enums values can't contain hyphens, so that isn't an option here.)
Other than replacing some resolvers with lambdas, the only way I can think of to apply these sorts of validation rules is to do it in VTL in the RequestMappingTemplate. That would work, but it would be tedious and likely result in duplicate code. Are there alternatives?
Unfortunately, only way without lambda is VTL , I suggest that instead of writing validation directly inside RequestMappingTemplate, using pipeline resolver.(less duplicated)
Pipeline Resolvers contain one or more Functions which are executed in order.
Functions allow you to write common logic for reuse across multiple Resolvers in your schema. They are attached directly to a data source and like a Unit resolver, contain the same request and response mapping template format.
You can find a good example here.

Is it okay for a resolver to have side effects besides resolving the type?

When creating a GraphQL mutation or query, you usually retrieve or save data. But let's assume I would like to send an e-mail when the data is saved or perform some additional side effects.
Is it common practice for resolvers to have these kind of side effects? Since resolvers should only resolve data according to the SOLID principle, right?
If it turns out that the resolver should not have side effects like these, then where would the side effects belong?
Most tutorials and articles online, including the official tutorial of GraphQL itself, don't seem to cover this or take it into account.
Many thanks!
It depends on that resolver is resolving fields of what types.
If it is resolving the fields of the root mutation ,it must involve sides effects as mutation is supposed to modify server-side data. So it is okay to send an email in root mutation field 's resolver.
For the resolver of object type and root query fields , as it is supposed to only retrieving data, it is awkward for me if they have any side effects.

Why are there "two names" for each GraphQL query/mutation?

I am learning GraphQL and one basic point has me puzzled. I know there is an easy explanation, but I can't find it. Specifically, from the Apollo documentation (https://www.apollographql.com/docs/apollo-server/essentials/data.html#operation):
...it makes sense to name the operation in order to quickly identify
operations during debugging or to aggregate similar operations
together...Operations can be named by placing an identifier after the
query or mutation keyword, as we’ve done with HomeBookListing here:
query HomeBookListing {
getBooks {
title
}
}
If HomeBookListing is the name of the query, what, then, is getBooks? The name of the resolver?
Similarly, when you pass variables to a query, why are there "two levels" of parameters, like this
mutation HomeQuickAddBook($title: String, $author: String = "Anonymous") {
addBook(title: $title, author: $author) {
title
}
}
So, would $title: String, $author: String = "Anonymous" be the variables passed to the query, and title: $title, author: $author variables passed to the resolver?
Of course I can memorise the pattern, but I'm keen to understand, conceptually, what the different pieces are doing here. Any insights much appreciated!
You may find it helpful to review the spec, but what follows is a somewhat shorter explanation:
What is an operation?
There are three operations in GraphQL (query, mutation and subscription). Typically, a GraphQL request consists of only one of these three operations, and it forms the root of the request, or the entry point into the rest of the schema.
Each operation has a single object type associated with it. By convention, these types are named Query, Mutation and Subscription, but their naming is functionally irrelevant to your schema. Other than their association with a particular operation, there's nothing special about these object types -- each has a name, description and fields just like any other object type in your schema. Collectively, we call these three types root operation types.
In your example, the query root type has a field called getBooks. That field is resolved according to the same rules as any other field in your schema. The only special thing about this field is that it's at the root -- there is no "parent" field that was resolved before it.
Operation names are optional because they do not impact the data returned by the server -- they are there generally for debugging purposes (although some clients and tools use them to provide other features, so it's always good to have them). Specifying at least one field name for your root operation type, however, is necessary, otherwise your operation would not actually do anything (i.e. query the server for the data). Again, these fields are your entry point into the rest of the schema and the starting point for your data graph.
Ok, but what about the variables?
According to the spec:
Variables must be defined at the top of an operation and are in scope throughout the execution of that operation.
While we do not initialize a variable inside the document with a value, we do need to define it by telling GraphQL what the type of the variable it is. This allows GraphQL to then validate the usages of your variables throughout the document. For example, if you define a variable as a String and then attempt to use it at an input field that is an Int, validation will fail and your request will blow up before it is even executed.
Variables are always defined as part of the operation definition -- they can be used anywhere in the document, though, even multiple times. So there are no "two levels of parameters" here -- one line is simply the definition, the other line is usage.
A word on semantics
Even though we have a spec, the language around GraphQL has evolved past the terms outlined inside it. The term "query" has taken on multiple meanings that you may encounter while reviewing various docs and articles. It helps to keep these definitions in mind to avoid getting confused:
By convention, we name the root operation type associated with the query operation the Query type
Informally, the fields on that Query (i.e. getBooks) that are often referred to as the "queries" of your schema (just like the fields on the Mutation type are often called the "mutations" of your schema.
The complete request string we send to the server, which includes the whole operation and any relevant fragments is officially called the document. However, we often refer to making a request as querying your server. This has led to the document itself often being called a query, whether the operation is contains is actually a query or a different operation like a mutation.

Accessing the request object with express-graphql

According to
http://graphql.org/graphql-js/authentication-and-express-middleware/
To use middleware with a GraphQL resolver, just use the middleware like you would with a normal Express app. The request object is then available as the second argument in any resolver.
However, when I run my resolver
module.exports = {
Query: {
books(root, args, context) {
return books;
}
}
};
the second argument is my query arguments. The third argument, however, unless I override the context config property to expressGraphql is indeed my request object.
My full config is
app.use(
"/graphql",
expressGraphql({
schema,
graphiql: true
})
);
Are the docs wrong, or am I doing something incorrectly?
Neither :)
Even when building your schema with plain ole' GraphQL.js (as opposed to using graphql-tools or another library), there's a couple of different ways to pass in your resolvers.
You can generate it by creating a GraphQLSchema object, for example, in which case your resolvers are included within the GraphQLObjectTypes you add to it. Here is a repo that does just that.
Alternatively, you can declare a schema using a string with buildSchema. You can see that being done in the docs. If you go this route, the only way to pass in your resolvers is to utilize the rootValue object passed into your endpoint configuration. Normally, the values parameters passed to your resolvers are
root
args
context
info
However, when you go the above route, you lose the first parameter... since I imagine we can't pass root to itself
It's a good illustration of why, if you're going to generate a schema declaratively, graphql-tools is the way to go.

Resources