Allow refering models from custom types in Gen 2 to allow for basic usable argument/return types in custom queries/mutations
Describe the feature you'd like to request
I'm integrating OpenSearch in my Gen 2 backend using custom queries where i need to return the items, nextToken as well as the total. The only way of doing this is using a custom type as the return type of the custom query:
// The model i'm working with and trying to integrate search with:
Feedback: a
.model({
title: a.string().required(),
...
}),
// The custom type that defines the structure of the return type of the custom search query:
SearchableFeedbackConnection: a.customType({
items: a.ref("Feedback").array().required(),
nextToken: a.string(),
total: a.integer(),
}),
searchFeedback: a
.query()
.arguments({
...
})
// Referencing the custom return type from above:
.returns(
a.ref("SearchableFeedbackConnection")
)
This results in the following error:
Cannot use ".ref()" to refer a model from a "custom type". Field "items" of "SearchableFeedbackConnection" refers to model "Feedback"
The problem is that i NEED to return the nextToken alongside all Feedback items. So i can't just return .returns(a.ref("Feedback").array()), which wouldn't result in the error.
The problem isn't isolated to the opensearch example above, i have this issue using custom queries/mutations in general, for example in all my custom list queries where i also need to return the nextToken to be able to query for the next batch of items.
And the same goes for arguments: https://github.com/aws-amplify/amplify-data/issues/451
Describe the solution you'd like
I want to be able to refer to models from custom types so that custom queries work.
Describe alternatives you've considered
Creating another custom type that represents a Feedback instead of using the "Feedback" model obviously doesn't result in the above error but also doesn't have all the field-level authorization rules and relationships that allow for nested queries the model has:
// This does not work since i need all the field-level authorization rules and the ability to perform nested queries using the
// relationships on the "Feedback" model
FeedbackResponse: a.customType({
...
}),
SearchableFeedbackConnection: a.customType({
items: a.ref("FeedbackResponse").array().required(),
nextToken: a.string(),
total: a.integer(),
}),
Additional context
No response
Is this something that you'd be interested in working on?
- [ ] 👋 I may be able to implement this feature request
Would this feature include a breaking change?
- [ ] ⚠️ This feature might incur a breaking change
This is a blocker for me since my custom list & search queries need to return my model's items but without being able to return a nextToken they're kind of useless atm.
Hey @thomasoehri, Thanks for requesting this. We are marking this as a feature request for the team to evaluate further.
Hi @AnilMaktala, I also encountered this problem. Looking forward to your solutions
@AnilMaktala - This is a big blocker for custom queries. There should be a type in the generated schema for the referenced model looks like this:
type ModelMyModelConnection @aws_cognito_user_pools @aws_iam {
items: [Authorization]!
nextToken: String
}
How do we get a hold of this object for reference on the TS side of things? Is it nested in the Schema type somewhere?
Work around I just implemented for anyone whose blocked by this. The trick is to duplicate the model, and make the dupe a customType. Its annoying but works.
// The model I'm working with and trying to integrate search with:
Feedback: a.model({
title: a.string().required(),
...
}),
// Duplicate that allows referencing:
Feedback2: a.customType({
title: a.string().required(),
...
}),
// The custom type that defines the structure of the return type of the custom search query:
SearchableFeedbackConnection: a.customType({
items: a.ref("Feedback2").array(),
nextToken: a.string(),
scannedCount: a.integer(),
startedAt: a.timestamp()
}),
searchFeedback: a.query()
.arguments({
...
})
// Referencing the custom return type from above:
.returns(a.ref("SearchableFeedbackConnection"))
.handler(a.handler.custom({
dataSource: a.ref('MasterTenantTable'),
entry: './MyResolverId.js',
})),
Appsync will often be none the wiser because the schemas match. However you still won't be able to get nextTokens for nested nodes.
I think (hope!) this will shortly be addressed by an open PR here: https://github.com/aws-amplify/amplify-data/pull/497 There's also a similar ticket open here: https://github.com/aws-amplify/amplify-data/issues/435
Unfortunately, the PR I referenced only seems to address the arguments part of this issue (that has now been merged), not the return values. It's frustrating that they have not looked at both at the same time.
A comment and quick tip: Comment: This is not isolated to OpenSearch. When handling scalable, dynamic, authorization you’re forced to replicate the prebuilt get, list, etc. in your own custom resolvers with integrated authorization where paginated return types are frequently required. So, I look forward to seeing this feature added! :)
Quick tip (building on what jmarshall9120 way saying): Leverage typescript, extract your types! This will help maintainability So in your example:
const FeedbackType = {
title: a.string().required(),
parentId: a.id(),
// ...
}
const schema = a.schema({
Parent: a.model(/*whatever you want*/),
Feedback: a.model({
...FeedbackType,
// Still need to extract relationships
a.hasOne("Parent", "parentId"),
}),
BaseFeedback: a.customType(FeedbackType),
PaginatedFeedback: a.customType({
feebacks: a.ref("BaseFeedback").array(),
nextToken: a.string(),
total: a.integer(),
// ...
}),
searchFeedback: a
.query()
.arguments({
//...
})
.returns(
a.ref("PaginatedFeedback")
)
//.(...)
})
Using built in object methods to split up the schema into pieces is also a great practice for handling large schemas. Took me way too long to realize!