Meta data values of "Enum" - graphql

I created a field in a model named "Listing". The field in here is "category". This is set to enum of some values (screenshot below). Does graphql allow to query to fetch the enum list?

This is possible using a GraphQL introspection query.
Enum types on Graphcool are named "MODEL_FIELD", so in your case you can run this query:
{
__type(name: "LISTING_CATEGORY") {
name
enumValues {
name
}
}
}
You can find more information in this answer to a similar question.

An easier approach, when using Nexus.js, is to just create a new root type, and have it resolve to your array of ENUM values. This works because with Nexus.js you're writing your schema with code (TypeScript), which means you're able to import your array of ENUM values into the resolver — it's just code.

Related

hotchocolate throws error when using UseFiltering() on a field

I have a pretty simple setyp where I'm putting graphql over an entityframework datacontext (sql server).
I'm trying to get filtering to work. I've tried adding .UseFiltering() to a field descriptor like so...
descriptor.Field(t => t.AccountName).Type<NonNullType<StringType>>().UseFiltering();
But it causes this error on startup...
HotChocolate.SchemaException: 'Unable to infer or resolve a schema
type from the type reference Input: System.Char.'
I assume I'm doing something wrong somewhere...
"UseFiltering" is supposed to be used to filter data which represents a collection of items in some way (IQueryable, IEnumerable, etc).
For instance, if you have users collection and each user has AccountName property you could filter that collection by AccountName:
[ExtendObjectType(Name = "Query")]
public class UserQuery
{
[UseFiltering]
public async Task<IEnumerable<User>> GetUsers([Service]usersRepo)
{
IQueryable<User> users = usersRepo.GetUsersQueryable();
}
}
In that example the HotChocolate implementation of filtering will generate a number of filters by user fields which you can use in the following way:
users(where: {AND: [{accountName_starts_with: "Tech"}, {accountName_not_ends_with: "Test"}]})
According to your example: the system thinks that AccountName is a collection, so tries to build filtering across the chars the AccountName consists of.

Apollo Graphql modify input data

