AppSync batch call for nested properties - rest

My backend REST API takes a list of id's and returns a list of, say, Person objects whose id was requested. Each Person has a children property, which is a list of Person.id. A nice family tree.
// GET /id/[1,2]
{
"id": 1,
"name": "Jacob",
"children": [3, 4]
},
{
"id": 2,
"name": "Jack",
"children": [5, 6]
}
Using AppSync to front said API, I have a resolver for children that makes a call to my API using the following template:
#set( $myMap = {
"id" : $context.source.children
} )
{
"version" : "2017-02-28",
"operation": "Invoke",
"payload": {
"id": $util.toJson($myMap)
}
}
This works fine, and AppSync nicely "unwraps" the children array with the appropriate Person. The issue is that if a Person has N children, then N calls are made to my backend API if the client requests the children.
Since my API could take all of the children IDs for all N children at once, it would be nice if AppSync had a way to batch those calls, and then somehow untangle the response and put each Person in the right place.
Question
Is there a way to batch all of individual calls for a nested property into a single call per nested level?
Edit
My GraphQL schema for the example above:
type Person {
id: Int
name: String
children: [Person]
}

AppSync supports this type of batching behavior when proxied through AWS Lambda. See https://docs.aws.amazon.com/appsync/latest/devguide/tutorial-lambda-resolvers.html and search for "Batching".
With that being said, based on what you have provided, can you not just call your batch API from the Person.children field for example?
You specified that you have an object:
type Person {
id: Int
name: String
childIds: [String]
children: [Person]
}
Can you not just add the field Person.children that calls your batch HTTP endpoint with the list of children?
type Person {
id: Int
name: String
childIds: [String]
# An HTTP resolver that performs a GET to /id/[1,2]
# where [1,2] comes from $ctx.source.childIds
children: [Person]
}
This will perform a single batch call to your HTTP endpoint per person.

AppSync supports this type of batching behavior to an extent when proxied through AWS Lambda. See https://docs.aws.amazon.com/appsync/latest/devguide/tutorial-lambda-resolvers.html and search for "Batching".
With that being said, based on what you have provided, can you not just call your batch API from the Person.children field for example?
You specified that you have an object:
type Person {
id: Int
name: String
childIds: [String]
children: [Person]
}
Can you add the field Person.children that calls your batch HTTP endpoint with the list of children provided by the $ctx.source object?
type Person {
id: Int
name: String
childIds: [String]
# An HTTP resolver that performs a GET to /id/[1,2]
# where [1,2] comes from $ctx.source.childIds
children: [Person]
}
This will perform a single batch call to your HTTP endpoint per person.

Related

Custom filters that accept objects - lighthouse-php

