AWS CDK + Appsync nested lambda resolvers arguments - graphql

I'm building a graphql API on AWS using CDK, resolvers are custom lambdas. One type in my schema.graphql has got a field with its own inputs. The schema looks something like this.
type Output {
field1: Int!
field2: String
field3(input1: String!, input2: String!): Int
}
input MyInput {...}
type Mutation {
MyMutation(input: MyInput): Output
}
Currently I'm only declaring the main MyMutation resolver in the CDK script.
The arguments for field3 are inside event.info.selectionSetGraphQL, but are formatted as strings, like field3(input1: 'str1', input2: 'str2)\n.
Shouldn't I be able to get them in some sort of arguments field as an object as it is for the arguments of the top level MyMutationfield?
Is this because I'm not using field resolvers properly?
I could not find examples of nested graphql resolvers online. Is there a better way of doing this? Something like Apollo's resolver maps?
Thank you.

Related

Graphql - How to include schema from other types

Let us say I have the following type:
type Foo {
id: ID!
field1: String
}
Now, I wish to define another type, which includes the earlier type. Something like this:
type Bar {
...Foo,
field2: String
}
How do I achieve the above in graphql? I want to basically first create a type, and then include that type in the definition of other types so that I don't have to type all the attributes multiple times.
I am using Amplify / AWS Appsync so if there's any special directive that I could use that would also be helpful
GraphQL has the concept interfaces for this. Appsync, AWS's GraphQL implementation, supports interfaces.
[Edit:] GraphQL does not support "...spread" syntax for interfaces. Fields are defined explicitly. Spread syntax does figure in GraphQL, but in the form of Fragments, resuable units of fields for reducing repetition in queries.
interface Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
}
type Human implements Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
starships: [Starship]
totalCredits: Int
}
type Droid implements Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
primaryFunction: String
}
Amplify, which automagically creates AppSync schemas, resolvers and data sources, is apparently a more difficult story. The amplify-cli repo has an open feature request, Does the GraphQL Transformer support interfaces?. I am no Amplify expert, but a quick look at the loooong feature request comment thread suggests the answer for Amplify is "not out-of-the-box", but "maybe works in narrow circumstances or with advanced customization".

Mock GraphQLUpload dataype in POSTMAN

I am using Postman to send GraphQL queries to my graphQL server. This is what a particular mutation schema looks like:
extend type Mutation {
createMutation(
my_id: ID!
my_data: input_data
): some_output
input input_data {
some_key: int
file : Upload!
}
I am able to perform other mutations and query through graphQL by defining appropriate GraphQL variables in here
I am not sure how to create a json value in GraphQL Variables for "file" of type Upload

GraphQL | How to implement conditional nesting?

Please consider the following GraphQL schema:
type User {
id: ID!
events: [Event]
}
type Event {
id: ID!
user: User!
asset: Asset!
}
type Asset {
id: ID
price: Number!
name: String!
}
GraphQL is a fantastic framework for fetching nested objects, but I'm struggling to understand how conditional nesting is implemented.
Example:
I want to retrieve all events for a specific user where asset.price is greater than x.
Or
I want to retrieve all events for an asset that belongs to a list of users [].
Question: Is conditional nesting a concept in GraphQL and how is it implemented?
Side note: I use AWS AppSync and resolvers are fetching data from AWS DynamoDB.
You can define a filter/condition on any GraphQL query such as:
query {
users(permission: 'ADMIN') {
...
}
}
The permission param is passed to your resolver (say DynamoDb VTL template, Lambda etc) to be handled however you want - to GQL this is just another parameter.
You can carry this concept into nested field by creating an events resolver and you'd then call it like this:
query {
user(id: '123') {
name
events(minPrice: 200) {
nodes: {
id
eventName
eventDate
}
}
dob
...
}
}
In above case I am using a simple minPrice param but you could do more complex things such price ranges, even pass operators (eq, gt, ...). It's all irrelevant to GraphQL - all gets passed to the resolver.
How you implement that on backend depends on your setup. I use AppSync without Amplify and write my own VTL templates and build the DynamoDb request using the provided GQL fields.
Here is an SO post that shows how to create a date filter.

can I assign a variable in a graphql playground to the result of a mutation

If I have a mutation with 2 fields:
type Mutation {
createSimulation(
name: String
simulators: [AvailableSimulators!]!
timeToLiveInMS: Int
): Simulation!
create(
simulationID: ID!
simulator: AvailableSimulators!
type: String!
attributes: KeyValuePair
): CreateResult!
}
When I run the mutation in the graphql applo server playground, I need a value from the return of createSimulation in a call to create:
Can I somehow assign a variable that I can use in create?
Not part of the GraphQL standard. Should handle this in the resolver in the backend. It will depend on the technology used, but in most of the technologies you can use results from previous resolvers or call resolvers manually.
No, you should send 2 requests to handle it. The client-side should request createSimulation first to get a response then request another request to create mutation with that UUID.

Change the exposed graphql schema through directives

Directives are nice to alter the behaviour of resolvers, but is there a way to directly change the exposed schema with them?
Example
expected superuser schema
type Query {
getBooks: [Book]
getAuthors: [Author]
}
expected normal user schema
type Query {
getBooks: [Book]
}
one definition to build them all
type Query {
getBooks: [Book] #allow(scopes: ["superuser"])
getAuthors: [Author]
}
The scope would be defined through the given context as i would build one schema for each possible scope.

Resources