amplify-swift
amplify-swift copied to clipboard
[Feedback]Make a GraphQL request for a nested query
Page: /lib/q/platform/ios
Feedback:
Hi there!
I'm trying to update a fairly straightforward app from the old Amplify SDK to the new Amplify Library. The docs don't have any migration guide sadly.
One thing that isn't at all clear is how you go about using custom queries with the API exposed by the library. The docs show how to fetch a single or list of a model, but not how to make a GraphQL request for, say, a nested query.
Please could you advise?
Many thanks!
Thanks for clarifying the request @swaminator.
To add another (very related) query: By using custom queries I'm wanting to fetch nested objects with a single HTTP request but also wanting to avoid over-fetching of data by allowing engineers on the client side to specify which subset of fields they require.
Thanks!
Hi @mrtom, we have some ideas to provide a migration strategy for customers using AppSync SDK to Amplify API plugin. It may involve exensions that translate the code generated classes in AppSync over to the Amplify's GraphQLRequest
so I'm thinking that we can better track this if I transfer the issue over to the Amplify iOS repo.
To add another (very related) query: By using custom queries I'm wanting to fetch nested objects with a single HTTP request but also wanting to avoid over-fetching of data by allowing engineers on the client side to specify which subset of fields they require.
I believe a custom query where you pass a custom selection set is the first step to getting a subset of fields required on the client side. Are you constructing a GraphQLRequest
with the graphql document containing customized selection set?
Then what is the responseType
used when deserializing the response data? Are you using the Model classes generated from amplify codegen models
or are you using an hand crafted Codable types that contain a subset of the fields?
I had imagined that a code gen step could/would be run that would generate models for a specific query, but in a strongly typed language like Swift it can be difficult to do well.
There are three approaches that I feel you could consider. Let's imagine you have a GraphQL schema like so, with a user and a post, and a many-to-many connection between users and posts.
type User
@model
{
id: ID!
username: String!
sub: String!
posts: [UserPost] @connection(keyName: "byUser", fields: ["id"])
createdAt: AWSDateTime!
}
type Post
@model
{
id: ID!
associated: [UserPost] @connection(keyName: "byPost", fields: ["id"])
title: String!
body: String!
createdAt: AWSDateTime!
}
type UserPost
@model
@key(name: "byUser", fields: ["userPostUserId", "userPostPostId"])
@key(name: "byPost", fields: ["userPostPostId", "userPostUserId"])
{
id: ID!
user: User! @connection(fields: ["userPostUserId"])
userPostUserId: ID!
post: Post! @connection(fields: ["userPostPostId"])
userPostPostId: ID!
createdAt: AWSDateTime!
}
You may want to get a list of posts by created date or whatever and just render the the authors name next to each post. So you may create a GraphQL query like (haven't created this IRL, so may not be perfect):
query postsByCreatedAtQuery(input: { limit: 25} ) {
posts {
items {
post {
id
title
body
createdAt
associated {
items {
user {
username
}
}
}
}
}
}
}
So here, you have a query that gets all the fields for the Post type, but only one field (the username) for the associated users.
I think you have three options:
- Codegen a type called
postsByCreatedAtQuery_user
, which is specific to the user fetched for this query and only gives the developer access to theusername
field on that type. This is essentially how the Amplify SDK worked I believe. - Code gen a single User type but make all fields optional. This sucks for a bunch of reasons (not least that you're not respecting the optionality of types from the GraphQL schema). It is simpler for new developers to understand, but on a larger codebase can be tricky for people to figure out what data has been fetch and what has not.
- Create a new type,
MUST_FETCH
. And each code gen'd model now has fields that are a union type of some sort, i.e. username can be of typeString | MUST_FETCH
(a bit like a result type I suppose). You can now return yourUser
model in this case, but all fields other thanusername
are set toMUST_FETCH
and if the code tries to read from them the code throws an error (in development at least), essentially indicating to the developer than they need to add the field to their GraphQL query.
You could also add some linting/static analysis to ensure that only fields which have been fetched are accessed. Relay does this for example, but requires adding fragments for each component. This doesn't work very well with UIKits programming model, but would work very well with SwiftUI.
Hello,
We have an example to create custom GraphQL requests for nested data queries through the custom selection set feature. https://docs.amplify.aws/cli/migration/lazy-load-custom-selection-set/#custom-selection-sets
Let us know if this works for you.
@thisisabhash - How would this look for TypeScript/JavaScript? I've been after this for so, so long!
@armenr Please open an issue in the JS repo with your question: https://github.com/aws-amplify/amplify-js
I'm good. I figured it out myself. Thanks for the quick instructions to open issues.