Fetching data from multiple APIs using Graphql / REST - api

I have design question, on the approach that should fetch information about an entity from multiple applications and publish the result to the frontend at real-time. All of these applications can produce results abiding to a particular schema.
Currently I am using GraphQL, to combine the results from both the application source and concatinating the result to the frontend for users to compare and validate the data.
Ex: The query below, fetches the information of a client from two different applications through RESTful data source
const resolvers = {
Query: {
client: async (_source, { clientId }, { dataSources }) => {
clientApp1 = await dataSources.App1ClientsAPI.getClient(clientId);
clientApp2 = await dataSources.App2ClientsAPI.getClient(clientId);
const client = [].concat(clientApp1, clientApp2);
return client;
},
},
};
Schema
type Client {
ssnNumber: String
firstName: String
lastName: String
dateofBirth: String
clientId: Float
source: String
contactList: [Contact]
addressList: [Address]
}
Currently this approach is working fine for top level entity like Client, but when we go to bottom of the chain, like contacts, address, transactions, the number of resultset returned expands drastically. With this approach I see 2 drawbacks, pretty sure, there could be more, but these are the once which are critical for me
Unable to add a filter in the backend Ex: return only App1 data
Unable to control number of records returned by each application at the lower level entities [pagination]
Question
1.Should I modify the approach to use Schema Stiching or GraphQL federation for better performance
2.Any better options outside of Graphql that I should look for, for combining the results from Multiple API's

Related

How can I get and use the properties I need from this GraphQL API using Dart?