In Apollo Server, one could use a schema directive to implement a resolver middleware like such:
adminGetUsers(getUsersPL: GetUsersPL!): [User] #hasRole(role: "ADMIN")
#hasRole(role: "ADMIN") serves as a middleware to prevent any non-admin user from using this mutation.
So how would one sanitize/transform input data? For example,
getUser(userId: String! #transform): [User]
#transform will take in userId as a hashed id (ie: xyfd), and transform it to a numbered ID (ie: 12). This syntax is not allowed of course. Basically I want a way to modify input data before it goes into resolver.
That actually is valid syntax. You can define a directive that's applied to argument definitions like this:
directive #test on ARGUMENT_DEFINITION
type Query {
foo(input: String #test): String
}
Schema directives are not middleware. They are just ways of altering individual definitions inside your schema. Most commonly they are used to alter field definitions, but you can alter other definitions like object types, input object types, enums, unions, etc. When using a directive with a field, you can wrap the existing resolve function inside another one (or replace it altogether) -- in doing so, we can create "middleware" for resolvers. However, that's not the purpose of schema directives.
That aside, you can't use an argument directive to alter the value the argument is passed. At best, you can change the type of the argument to something else (like a custom scalar). However, you can just use a field directive to do what you're trying to accomplish:
class ExampleDirective extends SchemaDirectiveVisitor {
public visitFieldDefinition(field) {
const { resolve = defaultFieldResolver } = field
field.resolve = async function (
source,
args,
context,
info,
) {
args.someArg = doSomething(args.someArg)
return resolve.call(this, source, args, context, info);
}
}
}

Apollo GraphQL fails to invoke resolver for a nested field

I've the following structure in my schema:
type gn_Feature implements Some_Interface {
s_description: String
s_id: URL!
some_parent: gn_Feature
}
As you can see, each gn_Feature has an another linked gn_Feature object (the linking is handled elsewhere, it doesn't really matter). By my current understanding, you only need to define the resolvers for the return types, so my resolvers look like the following:
export const resolvers = Object.assign(
{},
{
DateTime: DateTime,
Date: DateTime,
Time: RegularExpression("Time", /^\d{2}:\d{2}(:\d{2})?$/),
URL,
Query: {
gn_Feature: gn_FeatureResolver
},
gn_Feature: gn_FeatureResolver
}
);
But, my queries fail with the following error if I don't explicitly define the resolver for the nested field, like so:
gn_Feature: {some_parent: gn_FeatureResolver}
Error:
"message": "Resolve function for \"gn_Feature.s_description\"
returned undefined"
My resolver function doesn't even get invoked for my nested object when I don't specify it like the above.
My backend consists of some voodoo transpiling of GraphQL queries into SparQL queries which return data back so I won't post the resolver code as I utilize one universal resolver for many fields. I'd like to avoid having to specify resolvers for each nested field as that's going be extremely tedious (I have dozens of types with dozens of fields). Any clarifications are welcome, as I'm completely baffled.
Example GraphQL query:
gn_Feature(some_field:"AD", last:2){
s_description,
s_id
some_parent{
s_description
}
}
When executing a query, only the value of any particular field is initially unknown and needs to be resolved... everything else, like the types, selection sets, etc. is already known. A resolver is a function called to resolve a specific field. In GraphQL.js, there are no resolvers for types.
Apollo muddies the waters a bit in this regard, since for convenience, the API for makeExecutableSchema allows you to define custom scalars by including them in the resolver map. Additionally, even though interfaces and unions don't have resolvers either, Apollo lets you use the resolver map to specify a __resolveType function for these abstract types as well. Apollo's docs actually define a resolver as "a function that connects schema fields and types to various backends." However, in the context of GraphQL in general, you should think of a resolvers as "a functions that resolves the value of a field".
So, given a type called gn_Feature and a field called some_parent, this is the correct way to structure your resolvers:
const resolvers = {
gn_Feature: {
some_parent: someResolverFunction
}
}
Also note that Query and Mutation are both types themselves, so if you have a query called gn_Feature, you are actually resolving a field called gn_Feature for the Query type.

Graphql, nodejs, how to resolve non-root level query field based on if it is queried?

I'd like to resolve a field called 'outstandingBalance' in Client type. If front-end query:
query {
Client {
id
name
outstandingBalance
}
}
The resolver function for outstandingBalance is expensive to run. If front-end query:
query {
Client {
id
name
}
}
Then, don't trigger the resolver for 'outstandingBalance'. I have basic understanding of graphql and read most of its official document. But have not seen an answer to this pattern, or this way of using Graphql is not allowed?
Question
Is there a thing called "Nont-root level resolver" for graphql? like the 'outstandingBalance' field in Client type?
Question: How to implement in graphql? Especially using resolver:
async function outstandingBalance(obj, args, context, info) {
console.log('called...')
}
to query one field in one type based on if this field is queried?

Apollo/GraphQL field type for object with dynamic keys

Let's say my graphql server wants to fetch the following data as JSON where person3 and person5 are some id's:
"persons": {
"person3": {
"id": "person3",
"name": "Mike"
},
"person5": {
"id": "person5",
"name": "Lisa"
}
}
Question: How to create the schema type definition with apollo?
The keys person3 and person5 here are dynamically generated depending on my query (i.e. the area used in the query). So at another time I might get person1, person2, person3 returned.
As you see persons is not an Iterable, so the following won't work as a graphql type definition I did with apollo:
type Person {
id: String
name: String
}
type Query {
persons(area: String): [Person]
}
The keys in the persons object may always be different.
One solution of course would be to transform the incoming JSON data to use an array for persons, but is there no way to work with the data as such?
GraphQL relies on both the server and the client knowing ahead of time what fields are available available for each type. In some cases, the client can discover those fields (via introspection), but for the server, they always need to be known ahead of time. So to somehow dynamically generate those fields based on the returned data is not really possible.
You could utilize a custom JSON scalar (graphql-type-json module) and return that for your query:
type Query {
persons(area: String): JSON
}
By utilizing JSON, you bypass the requirement for the returned data to fit any specific structure, so you can send back whatever you want as long it's properly formatted JSON.
Of course, there's significant disadvantages in doing this. For example, you lose the safety net provided by the type(s) you would have previously used (literally any structure could be returned, and if you're returning the wrong one, you won't find out about it until the client tries to use it and fails). You also lose the ability to use resolvers for any fields within the returned data.
But... your funeral :)
As an aside, I would consider flattening out the data into an array (like you suggested in your question) before sending it back to the client. If you're writing the client code, and working with a dynamically-sized list of customers, chances are an array will be much easier to work with rather than an object keyed by id. If you're using React, for example, and displaying a component for each customer, you'll end up converting that object to an array to map it anyway. In designing your API, I would make client usability a higher consideration than avoiding additional processing of your data.
You can write your own GraphQLScalarType and precisely describe your object and your dynamic keys, what you allow and what you do not allow or transform.
See https://graphql.org/graphql-js/type/#graphqlscalartype
You can have a look at taion/graphql-type-json where he creates a Scalar that allows and transforms any kind of content:
https://github.com/taion/graphql-type-json/blob/master/src/index.js
I had a similar problem with dynamic keys in a schema, and ended up going with a solution like this:
query lookupPersons {
persons {
personKeys
person3: personValue(key: "person3") {
id
name
}
}
}
returns:
{
data: {
persons: {
personKeys: ["person1", "person2", "person3"]
person3: {
id: "person3"
name: "Mike"
}
}
}
}
by shifting the complexity to the query, it simplifies the response shape.
the advantage compared to the JSON approach is it doesn't need any deserialisation from the client
Additional info for Venryx: a possible schema to fit my query looks like this:
type Person {
id: String
name: String
}
type PersonsResult {
personKeys: [String]
personValue(key: String): Person
}
type Query {
persons(area: String): PersonsResult
}
As an aside, if your data set for persons gets large enough, you're going to probably want pagination on personKeys as well, at which point, you should look into https://relay.dev/graphql/connections.htm

Resources