I'm using AWS Appsync and Amplify.
A snippet of my GraphQL schema look like this:
type Recipe
#model
#auth(rules: [{allow: owner}])
{
id: ID!
title: String!
key: String!
courses: [Course!]!
}
type Course
#model
#auth(rules: [{allow: owner}])
{
id: ID!
name: String!
}
On amplify push it creates the DynamoDB tables Recipe and Course
After reading many tutorials I still don't get it how to add a recipe in GraphiQL.
How can i insert a new Recipe that has a reference to a course and avoid duplicates in the Courses table?
To create multiples Recipe referencing the same Course without duplicates in the Course table, you need to design a many-to-many relationship.
So far the relationship you have designed is not enough for AppSync to understand, you are missing #connection attributes. You can read this answer on github to have an explanation of how to design this many-to-many relation in AppSync
After designing the relation, you will use a mutation to insert data, and it's likely that AppSync will generate the mutation code for you (if not, use amplify codegen in the console). You will then be able to create data.
Since you use DynamoDB with multiple tables (default mode for amplify / AppSync), you will have to either :
Call multiple mutations in a row
Use a custom resolver, as described in this SO answer
Related
There's one thing I have never understood about Graphql. I think I'll paint by problem by giving some examples.
Let's say I have an Account type and a Chat type. An Account can have multiple Chats associated with it, but a Chat can only be associated with a single Account. The schema might look something like this:
type Account {
id: String!
username: String!
chats: [Chat!]!
}
type Chat {
id: String!
text: String!
account: Account!
}
Let's say I am exposing the following queries:
type Query {
account(id: String!): Account
accounts: [Account!]!
}
Now, I will query my schema:
accounts {
id
username
chats {
id
text
}
}
This is a pretty straigthforward query - I simply what to grab all the chats by all the accounts.
But what if I have a deeper query like this:
accounts {
id
username
chats {
id
text
account {
id
username
chats {
id
text
}
}
}
}
I know that in reality, querying this data would be ridiculous, but still, it should be possible, right?
Now let's switch our view from the consumer of the API to the actual developer of the API.
How is the graphql developer meant to sustain these, potentially, infinite queries? Does Graphql resolve these automatically (it doesn't seem to)? Or am I just missing something out?
Thanks for any help.
How is the graphql developer meant to sustain these, potentially, infinite queries?
Not possible in having "infinite" nested queries.
GraphQL schema should be acyclic - meaning the relationships between types should be unidirectional.
Just a rule that the schema should be based on the query patterns in the client application.
So having a nested account within the chat would simply mean the use case and sequence are not properly defined.
For use cases like authentication it may be of use to add custom directives to a GraphQL schema as shown in the documentation: https://www.graphql-java.com/documentation/v16/sdl-directives
For example the Employee type has a salary field that had a directive on it meaning that only the manager role is allowed to read it:
directive #auth(role: String!) on FIELD_DEFINITION
type Employee {
id: ID
name: String!
startDate: String!
salary: Float #auth(role: "manager")
}
From the client side I would like to be able to read these directives on the Employee type using an Introspection query.
Is that possible at all? Would you be able provide a sample query for the example above?
This is not currently supported by GraphQL. You can use introspection to find out what directives exist in the schema, but you cannot use it to find out which fields had directives applied to them. You can see additional discussion around this issue here.
I am writing an Apollo GraphQL API that returns product information from various brands. A simplified version of the schema looks like this:
type Query {
products: [Product]!
}
type Product {
name: String!
brand: String!
}
I want to be able to query products from a specific brand. Normally this would be simple to achieve by adding a brand argument to the Product object:
type Query {
products(brand: String!): [Product]!
}
However, I have multiple GraphQL clients in different apps and each is associated with a specific brand so it seems redundant to always pass the same brand argument in every query. I also have many other objects in my schema (orders, transactions, etc.) that are specific to a brand and would require a brand argument.
Furthermore, my resolvers need to query a different API depending on the brand so even objects in my schema such as User, which are conceptually unrelated to a brand, would potentially need a brand argument so that the resolver knows which API to fetch from.
Is there a way to set the brand context for each client and have this context received by the server? Or maybe there is a better way to achieve this brand separation?
I would probably make Brand be a first-class type in your GraphQL query. That doesn't save you from having to qualify many of the queries you describe by a specific brand, but it at least gives you a common place to start from. Then you'd wind up with an API somewhat like:
type Query {
brand(name: String!): Brand
allProducts: [Product!]!
}
type Brand {
name: String!
products: [Product!]!
# users: [User!]!
}
type Product {
name: String!
brand: Brand! # typical, but not important to your question
}
If the differences between kinds of brands are visible at the API layer, you also could consider using a GraphQL interface to describe the set of fields that all brands have, but actually return a more specific type from the resolver.
The way you describe your application, it could also make sense to run one copy of the service for each brand, each with a different GraphQL endpoint. That would let you straightforwardly parameterize the per-brand internal object configuration and make the "current brand" be process-global context. The big constraints here are that, at a GraphQL level, one brand's objects can never refer to another, and if you have a lot of brands, you need some good way to run a lot of servers.
This is a very basic question but how do you call an extended type or interface?
All the documentations points to using extend type Person to add fields based on Person.
I would expect it to work like this
Employee extend type Person {
salary: Int!
}
But the documentation suggests it's like this:
extend type Person{
salary: Int!
}
So, how do I query for an Employee salary? What if there are multiple extensions of Person, e.g. Employee and Renter? I think I might be hampered by traditional thinking but I would expect the extension to result in something named and queryable.
The extend keyword is effectively used to modify an existing type within a schema. This is most commonly used in two scenarios:
1. Concatenating multiple strings that represent a single schema. You can have your schema broken up across multiple files, divided by domain. You can then do something like:
#base.graphql
type Query {
viewer: User
}
# user.graphql
extend type Query {
users: [User!]!
}
# post.graphql
extend type Query {
post: [Post!]!
}
This results in a schema that's effectively the same as:
type Query {
viewer: User
users: [User!]!
post: [Post!]!
}
2. Extending from a base schema. You might have multiple schemas that build on top of some base schema. Or you might be stitching together remote schemas. In these scenarios, we often want to add fields specific to our new schema that don't exist on the base types. This can be used to implement directives that are missing from the base schema as well:
extend type SomeType #customDirective
The extend keyword can only modify existing types; it is not a vehicle for inheritance. In fact, GraphQL does not support type inheritance. Interfaces provide a level of abstraction over existing types, but types that implement an interface do not inherit any fields from that interface. There's no way to do that, unless you use some library like graphql-s2s.
I'm attempting my first GraphQL backend using AWS AppSync. I'm simply trying to figure out how to use one-to-many associations. I expect to receive the many related objects as a list of children, and to be able to write some of these children when creating a new user.
type User {
id: ID!
name: String!
records: [Records!]!
}
type Records {
id: ID!
userId: ID!
title: String!
... etc ...
}
Using the AppSync interface, I click on Create Resources once to make a Records table and again to make a Users table, both in DynamoDB. This also automatically adds mutations, subscriptions, input types, and more types, to my schema, and creates resolvers for me.
What is the syntax for a mutation to create Record objects associated with my User objects? How can I PUT the Record data when I create the User?
If needed I can include more of the schema that AppSync is autogenerating.
Since you are using two DynamoDB tables (Users and Records), you will need to make two DynamoDB calls during the CreateUser mutation. One way to make two DynamoDB calls in a single mutation is to utilize DynamoDB's BatchPutItem operation.
To utilize BatchPutItem, you will need to modify the resolver which is attached to your CreateUser mutation. The resolver is responsible for taking your graphQL request, converting it into a DynamoDB operation, and then converting the results of the DynamoDB operation into a graphQL response. The resolvers have two components: a request mapping template, and a response mapping template.
The request mapping template will be responsible for taking mutation arguments and converting them into a DynamoDB BatchPutItem request.
The resolver's response mapping template will be responsible for converting the result of the DynamoDB BatchPutItem operation into your mutation's return type/structure.
Here is a tutorial on how to utilize multi-table BatchPutItem in a resolver: https://docs.aws.amazon.com/appsync/latest/devguide/tutorial-dynamodb-batch.html
Here is a programming guide for using the Template language required for the resolvers: https://docs.aws.amazon.com/appsync/latest/devguide/resolver-mapping-template-reference-programming-guide.html