The API that I am connecting to isn't using JSON-API for its response format, so I am having to serialize the responses for my Ember app.
The problem I am having is that my response from the API looks like this:
{
#type: "application/x.app-name.nurse-collection+json",
#type.item: "application/x.app-name.nurse+json",
#type.collection: "application/x.app-name.nurse-collection+json",
#href: "http://192.168.33.10:3000/api/nurses{?includePatients}",
#item: "http://192.168.33.10:3000/api/nurses/{id}",
items: [
{
#type: "application/x.app-name.nurse+json",
#type.item: "application/x.app-name.nurse+json",
#type.collection: "application/x.app-name.nurse-collection+json",
#href: "http://192.168.33.10:3000/api/nurses/1{?includePatients}",
id: 1,
Nurse: "Maria Holmes",
NurseTelephoneNo: "123 4567",
NurseMobileNo: "0123345566",
Email: "someone#example.com",
Patients: {
#type: "application/x.app-name.patient-collection+json",
#href: "http://192.168.33.10:3000/api/nurses/1/patients{?name,gender,includeNurse,includeEvents,includeReferrals,includeReviews,includePanels,includeRetrospectives,includePhbs,includeDisputeStage2s,includeDisputeResolutionPanels,includeEthnicity,includeFirstLanguage,includeMaritalStatus,includeReligion,includeSecurityLevel,includeSexualOrientation,includeWhichReview}",
#rel: "http://192.168.33.10:3000/api/nurses/1/patients{?name,gender,includeNurse,includeEvents,includeReferrals,includeReviews,includePanels,includeRetrospectives,includePhbs,includeDisputeStage2s,includeDisputeResolutionPanels,includeEthnicity,includeFirstLanguage,includeMaritalStatus,includeReligion,includeSecurityLevel,includeSexualOrientation,includeWhichReview}"
}
},
{
#type: "application/x.app-name.nurse+json",
#type.item: "application/x.app-name.nurse+json",
#type.collection: "application/x.app-name.nurse-collection+json",
#href: "http://192.168.33.10:3000/api/nurses/2{?includePatients}",
id: 2,
Nurse: "Julie Smart",
NurseTelephoneNo: "543 1234",
NurseMobileNo: null,
Email: "someone#example.com",
Patients: {
#type: "application/x.app-name.patient-collection+json",
#href: "http://192.168.33.10:3000/api/nurses/2/patients{?name,gender,includeNurse,includeEvents,includeReferrals,includeReviews,includePanels,includeRetrospectives,includePhbs,includeDisputeStage2s,includeDisputeResolutionPanels,includeEthnicity,includeFirstLanguage,includeMaritalStatus,includeReligion,includeSecurityLevel,includeSexualOrientation,includeWhichReview}",
#rel: "http://192.168.33.10:3000/api/nurses/2/patients{?name,gender,includeNurse,includeEvents,includeReferrals,includeReviews,includePanels,includeRetrospectives,includePhbs,includeDisputeStage2s,includeDisputeResolutionPanels,includeEthnicity,includeFirstLanguage,includeMaritalStatus,includeReligion,includeSecurityLevel,includeSexualOrientation,includeWhichReview}"
}
},
...
]
}
So in this example, patients could be a collection, but I don't have the id's for the hasMany relationship until I have gone to that endpoint.
I'm wondering if there is a way to serialize this in a way that is JSON-API compliant, but will let me load relationship data asynchronously, once my app has decided to go to the patients endpoint.
I would write a custom serializer that fits this response type instead of converting it to the JSON-API spec.
It doesn't look like there's any information here about the patient records (ie a list of ids), so ignore it here unless you want to set the link attribute for the hasMany.
Loading relationship data asynchronously will work fine with ember-data. When you get an array of patient records and add them to the store they will be available with nurse.get('patients') as long as the loaded records each have a nurse_id.
Related
In loopback framework, is there a way to avoid updates for few fields
Below code allows updates for all fields that is passed in the API request body.
async updateById(
#param.path.number('id') id: number,
#requestBody({
content: {
'application/json': {
schema: getModelSchemaRef(Todo, {partial: true}),
},
},
})
todo: Todo,
): Promise<void> {
await this.todoRepository.updateById(id, todo);
}
As far as I understand from your question, you want to update some part of the object in the database.
this.repo.updateById(id,objectYouWantToUpdate)
This is going to work perfectly, just send the data you want to update and not the whole object.
exclude key can help
schema: getModelSchemaRef(Todo, {partial: true, exclude: ['title']})
I'm using NestJS + Prisma + Apollo Federation.
On microservice A is the definition of user, on microservice B is defined posts.
The relation is 1 - N, a user can have N posts.
In Prisma, datamodel of Post is defined with a String for user, since userId is a uuid.
type Post {
id: Int! #id
createdAt: DateTime! #createdAt
updatedAt: DateTime! #updatedAt
user: String!
}
In generated schema (with https://graphql-code-generator.com), Post has a attribute of type User, and this type User extends the id and a array of posts:
type Post #key(fields: "id") {
id: Int!
createdAt: DateTime!
updatedAt: DateTime!
user: User!
}
extend type User #key(fields: "id") {
id: ID! #external
posts: [Post]
}
In apollo federation, all works as expected, except when a query is made trying to link between both microservices.
On playground, if you try to query posts with its user without setting subfields, it breaks the schema and say you have to set the subfields of User, and if you set the subfields graphql responds with a message that you cannot use subfields because its type is String.
The only way that I could make this work correctly was setting in Prisma a userId field of type string and setting another field in schema called user of type User. But all the examples didn't show a field to work with db and a field to work with schema.
My question is if that is the recommended or am I missing something.
In order to get User from Post, you have to create a resolver in your post and user service.
Post Service
const resolvers = {
Post:{//before you do this you have to extend User schema which you already did.
// you are basically asking the 'User' service, which field should be used to query user.
user: ref => ({ __typename: 'User', id: ref.userId })
}
Query:{
// query resolvers
},
Mutation:{
// mutation resolvers
}
User service
const resolvers = {
User:{//the code below allows other services to extend User in their own schemas
__resolveReference: (ref, { userDataLoader }) => userDataLoader.load(ref.id),
}
Query:{
// query resolvers
},
Mutation:{
// mutation resolvers
}
Now linking arrays like [Post] must be done purely in the post service
Post Service
const resolvers = {
Post:{//before you do this you have to extend User schema which you already did.
// you are basically telling the user service, which field should be used to query user.
user: ref => ({ __typename: 'User', id: ref.user })
},
User:{
posts:(ref, args, {postDataLoader}) => getOrders(ref.postIds) //or ref.userId(foreign key)
},
Query:{
// query resolvers
},
Mutation:{
// mutation resolvers
}
I have JSON coming from the server which looks like:
data: {
user: {
address: {
id: "id",
city: "city",
street: "street",
.......
}
name: "name",
......
}
authentication-token: {
token: "token",
id: "id"
}
}
The idea is to store this two models (user, authentication-token) in ember store under the same names. When I gat the above mentioned response from a server, model user is saved successfully, but model authentication-token does not get saved to the store at all. When I log the data (in the adapter) before the data is passed to serializer I see that JSON has a structure which Ember-Data expects. I don't know whether the problem is that Ember-Data cannot handle two models in success at one time, and then save it to the corresponding models, or something else. Ideas?
Now it all makes sense to me. Of course, this was the problem in your last question. Anyway, ember-data's RESTAdapter can't handle this. If you're requesting a singular resource user it expects at most this user as a singular answer. Any other resource that may be "side-loaded" has to be an array. The requested singular user can either be a record under the user key or the first entry in an array unter the users key. Everything else (except meta data) has to be plural.
In case you're requesting a plural resource users, the main response must be under the users key, any sideloaded users that aren't part of the response prfixed with _ under the _users key.
For your example all this means that your json must be formatted like this:
data: {
user: {
address: {
id: "id",
city: "city",
street: "street",
.......
}
name: "name",
......
}
authentication-tokens: [{
token: "token",
id: "id"
}]
}
If you can't change your server, you'd have to configure the RESTAdapter to normalize the JSON data through normalize of the Serializer.
Trying to use loopback framework for simulating a backend service. I need to retrieve an object using POST method. I know REST services typically allow POST to update/create a resource, but here, I cannot use GET with resource details for retrieving data.
In my case, POST data contains a few query fields that have to be used to query an object and send json back. Is this possible with loopback? I cannot use GET with query parms due to security restrictions with sending data as query parms in a GET URL.
here is post request data
[ { customer:"sam", city:"noWhere", } ]
the POST event should query by customer and city, then return matching customer object
[ { customer:"sam", postcode:"352345", city:"noWhere", country:"US" } ]
I think that what you need is an express http method override middleware: https://github.com/expressjs/method-override
And defining middleware in loopback:
http://docs.strongloop.com/display/LB/Defining+middleware
You can override default loopback endpoint, like this
// Define custom remote method
Customer.fetch = function(oRequest, fnResponseCb) {
/* Do staff to find customer and finally call fnResponseCb(null, oCustomer) */
}
// Override custom remote method
Customer.remoteMethod('fetch', {
accepts: {
arg: 'oRequest',
type: 'object',
http: { source: 'body' }
},
returns: {
type: 'object',
root: true
},
http: {
path: '/',
verb: 'POST'
},
description : 'Fetch Customer'
});
I'm building an adapter to wrap the Keen.io API, so far I've been able to successfully load the projects resource, however the returned object looks like this:
{
partners_url: "/3.0/projects/<ID>/partners",
name: "Project Name",
url: "/3.0/projects/<ID>",
saved_queries: [ ],
events_url: "/3.0/projects/<ID>/events",
id: "<ID>",
organization_id: "<ORG ID>",
queries_url: "/3.0/projects/<ID>/queries",
api_key: "<API KEY>",
events: [
{
url: "/3.0/projects/<ID>/events/user_signup",
name: "user_signup"
},
{
url: "/3.0/projects/<ID>/events/user_converted",
name: "user_converted"
},
{
url: "/3.0/projects/<ID>/events/user_created_project",
name: "user_created_project"
}
]
}
Excluding a lot of cruft, Ember has no trouble mapping the name and id attributes using the RESTSerializer, but if I add an events relation to my Project model it blows up with:
Error while loading route: TypeError: Cannot set property 'store' of undefined
at Ember.Object.extend.modelFor (http://localhost:3000/assets/ember-data.js?body=1:9813:23)
at Ember.Object.extend.recordForId (http://localhost:3000/assets/ember-data.js?body=1:9266:21)
at deserializeRecordId (http://localhost:3000/assets/ember-data.js?body=1:10197:27)
at deserializeRecordIds (http://localhost:3000/assets/ember-data.js?body=1:10211:9)
at http://localhost:3000/assets/ember-data.js?body=1:10177:11
at http://localhost:3000/assets/ember-data.js?body=1:8518:20
at http://localhost:3000/assets/ember.js?body=1:3404:16
at Object.OrderedSet.forEach (http://localhost:3000/assets/ember.js?body=1:3247:10)
at Object.Map.forEach (http://localhost:3000/assets/ember.js?body=1:3402:10)
at Function.Model.reopenClass.eachRelationship (http://localhost:3000/assets/ember-data.js?body=1:8517:42)
From my investigation this seems to be because it can't find the inverse relation to map an Event back to a Project because there's no parent ID.
Is it possible to create a relation in Ember Data to support this? Or is it possible to modify the Serializer to append a projectId to each event before loading?
I'm using Ember 1.5.0-beta.4 and Ember Data 1.0.0-beta.7+canary.f482da04.
Assuming your Project model is setup the following way:
App.Project = DS.Model.extend({
events: DS.hasMany('event');
});
You need to make sure that the JSON from your API is in a certain shape that Ember-Data expects.
{
"project": {
"id": 1,
"events": ["1", "2"],
},
"events": [{
"id": "1",
"name": "foo"
}, {
"id": "2",
"name": "bar"
}]
}
You can, however, implement extractArrayin your model's serializer to transform the JSON from the server into something similar like the above example.
There's a working example and an explanation in the Ember docs.