Good day!
Just want to ask on how to create a JSONAPISerializer for an ajax call?
From what I understand on the docs. I must first create a model before I can make a JSONAPISerializer. But I need to call a custom endpoint that is not listed as my model.
My goal is to pushPayload all the sideloaded data coming from my endpoint. but the problem is :
{
"data":[
{
"type":"promotions-verify", <----- it's not an actual model
"attributes":{
"cart-items-discount-in-cents":21900
},
"relationships":{...}, <---- sideloaded data that I want to push on ember-data
}],
"included": [] <---- sideloaded data that I want to push on ember-data
}
Is there a reason you can't make a model for promotions-verify? That would be the cleanest way to implement loading in the side-loaded data, since Ember would handle much of the serialization/pushing to the store for you.
If that isn't possible and you make an ajax request, you may need to map the relationships and included payloads to match up with one another (Lodash _.map() could work well for this). Then you can manually push that data (pushPayload) to the Ember store, while ensuring that the items you're pushing also have models and serializers.
Also, I'm not sure if this was accidental, but your example payload doesn't conform to JSON API standards – the relationships object should be nested within data. This will affect how Ember serializes the data, as it's expecting:
{
"data": [{
"id": 1,
"type": "promotions-verify",
"attributes": {},
"relationships": {}
}],
"included": []
}
Related
I hope you’re doing well.
So, I’m creating a database-driven website about Portuguese dubs and using Strapi as backend and CMS.
I created a collection type name Movies that has, amongst other things, a repeatable component for the cast.
This repeatable component is made of a relations component with the Voice Actors collection (one to one) and a text field with the character he played.
When I call the API for a certain movie and populate Cast, it retrieves me the cast. However, it retrieves me the wrong ID for the Voice Actor.
For example, my Voice Actors’ ID go from 16 to 20, whoever the API is returning 10, 11, 12…
How do I retrieve the Voice Actors’ ID?
so let's make it clear you have a collection movies, witch has repeatable components casts witch has relation to an actor.
So id that you have in your screenshot is likely an id of component, to get full data you would need to populate at list two levels deep:
const query = qs.stringify(
{
populate: {
casts: {
populate: ['actor'],
},
},
},
{
encodeValuesOnly: true,
}
);
witch would give request like this:
http://localhost:1337/api/movies?populate[casts][populate][0]=actor
I'm writing a controller that handles an array of object, something like:
#PostMapping("/post")
public void saveEmployeeArray(#RequestBody Emoloyee[] employeeArray)
{
// Method body
}
Keep in mind that, in this case, employees are usually created, update, or deleted in bulk, meaning, I usually need to handle multiple employees at the same time.
A colleague told me that although it works I should only handle one employee in the controller as, according to him, it's rest best practice. But he didn't present an alternative to the issue of having to handle multiple employees most of the time, other than making multiple requests.
My question is, how is the best way to handle multiple objects?
If your list of Employee is inside another data structure (e.g. company), you can offer PATCH operation for outer resource to add or change a list of Employees. JSON PATCH is specified in RFC 6902, see https://datatracker.ietf.org/doc/html/rfc6902/
The JSON body of a PATCH request would look like this:
[
{ "op": "add", "path": "/employees", "value": { "name": "Employee 1", ...} },
{ "op": "add", "path": "/employees", "value": { "name": "Employee 2", ...} },
...
]
The body is a list of PATCH operations to change the addressed data structure at several places within one request, so it fits to your requirement.
The specification in RFC 6902 offers more than just adding elements, you can also remove, replace, move, copy and test. Have a look in the RFC for good and simple examples.
Let's say my graphql server wants to fetch the following data as JSON where person3 and person5 are some id's:
"persons": {
"person3": {
"id": "person3",
"name": "Mike"
},
"person5": {
"id": "person5",
"name": "Lisa"
}
}
Question: How to create the schema type definition with apollo?
The keys person3 and person5 here are dynamically generated depending on my query (i.e. the area used in the query). So at another time I might get person1, person2, person3 returned.
As you see persons is not an Iterable, so the following won't work as a graphql type definition I did with apollo:
type Person {
id: String
name: String
}
type Query {
persons(area: String): [Person]
}
The keys in the persons object may always be different.
One solution of course would be to transform the incoming JSON data to use an array for persons, but is there no way to work with the data as such?
GraphQL relies on both the server and the client knowing ahead of time what fields are available available for each type. In some cases, the client can discover those fields (via introspection), but for the server, they always need to be known ahead of time. So to somehow dynamically generate those fields based on the returned data is not really possible.
You could utilize a custom JSON scalar (graphql-type-json module) and return that for your query:
type Query {
persons(area: String): JSON
}
By utilizing JSON, you bypass the requirement for the returned data to fit any specific structure, so you can send back whatever you want as long it's properly formatted JSON.
Of course, there's significant disadvantages in doing this. For example, you lose the safety net provided by the type(s) you would have previously used (literally any structure could be returned, and if you're returning the wrong one, you won't find out about it until the client tries to use it and fails). You also lose the ability to use resolvers for any fields within the returned data.
But... your funeral :)
As an aside, I would consider flattening out the data into an array (like you suggested in your question) before sending it back to the client. If you're writing the client code, and working with a dynamically-sized list of customers, chances are an array will be much easier to work with rather than an object keyed by id. If you're using React, for example, and displaying a component for each customer, you'll end up converting that object to an array to map it anyway. In designing your API, I would make client usability a higher consideration than avoiding additional processing of your data.
You can write your own GraphQLScalarType and precisely describe your object and your dynamic keys, what you allow and what you do not allow or transform.
See https://graphql.org/graphql-js/type/#graphqlscalartype
You can have a look at taion/graphql-type-json where he creates a Scalar that allows and transforms any kind of content:
https://github.com/taion/graphql-type-json/blob/master/src/index.js
I had a similar problem with dynamic keys in a schema, and ended up going with a solution like this:
query lookupPersons {
persons {
personKeys
person3: personValue(key: "person3") {
id
name
}
}
}
returns:
{
data: {
persons: {
personKeys: ["person1", "person2", "person3"]
person3: {
id: "person3"
name: "Mike"
}
}
}
}
by shifting the complexity to the query, it simplifies the response shape.
the advantage compared to the JSON approach is it doesn't need any deserialisation from the client
Additional info for Venryx: a possible schema to fit my query looks like this:
type Person {
id: String
name: String
}
type PersonsResult {
personKeys: [String]
personValue(key: String): Person
}
type Query {
persons(area: String): PersonsResult
}
As an aside, if your data set for persons gets large enough, you're going to probably want pagination on personKeys as well, at which point, you should look into https://relay.dev/graphql/connections.htm
I'm trying to implement a HATEOAS Rest Client using Spring Boot.
Right now, I'm stuck in a point where I need to convert HATEOAS into an actual API URI.
If I post a new object of type Customer like:
{
"name": "Frank",
"address": "http://localhost:8080/address/23"
}
And then I retrieved with a request to http://localhost:8080/api/customer/1`, HATEOAS gives me something like
{
"name": Frank,
"_links": {
"address": {
"href": "http://localhost:8080/api/customer/1/address"
}
}
}
Is it possible to convert a link of the form of http://localhost:8080/api/customer/1/address to an API call like http://localhost:8080/api/address/23 ?
If you see what HATEOS returns after you say,
GET: http://localhost:8080/api/customer/1
is
{
"name": Frank,
"_links": {
"address": {
"href": "http://localhost:8080/api/customer/1/address"
}
}
}
According to Understanding HATEOS,
It's possible to build more complex relationships. With HATEOAS, the output makes it
easy to glean how to interact with the service without looking up a specification or
other external document
which means,
after you have received resource details with
http://localhost:8080/api/customer/1
what other operations are possible with the received resource those will be shown for easier/click thru access to your service/application,
here in this case HATEOS could find a link http://localhost:8080/api/customer/1/address that was accessible once you have customer/1 and from there if you want then without going anywhere else customer/1 's address could be found with /customer/1/address.
Similarly if you have /customer/1's occupation details then there would be another link below address link called http://localhost:8080/api/customer/1/occupation.
So if address is dependent on customer i.e. there can be no address without customer then your API endpoint has to be /api/customer/1/address and not directly /api/address/23.
However, after understanding these standards and logic behind HATEOS's such responses if you still want to go with your own links that may not align with HATEOS's logic you can use,
Link Object provided by LinkBuilder interface of HATEOS.
Example:
With object of type Customer like:
Customer c = new Customer( /*parameters*/ );
Link link= linkTo(AnyController.class).slash("address").slash(addressId);
customer.add(link);
//considering you want to add link `http://localhost:8080/api/address/23` and `23 is your addressID`.
Also you can create a list of Links and keep adding many such links to that list and then add that list to your object.
Hope this helps you !
I am looking to make some sort of "GenericModel" class extending Eloquent's Model class, that can load database configuration (like connection, table name, primary key column) as well as relationships at runtime based on a configuration JSON file.
My reasons for wanting this are as follows: I'm going to have a lot of database tables and thus a lot of models, but most don't really have any complicated logic behind them. I've developed a generic CRUD API and front-end interface to interact with them. Each model has a "blueprint" JSON file associated with it that describes things like its attributes and relationships. This lets me automatically generate, say, a view to create a new model and it knows what attributes I need to fill in, what input elements to use, what to label them, which are mandatory, how to validate, whether to check for uniqueness, etc. without ever needing code specific to that model. Here's an example, project.json:
{
"db_table": "projects",
"primary_key": "projectid",
"display_attr": "title", // Attribute to display when picking row from list, etc
"attributes": {
"projectid": { // Attribute name matches column name
"display": "ID", // Display this to user instead of db column name
"data_type": "integer" // Can be integer, string, numeric, bool...
},
"title": {
"data_type": "string",
"unique": true // Check for uniqueness when validating field
},
"customer": {
"data_type": "integer", // Data type of local key, matches customer PK
"relationship": { // Relationship to a different model
"type": "manytoone",
"foreign_model": "customer"
},
"user": "autocomplete" // User input element/widget to use, queries customer model for matches as user types
},
"description": {
"data_type": "string",
"user": "textarea" // Big string, use <textarea> for user input
"required": false // Can be NULL/empty, default true
}
},
"views": {
"table": [ // Show only these attributes when viewing table
"customer",
"title"
],
"edit_form": [ // Show these when editing
"customer",
"title",
"description"
],
...
}
}
This works extremely well on the front end, I don't need any more information than this to describe how my models behave. Problem is I feel like I just end up writing this all over again in most of my Model classes and it seems much more natural to have them just pull information from the blueprint file as well. This would result in the information being in one place rather than two, and would avoid extra effort and possible mistakes when I change a database table and only need to update one file to reflect it.
I'd really just like to be able to do something like GenericModel::blueprint('project.json')->find($id) and get a functioning "product" instance. Is this possible, or even advisable? Or is there a better way to do this?
Have you looked at Migrations (was Schema Builder)? It allows you to programatically build models (from JSON if necessary).
Then you could leverage Eloquent on your queries...