JSON API Standard - json-api

I have a doubt what is a better option when using json api standard and communication between backend and frontend. I need only one attribute from author association - „username” and other stuff should be hidden for user that fetch this
Case a)
data: [
{
id: „100”,
type: „resource1”,
attributes: {…},
relationships: {author: {data: {id: „10”, type: „author”}}}
}
],
included: [
{
id: „10”,
type: „author”,
attributes: {username: „name”},
relationships: {resources1: {data: [{id: „100”, type: „resource1”}]}}
}
]
Case b)
data: [
{
id: „100”,
type: „resource1”,
attributes: {authorName: „name”, …},
relationships: {author: {data: {id: „10”, type: „author”}}}
}
],
included: []
Case a) looks semantic but there serve much more information in payload
Case b) is faster to get what I want from author (one attribute „username” and this is added in additional attribute: „authorName”), so also don’t need to pleas with associations in frontend side.
Any thoughts which is better practice and why?

Strictly speaking both case a and case b are valid per JSON:API specification.
In case a username is an attribute of author resource. In case b authorName is an attribute of resource1. author resource may have a username attribute in case b as well. In that case you have duplicated state.
I would recommend to only use duplicated state if you have very good reasons. Duplicated state increases complexity - both on server- as well as client-side. Keeping both attributes in sync comes with high costs. E.g. you need to update a client that resource1 changed after a successful update request, which affected the username of author resource. And the client need to parse that response and update the local cache.
There are some reasons, in which duplicating state pays off for good reasons. Calculated values, which would require a client to fetch many resources to calculate them, is a typical example. E.g. you may decide to introduce a averageRating attribute on a product resource because without a client would need to fetch all related ratings of a product only to calculate it.
Trying to reduce payload size is nearly never a good reason to accept the increased complexity. If you consider compressing and package sizes at network level, the raw payload size often doesn't make a big difference.

Related

JSON API V1 Creating Nested Resources

I am implementing a JSON API V1 compliant API using Grape API that uses AR as the ORM. I am a little confused on the format to create nested resources / relationships. It looks like we need to create one resource at a time and can link to exisitng resources. But we can't lets say create records for a has many relationship int he same request.
My situation: There is the Donation modal. It has many Splits. A split belong to a Fund. I need to create a donation with multiple splits.
Question: How can I structure the API according to the JSON:API recommendations?
Going through the documentation few times, I am thinking I can't make the donation in one API call and will have to create each resource separately and may be run a final commit to to trigger the donation.
Step 1: Create the donation - assume returns ID 100
POST /api/v1/donations
{ type: "donation", data: { comments: "abc" } }
Step 2: Create Split 1
POST /api/v1/splits
{
type: "split",
data: { amount: 100_00 },
relationships: {
data: [{ type: "fund", id: 5 }, { type: "donation", id: 100 }]
}
}
Finally: trigger the donation with some thing like
PATCH /api/v1/donations/100
{
type: "donation"
data: {
state: "process"
}
}
Is there a way to create it in one request?
JSON:API specification does not support creating, updating or deleting multiple resources in one request in v1.0, which is the current stable version.
The upcoming v1.1 is planned to support Extensions, which allow to extend the base specification. There is a proposal of a Atomic Operations extension provided by one of the maintainers of the specification. It's planned to be released with v1.1 as official extension.

Use PUT or PATCH verb when request is updating and deleting: Real life design

I've been trying to get the HTTP verbs right lately, however I have a doubt regarding using PUT, PATCH or even POST for the following scenario.
The front end part is sending the following JSON data:
{
name: "Spanish-01",
code: "ESP01",
students: [{
IdStudent: 1,
name: "Peter Parker"
},
{
IdStudent: 2
name: "Ben Reilly",
dirtyRemove: true
}]
}
The back end code will update the Class record (e.g name and code). However, it will also delete the students with flag dirtyRemove, and those live in another table called Student.
So what's the rule here? Since PUT and PATCH according to w3.org here is for updating an existing resource. In this case the back end is both updating and deleting at the same time?
Should I use PUT or PATCH or neither?
NOTE: Don't mind about the FE part, I minimized the scope in order to get a more straightforward example
How your resources are implemented internally using tables is an implementation detail. It doesn't matter.
That said, your example payload doesn't fit PUT (to remove a student, you would omit it). It might fit PATCH, if you properly label the payload with a content type describing what semantics you expect.
Nit: the HTTP spec is not a W3 document, and the version you're looking at is outdated.

Why use DELETE/POST instead of PUT for 'unfollowing/following' a user?

Referencing this API tutorial/explanation:
https://thinkster.io/tutorials/design-a-robust-json-api/getting-and-setting-user-data
The tutorial explains that to 'follow a user', you would use:
POST /api/profiles/:username/follow.
In order to 'unfollow a user', you would use:
DELETE /api/profiles/:username/follow.
The user Profile initially possesses the field "following": false.
I don't understand why the "following" field is being created/deleted (POST/DELETE) instead of updated from true to false. I feel as though I'm not grasping what's actually going on - are we not simply toggling the value of "following" between true and false?
Thanks!
I think that the database layer have to be implemented in a slightly more complex way than just having a boolean column for "following".
Given that you have three users, what would it mean that one of the users has "following": true? Is that user following something? That alone cannot mean that the user is following all other users, right?
The database layer probably consists of (at least) two different concepts: users and followings; users contain information about the user, and followings specify what users follow one another.
Say that we have two users:
[
{"username": "jake"},
{"username": "jane"}
]
And we want to say that Jane is following Jake, but not the other way around.
Then we need something to represent that concept. Let's call that a following:
{"follower": "jane", "followee": "jake"}
When the API talks about creating or deleting followings, this is probably what they imagine is getting created. That is why they use POST/DELETE instead of just PUT. They don't modify the user object, they create other objects that represent followings.
The reason they have a "following": true/false part in their JSON API response is because when you ask for information about a specific user, as one of the other users, you want to know if you as a user follows that specific user.
So, given the example above, when jane would ask for information about jake, at GET /api/profiles/jake, she would receive something like this:
{
"profile": {
"username": "jake",
"bio": "...",
"image": "...",
"following": true
}
}
However, when jake would ask for the profile information about jane, he would instead get this response:
{
"profile": {
"username": "jane",
"bio": "...",
"image": "...",
"following": false
}
}
So, the info they list as the API response is not what is actually stored in the database about this specific user, it also contains some information that is calculated based on who asked the question.
Using a microPUT would certainly be a reasonable alternative. I don't think anybody is going to be able to tell you why a random API tutorial made certain design decisions. It may be that they just needed a contrived example to use POST/DELETE.
Unless the author sees this question, I expect it's unanswerable. It's conceivable that they want to store meta information, such as the timestamp of the follow state change, but that would be unaffected by POST/DELETE vs. PUT.

