I am in the process of writing an application in React using AWS Appsync as my backend and bootstrapping it with AWS Amplify.
The use case is to retrieve a list of cars from a DynamoDB table, based on the current authenticated users group id. The schema looks as follows:
type Query {
carsByUserGroup(userGroup: String!, limit: Int, nextToken: String): CarConnection
@aws_cognito_user_pools
}
type CarConnection {
items: [Car]
nextToken: String
}
type Car {
timestamp: String
regno: String!
country: String!
type: String
attributes: CarAttributes
links: [Link]
basic: BasicInfo
inspection: InspectionInfo
status: StatusInfo
technical: TechnicalInfo
uploadedBy: String
userGroup: String
}
Note: some of the fields in type "Car" relates to other types, which are defined in the schema, but I haven't added here, to save space.
After downloading the new schema and putting into my schema.graphql in my React App I run "amplify codegen" in order to generate the new query. This is what I get in "queries.ts":
query CarsByUserGroup($userGroup: String!, $limit: Int, $nextToken: String) {
carsByUserGroup(userGroup: $userGroup, limit: $limit, nextToken: $nextToken) {
items {
timestamp
regno
country
type
uploadedBy
userGroup
__typename
}
nextToken
__typename
}
}
This is where two issues occur:
It generates some of the attributes in "Car" in the "items" field but not all of them for some reason? Can someone explain why this is?
When I want to use the query in my React App I do it via this function:
export const fetchCarsByGroup = async (userGroup: string, limit = 10) => {
try {
const data = await client.graphql({
query: queries.carsByUserGroup,
variables: {
userGroup: userGroup,
limit: limit,
}
})
console.log('Fetched cars:', data);
// Handle the fetched data
} catch (error) {
console.error('Error fetching cars:', error);
// Handle error
}
};
The return of "fetchCarsByGroup" is an array matching with "userGroup", with the fields listed in the "CarsByUserGroup" in query.ts. However, my understanding of GraphQL and why it is so powerful, is that, depending on the use case, I can define which specific fields to return on a specific use case. This doesn't seem to be the case here?
I would assume that in my function "fetchCarsByGroup" I could define the fields to return in "variables" field, just like I can in the AWS Management Console in AppSync under "queries".
One solution that I read is to make a "custom-queries.ts" file and add the query for the specific use case.
If I make that and define the specific fields to return, then the next time I run "amplify codegen" after having edited something in my schema, the custom query in "custom-queries.ts" will not be synchronized to my Amplify setup. So in case I remove a field from "Car" I would have to do that manually in all the custom queries that fetches these fields.
Seeing that the app could scale up in functionality, this is a red flag for me.
So my question to you is, what have I misunderstood and is my assumption wrong in terms of defining which specific fields to return in the "fetchCarsByGroup" and have "CarsByUserGroup" as a global query that I can cherry pick from?
Don't trust codegen to generate queries for you. It doesn't know what you really want on the client. You should consider such queries as suggestive only.
You can modify the query:
query CarsByUserGroup($userGroup: String!, $limit: Int, $nextToken: String) {
carsByUserGroup(userGroup: $userGroup, limit: $limit, nextToken: $nextToken) {
items {
timestamp
regno
country
type
uploadedBy
userGroup
}
nextToken
}
}
to include or exclude fields depending on client needs. For example:
query CarsByUserGroup($userGroup: String!, $limit: Int, $nextToken: String) {
carsByUserGroup(userGroup: $userGroup, limit: $limit, nextToken: $nextToken) {
items {
regno
country
type
links {
linkfield1
linkfield2
etc…
}
userGroup
}
nextToken
}
}
will work just as well.
GraphQL variables are the $
-prefixed variables at the top. These can be passed in at runtime. Field selection is done in the query definition itself. You can build query definitions dynamically if you wish, they don't need to be static.
On the server, queries (and mutations) are defined in terms of the variables they expect and the type they return, not the fields. Given that a query returns a single type
or a list thereof [type]
it's up to you to pick which fields from that type and possibly which fields from related types (given that fields in a type can point to other types).
Note also that
__typename
is always included in the result even when you don't ask for it so don't feel like you need to ask for it.