I am using lighthouse-php as Api Gateway in a micro services architecture.
So for all my types I make a request internally through Guzzle.
But I am needing to implement filters that are suitable for any type and that give flexibility when making queries.
I need to implement a query like this:
query news (
order_by: {publication_date: desc}
where: {
_or: {categories_id: { _eq: 1 }, title: { _ilike: "news" } }
}
limit: 10
offset: 20
) {
id
category_name: name
photo
publication_date
text
title
}
But I have no idea how to implement this "where" filter that receives a composite object as in this example.
Remember that this query will not use any model within lumen, since it will be a custom query that will make a request to the microservice of news.
What I need is the way that my query receives whatever comes in where, limit and order, to send it on request. But I have no idea how to build something like this in the scheme.
Anyone have any idea how to do it?
Thanks friends.
Yes, you can.
Just now I'm making an component that will receive criterias to filter in graphql query so I need to fill filter's where params with those criterias.
Imagine the following schema:
type News{
id: ID!
title: String!
views: Int!
}
type Query{
getNews(where: _ #whereConditions(columns:["title", "views"])) : [News!] #all
}
We can make a query and fill where variables later
query GetNews($whereNews: [GetNewsWhereWhereConditions!]){
getNews(where: {OR: $whereNews}){
title
views
}
}
When querying we can fill the variables sending an object like
{
"where":[
{"column": "TITLE", "operator": "LIKE", "value": "Amazing title"},
{"column": "VIEWS", "operator": "GTE", "value": 10,
]
}

Api response structure with configurable columns definitions

I am building an application for a student application system which allows multiple organisations to provide an online application form to its students to apply for courses.
For each application, it will have ID, StudentName, CourseName. However some universities require SecondaryExamMark and some requires Reference name and reference contact no.
I am making an API call (/application/list?pageNo=1&rowsPerPage=20&orgId=1)
and returns all application data with common information plus the extra requirements based on the organisation id provided
I defined an application model that includes all common properties plus a subset of properties all organisations requested and store them in a single database table with a few nullable fields for different organisation.
In terms of the Api response structure, which of the following is more appropriate?
{
ID: 1,
StudentName: 'A B',
CourseName: 'B C',
ReferenceName: null,
ReferenceContact: null,
SecondarySchoolMark: '80'
}
OR
{
headers: [
{
Title: "ID",
Type: "text"
},
{
Title: "StudentName",
Type: "text"
},
{
Title: "CourseName",
Type: "text"
},
{
Title: "SecondarySchoolMark",
Type: "text"
}
],
application: [
{
Title: "ID",
Value: "12345"
},
{
Title: "StudentName",
Value: "A B"
},
{
Title: "CourseName",
Value: 'B C'
}
{
Title: 'SecondarySchoolMark',
Value: '80'
}
]
}
The first approach seems to be a general Api structure which returns an object that describes an application. However the second approach allows the Api to decide which columns should be rendered, and UI would only have to treat the response as display fields.
IMO, i would prefer the first approach because in order to make the API integrate-able with other clients, the API should provide resource based responses. And showing or hiding the columns (whether based on another Api call to /getdisplaycolumns?orgId=1 or just treat null columns as hidden is UI's responsibility)
Edit 1: not necessarily returning the null properties from the approach one as Json serializer allows to ignore null properties)
I agree, settling on a model (resource) that has a couple of nullable properties that the client can ignore sounds like a more robust design (strongly typed properties!) than deserializing whatever the second model is into a Dictionary of sorts and working with that.

Should I include URLs or Ids of resources in REST API response?

I am designing a REST API. There's an entity Organization which may have a parent organization and multiple child organizations.
Let's say a user does request GET /organizations/1234.
What should I respond with?
I can use URLs to these other organizations.
{
"data": {
"name": "Microsoft",
"parent_organization": "http://api.myapi.asdfghj/organizations/1220",
"child_organizations": [
"http://api.myapi.asdfghj/organizations/1236",
"http://api.myapi.asdfghj/organizations/1214"
]
}
}
or I can use their ids
{
"data": {
"name": "Microsoft",
"parent_organization": 1220,
"child_organizations": [
1236,
1214
]
}
}
Which one is better?
If it's the one with full URLs, how do I do document that in swagger? Do I just set that as a string, something like the following?
definitions:
Organization:
type: object
properties:
data:
type: object
properties:
name:
type: string
parent_organization:
type: string
format: url
child_organizations:
type: array
items:
type: string
format: url
What about POST /organizations for creating a new user? Should the user specify parent and children as urls too?
I suggest you use urls rather than some ids. The advantage of having actual urls is that you can change them dynamically without worrying about the clients who depend on some base urls and then have to compute the actual urls from ids, etc.
For documentation purpose you can treat urls as strings and explain them like other params.

Property named "type" on embedded records with RESTSerializer in Ember Data - Error no model was found

I use Ember Data 1.13.4.
I have a model with some embedded records, let's say they look like this:
var Partner = DS.Model.extend({
name: DS.attr(),
addresses: DS.hasMany('address', { async: false } ),
});
var Address = DS.Model.extend({
type: DS.attr(),
zip: DS.attr(),
city: DS.attr(),
street: DS.attr()
});
The API sends back the Address records embedded in the Partner records. Example response:
{
"partners": [
{
"id": 47,
"name": "Johnny",
"addresses": [
{
"id": 7,
"type": "MAIN",
"zip": "1234",
"city": "City-X",
"street": "6. Nowhere"
}
],
},
]
}
The problem is that type on the Address model is just a normal property, but Ember Data wants it to be the type of the embedded model, and I get this assertion message:
Error: No model was found for 'MAIN'
Where 'MAIN' is the content of the type property.
I can't change how the API sends back data, I need to adapt to it.
How do I do this?
Edit: Important detail which I forgot to include the first time.
The API has a /partners/search endpoint, which I access with a direct ajax request, then I (supposedly) need to call this.store.pushMany('partner', this.store.normalize('partner', response.partners)); And this is when the No model was found for 'MAIN' is raised.
Your { partners: { addresses: [ ... ] } } is returning an embedded data, but because you are providing an object with id and type ember-data is understanding that the api returns an polymorphic association. And trying to find a model called MAIN, but since it does't exist Error: No model was found for 'MAIN' is raised.
In order to tell ember-data that your relationship is embedded you need to override the PartnerSerializer and include the DS.EmbeddedRecordsMixin.
App.ApplicationAdapter= DS.RESTAdapter;
App.ApplicationSerializer = DS.RESTSerializer;
App.PartnerSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
addresses: { embedded: 'always' }
}
})
A live demo of this sample running http://emberjs.jsbin.com/nufofehota/1/edit?html,js,output

Rest API get resource id by field

What is a correct rest way of getting a resource ID by a field, for example a name. Take a look at the following operations:
GET /users/mike-thomas
GET /users/rick-astley
I don't want to use these operations at my API end, instead I want to write an API operation that will get me the ID when submitting a field (name in the case of users) for example:
GET /users/id-by-field
Submitted data:
{
"fullName": "Mike Thomas"
}
Return data:
{
"data": {
"id": "123456789012345678901234"
}
}
What you want is known as an algorithmic URL where the parameters for the algorithm are passed as URL parameters:
GET /users?name="Mike Thomas"
Advantages are that you are using the "root" resource (users) and the search parameters are easily extended without having to change anything in the routing. For example:
GET /users?text="Mike"&year=1962&gender=M
where text would be searched for in more than just the name.
The resultant data would be a list of users and could return more than the identification of those users. Unless fullName uniquely identifies users, that is what you need to allow for anyway. And of course the list could contain a single user if the parameters uniquely identified that user.
{
users: [
{
id: "123456789012345678901234",
fullName: "Mike Thomas",
dateJoined: 19620228
}
, {
id: "234567890123456789012345"
fullName: "Rick Astley",
dateJoined: 19620227
}
]
}