Before you start reading: I have looked at the GraphQL documentation, but my usecase is so specific and I only need the data once, and therefore I allow myself to ask the community for help on this one to save some time and frustration (not planning to learn GraphQL in the future)
Intro
I am a CS student developing an app for Flutter on the side, where I need information about the name and location of every bus stop in a specific county in Norway. Luckily, there's an open GraphQL API for this (API URL: https://api.entur.io/stop-places/v1/graphql). The thing is, I don't know how to query a GraphQL API, and I do not want to spend time learning it as I am only going to fetch the data once and be done with it.
Here's the IDE for the API: https://api.entur.io/stop-places/v1/ide
And this is the exact query I want to perform as I want to fetch bus stops located in the county of Trondheim:
{
stopPlace(stopPlaceType: onstreetBus, countyReference: "Trondheim") {
name {
value
}
... on StopPlace {
quays {
geometry {
coordinates
}
}
}
}
}
The problem with this query though, is that I don't get any data when passing "Trondheim" to the countyReference (without countyReference I get the data, but not for Trondheim). I've tried using the official municipal number for the county as well without any luck, and the documentation of the API is rather poor... Maybe this is something I'll have to contact the people responsible for the API to figure out, which shouldn't be a problem.
But now back to the real problem - how can I make this query using the GraphQL package for Dart? Here's the package I'm planning to use: (https://pub.dev/packages/graphql)
I want to create a bus stop object for each bus stop, and I want to put them all in a list. Here is my bus stop model:
class BusStop with ChangeNotifier {
final String id;
final String name;
final LatLng location;
BusStop({
this.id,
this.name,
this.location
});
}
When it comes to authentication, here's what the documentation says:
This API is open under NLOD licence, however, it is required that all consumers identify themselves by using the header ET-Client-Name. Entur will deploy strict rate-limiting policies on API-consumers who do not identify with a header and reserves the right to block unidentified consumers. The structure of ET-Client-Name should be: "company - application"
Header examples: "brakar - journeyplanner" "fosen_utvikling - departureboard" "norway_bussekspress - nwy-app"
Link to API documentation: https://developer.entur.org/pages-nsr-nsr
Would be great to know how I should go about this as well! I'm grateful for every answers to this, I know I am being lazy here as of learning GraphQL, but for my usecase I thought it would take less time and frustration by asking here!
Getting the query right
First of all you seem to have GraphQL quite figured out. There isn't really much more to it than what you are doing. What queries an API supports depends on the API. The problem you seem to have is more related to the specific API that you are using. I might have figured the right query out for you and if not I will quickly explain what I did and maybe you can improve the query yourself:
{
stopPlace(stopPlaceType: onstreetBus, municipalityReference: "KVE:TopographicPlace:5001") {
name {
value
}
... on StopPlace {
quays {
geometry {
coordinates
}
}
}
}
}
So to get to this I started finding out more about "Trondheim" bei using the topographicPlace query.
{
topographicPlace(query: "Trondheim") {
id
name {
value
}
topographicPlaceType
parentTopographicPlace {
id
name {
value
}
}
}
}
If you do that you will see that "Trondheim" is not a county according to the API: "topographicPlaceType": "municipality". I have no idea what municipality is but the is a different filter for this type on the query that you provided. Then putting "Trondheim" there didn't yield any results so I tried the ID of Trondheim. This now gives me a bunch of results.
About the GraphQL client that you are using:
This seems to be an "Apollo Client" clone in Dart. Apollo Client is a heavy piece of software that comes with a lot of awesome features when used in a frontend application. You probably just want to make a single GraphQL request from a backend. I would recommend using a simple HTTP client to send a POST request to the GraphQL API and a JSON body (don't forget content type header) with the following properties: query containing the query string from above and variables a JSON object mapping variable names to values (only needed if you decide to add variables to your query.

Merging data from different graphql resolvers in vue.js client side for simple outputting

I do query cars from an api with a single query but two resolvers (listing and listings)(hopefully resolver is the right name for it). One car I get by the id via listing and the other cars I get without filters by listings. The resolvers output the data i a little different structure on the server-side but I do get the same fields just at different „places“. I want to merge the structure in order to get a single array I can simply loop over in vue.js. For the apicalls I do use vue-apollo.
Couldn't find any information to merge data client-side inside graphqlqueries. All I found is about handling it serverside with resolvers but it's an api I do not own.
Is it possible with graphql or do I have to merge it inside my vuecomponent and if so what would be the best way to do so?
The output will be a grid of cars where I show the car of the week (requested by id) together with the newest cars of the regarding cardealer.
Full screenshot including response: https://i.imgur.com/gkCZczY.png
Stripped down example with just the id to show the problem:
query CarTeaser ($guid: String! $withVehicleDetails: Boolean!) {
search {
listing(guid: $guid){
details{
identifier{
id #for example: here I get the id under details->identifier
}
}
}
listings( metadata: { size: 2 sort:{ field: Age order: Asc}}) {
listings{
id #here it's right under listings
details{
…
}
}
}
}
}
}
Ideally you're right, it should be handled server-side, but if it's not your API the only solution is to manipulate the data on the client side, meaning in your component.
It's probably a lot simpler to leave the listings array untouched and to just merge the listing element with it, like this for instance:
// assuming 'search' holds the entire data queried from the api
const fullListing = [
// car of the week, data reformatted to have an identical structure as
// the 'other' cars
{
id: search.listing.details.identifier.id,
details: {
vehicle: search.listing.details.vehicle,
},
},
...search.listings.listings, // the 'other' cars
]

Designing of a RESTful API endpoint filter and search

I am in the process of developing some custom API endpoints (using loopback.io), on top of its existing CRUD endpoints.
In the past I've used some other Node RESTful API frameworks for prototyping, and really enjoyed the powerful filtering features they provide out of the box.
What I'd like to do is provide a similar set (or subset) of some kind of filtering for a custom endpoint. The endpoint just does a SQL query (with some JOINs) and returns an array of objects.
Is there any kind of standardized approach that I should use to design some filtering? For example, I may want to filter on fields of the base table, or filter on relations. I like the way loopback.io and sequelize allow relatively easy specification of includes to link related objects, as well as their filtering syntax.
How is this type of problem usually approached when a custom implementation is done?
As you probably noticed with CRUD endpoints, LoopBack provides querying out of the box via filter parameter. You can nicely experiment with it in API Explorer. If you want to expose querying for a custom remote method, just add filter as a parameter too.
example-model.js
module.exports = ExampleModel => {
const search = async (filter = {}) => {
return await ExampleModel.find(filter)
}
ExampleModel.remoteMethod('search', {
description: 'Returns a set of ExampleModel based on provided query.',
accepts: [
{arg: 'filter', type: 'object', required: false}
],
http: {path: '/search', verb: 'get'},
returns: {root: true}
})
ExampleModel.search = search
}

Using Odata to get huge amount of data

I have a data source provider :
public class DSProvider
{
public IQueryable<Product> Products
{
get
{
return _repo.Products.AsQueryable();
}
}
}
The repository in the above example currently gets ALL the records (of Products) from DB and then applies the filters, this just does not sound right if you had 50000 requests/sec from a website.How can you limit the repository to just return required info from DB without converting the service to a tightly coupled request option i.e. opposite of what you try to achieve by using oData?
So to summarize I would like to know if its possible to query the DB on the oData options supplied by the user so that my request does not always have to get all products and then apply filters of oData.
I found out after doing a small POC that Entity framework takes care of building dynamic query based on the request.

How do I select most popular objects, e.g list most read articles

I am looking at creating a social reader app on which users can read articles I publish.
On the app home page, I want to list the most read articles.
Is there a way to do this client side using the FB API, or can I get the data from the FB API server side some how and store it?
or do I need to collate the read count in my own data store and create the list server side?
I am planning to have custom objects and actions and I essentially want a list of most popular objects by action
Is there a way to do this client side using the FB API
No, I don't think so.
or can I get the data from the FB API server side some how and store it?
Of course, just save it to a database and do your logic. For example, increment the count for each page in your database table when a user reads the article. Then use an SQL query that pulls the "TOP X"
select top 10 * from articletable order by NumRead asc
When a user makes a call to news.read an action instance id is returned inspect that for the respective article right after making the call
FB.api(
'/me/news.reads',
'post',
{ article: 'URL_TO_ARTICLE' },
function(response) {
if (!response || response.error) {
alert('Error occured');
console.log(response.error);
} else {
FB.api(
response.id, function(response) {
var data = response['data'];
var video = data['article'];
// Send to your database logic article['id'];
});
}
});
Or you skip the second API call altogether and just save the current page in your table and increment its count.