Putting the HTTP specification aside, what is the main reason or advantage of appending the id of a resource in HTTP PUT?

If I can do the following when creating a record,
URI: /api/roles/
Method: POST
Content: { Id: 0, Name: 'Admin', Description: 'Administration Role' }
Can I just do this if I want to update a record?
URI: /api/roles/
Method: PUT
Content: { Id: 1001, Name: 'Admin', Description: 'Administration Role' }
...because I can just get the identifier in the Id field.
Instead of doing this?
URI: /api/roles/1001
Method: PUT
Content: { Id: 1001, Name: 'Admin', Description: 'Administration Role' }
...specifying the Id value twice. By the way, I cannot remove the Id field in the content.
If not, why? What is the main reason or advantage of appending the id of a resource in HTTP PUT, putting the HTTP specification aside?
Adding ID resource denotes one of the steps of RESTful API
You could look for further discussion here : What is the advantage of using REST instead of non-REST HTTP?
RESTful design principles specify HATEOAS which roughly states that interaction with an endpoint should be defined within metadata that comes with the output representation and not based on out-of-band information.
http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api
I agree there seems to be a strange redundancy in specifying the ID in the URL as well as the request body.
As I see it, the reason is not technical, but 'cultural'.
Whenever the URL is /api/roles/ you indicate that the list is being manipulated, whereas /api/roles/{id} clearly points to the target being a list item.

How do I prevent updates to nonexistent nodes?

I am using the REST api to PATCH nodes in my Firebase store. I would like to prevent updates to nodes that do not exist (because they were previously deleted). Right now, doing a PATCH to a nonexistent reference recreates it.
I looked into setting security rules, but newData.exists() does not discriminate between setting a new value and patching, so I couldn't figure out how to allow what I want without restricting new creations.
I can get a snapshot of the reference and check that before PATCHING, but I was hoping there was a more elegant way to do it without using two REST calls.
EDIT: some code!
My Firebase schema looks like:
requests:
rq123:
id: '123'
sender: '1'
recipient: '2'
expiration: '1234567',
filled: false,
filledDate: '',
New requests are written from a mobile client. My server can make updates to those request entries using the REST api. Using the python-firebase library, that looks like:
request_ref = firebase_root + '/requests/' + request.id
patch_data = {
'filled':'true',
'filled_date':'7654321'
}
firebase_conn.patch(request_ref, patch_data)
Given the design of my app, I'd like to only perform that patch if the request entry still exists. It's clear that I can get a snapshot and perform the check that way before patching, but that seemed awkward to me.
As I already remarked in the comments, there is no difference between these cases:
Writing to a database location that doesn't exist yet
Writing to a database location that doesn't exist anymore
So you will have to create that distinction in your application.
You have a few options. Not all of them apply to the REST API, but I'll mention them for completeness anyway.
Transactional update
The Firebase SDKs (for JavaScript, Java/Android and iOS/OSX) have a method called transaction, which allows you to run a compare-and-set operation.
With that operation you could
ref.transaction(function(current) {
if (current) {
current.filled: true,
current.filled_date:'7654321'
}
return current;
});
But since this method is not available on the REST API, it doesn't apply to your scenario.
Mark deleted records
Alternatively you can mark deleted records, instead of actually deleting them:
requests:
rq123:
id: '123'
sender: '1'
recipient: '2'
expiration: '1234567'
filled: false
filledDate: ''
DELETED: true
You could also delete all the other properties when you pseudo-delete the request, i.e.
requests:
rq123:
DELETED: true
Then in your security rules, you can reject the write operation when this flag is present:
".write": "data.child('DELETED').val() != true"
There are many ways to flag the record. For example it seems like in your case, the record-node will always have an id property. So you could also simply leave the record-node as a marker, but remove all its properties:
requests:
rq123: true
Since Firebase deleted nodes that don't have a value, I put true in here as the values.
With the above structure, we can only allow writes that either have an id property (which is the case when you create the request) or when an id property is already present (the PATH request from the REST API):
".write": "newData.child('id').exists() || data.child('id').exists()"
Keep a list of deleted nodes
My final approach would be to keep a list of the deleted request keys:
requests:
rq123:
id: '123'
sender: '1'
recipient: '2'
expiration: '1234567'
filled: false
filledDate: ''
deleted:
rq456: true
rq789: true
Once again, we set a dummy value of true for the deleted nodes to prevent Firebase from deleting them.
With this structure, you can reject write operations when the key you're writing to exists in the list of deleted requests:
".write": "!root.child('deleted').child(newData.key()).exists()"
Each approach has its own advantages and disadvantages, so you'll have to decide for yourself which one is best for your scenario.

Resources