I am developing an online game where characters can perform complex actions against other objects and characters. I am building a REST API, and having a lot of trouble trying to follow even some of the most basic standards. I know that REST isn't always the answer, but for a variety of reasons it makes sense for me to use REST since the rest of the API uses it appropriately.
Here are some tricky examples:
GET /characters/bob/items
This returns an array of items that Bob is carrying.
I need to perform a variety of 'operations' against these items, and im having a very difficult time modeling this as 'resources'.
Here are some potential operations, depending on the nature of the item:
throw, eat, drop, hold
This is complicated because these 'operations' are only suitable for certain items. For example, you can't eat a sword. Moreover, 'eat' essentially has a side-effect of 'deleting' the resource. Using 'throw' may also 'delete' the resource. Using 'drop' may 'transform' the resource into another resource type. 'Throw' requires that I provide a 'location'. 'Hold' requires that I supply which hand to hold the item in. So how do you model these operations as resources? None of them are 'alike' because they each require different parameters and result in completely different behaviors.
Currently, I have an 'actions' resource that I POST these arbitrary actions to. But this feels way too RPC and non-standardized/discoverable:
POST /actions/throw
{
characterId: 5,
itemId: 10,
x: 100,
y: 150
}
I try to stick to resources and GET/POST/PUT/PATCH/DELETE where possible, but the base verbs tend to map directly to CRUD calls. Other, more complex operations generally can't be mapped without additional information.
Focusing on the resources, I'd probably do something like this (posting messages to the resources):
POST /characters/bob/items/{bombId}?action=throw
POST /characters/bob/items/{foodId}?action=eat
POST /characters/bob/items/{potionId}?action=add&addedItem={ingredientId}
Return an error when the action is not appropriate for the item.
Where I want a resource to “do a complex action” while remaining RESTful, I'd POST a complex document to the resource that describes what I want to happen. (The complex document could be in XML, JSON, or any number of other formats.) This is somewhat distinct from the more common pattern of mapping POST to “create a child resource”, but the meaning of POST is “do non-idempotent action defined by body content”. That's a reasonable fit for what you're after.
As part of the HATEOAS principle of discovery, when you GET the resource which you will later POST to, part of the document returned should say what these complex action documents are and where they should be sent to. Logically, think of filling in a form and submitting it (even if the “form” is actually slots in a JSON document or something like that).
Related
I have come across numerous JSON over HTTP APIs that were suppose to be RESTful but I'm not sure if the following design is adhering to REST principals -
Request model is used as a subset of Response model.
For example, POST /flight/inquiry
Request :
{
"flight_no":"2384B",
"departure_time":78163918839123,
"arrival_time":78163918889382,
...
}
Response :
{
"request" :
{
"flight_no":"2384B",
"departure_time": 78163918839123,
"arrival_time": 78163918889382,
...
}
"status" : "ON TIME"
"last_updated" : 7816391884313,
...
}
If we analyze this in terms of Richardson Maturity Model, I think it will not qualify for level 1 because there is no clear definition of a Resource. If we call 'Inquiry' as the resource here, the response should not have result on the inquiry such as status, last_updated, etc. Typically, it should respond with an inquiry_id (like 123) that can be passed to a second end point GET /flight/123/status.
This approach, though is more aligned with REST principles (correct me if I'm wrong), developers tend to avoid it simply because it's easy to squeeze the same behavior in a single end point rather than two. My question is, are there consequences to ignoring REST principles in situations like these where it is simpler to take shortcuts?
I think your current understanding is suspect.
Using POST to request a representation of a resource isn't RESTful, because we have GET, which is more suitable.
It isn’t RESTful to use POST for information retrieval when that information corresponds to a potential resource, because that usage prevents safe reusability and the network-effect of having a URI. -- Fielding, 2008
More idiomatic queries would look more like
GET /flights?flight_no=2384B
or even
GET /flights?flight_no=2384B&departure_time=78163918839123&arrival_time=78163918889382
In these cases, nobody would be at all startled that the same "parameters" used in the identifier are also repeated in the representation of the resource.
Given that the query is assigned POST semantics by the client, there's absolutely nothing wrong with a response that looks like:
200 OK
Content-Location: https://example.org/flights?flight_no=2384B&departure_time=78163918839123&arrival_time=78163918889382
In which case having the parameters appear in the response body would also be perfectly normal (just as they would be if the client had used a GET on that resource directly).
are there consequences to ignoring REST principles in situations like these where it is simpler to take shortcuts?
If you relax the REST architectural constraints, then you can no longer expect the corresponding properties to hold.
In particular, when you start replacing the uniform interface with your own bespoke semantics, you sacrifice the capabilities of general-purpose components that could otherwise assist.
I have a question to RESTful services. In REST the POST method is used to create an entity.
And GET is used to query entities. Right?
As I read in another posts it is not allowed in HTTP to send a GET request with a body.
But when I want to send Json to make a query, what is the best way? Are there any best practices or how do you solve such json queries?
Thanks for your answers
In REST the POST method is used to create an entity. And GET is used to query entities. Right?
Not really. GET is used to fetch representations of resources. POST is deliberately vague -- anything not worth standardizing can use POST.
when I want to send Json to make a query, what is the best way?
There is no best way to do it, just trade offs.
The basic plot of HTTP is that you GET representations of resources. If the resource you want doesn't exist, you create a new one. So the "REST" flow would look something like sending a request to the server to create a "the answer to my query" resource, and then using GET to obtain the current representation of that resource. Which is great, because we can fetch the latest representation of that resource any time we're worried that our copy is out of date. Other people with the same query can use the same resource, so we can use a general-purpose cache to take a lot of the work. The end result is "web scale".
OK, not that great, because we learned that sending information over insecure channels is a bad idea; but we can put a general-purpose caching proxy in front of our server, and get some scale that way.
But "create a new resource" is a lot of ceremony when you only expect to need the query once.
Creating a new resource was using POST in this situation anyway, so why not return a representation of the solution right away? And the answer is, go right ahead! that works great... but doesn't give you any cache support at all. You are effectively performing a remote call under the guise of modifying a resource.
Also, POST doesn't promise idempotent semantics -- on an unreliable network, requests can get lost, and general purpose components won't know that in this particular case it is harmless to just repeat the same request.
PUT has idempotent semantics... but it also has very specific opinions about the contents of the payload that don't match "query" at all.
You can dig through other standardized methods, but there aren't really any good fits. The only methods that are close are SEARCH and REPORT, which are coupled to WebDAV semantics.
You can invent your own non standard method; but general purpose components won't understand it.
You can standardize a new method with the semantics you need, but that's a lot of work.
Or you can just use POST.
Remember, the web took over the world using nothing more than GET and POST. So it's probably fine.
This page says that for a JSONAPI recommended type for "id" is string.
identification
Every resource object MUST contain an id member and a type member. The values of the id and type members MUST be strings.
What do we get from keeping it as string. In other words if for some case ids are only number, is it still recommended to keep them as string?
They're not trying to cater for the 'some' cases. They're trying to put forward an API framework which allows clients to flexibly consume APIs. In particular, the idea is that if a client conforms to this specification, then it can start dealing with any API implementation which is also conformant. The key idea is portability. They're all about describing common characteristics between APIs.
Now, there aren't too many things you can definitely say about a resource, given that a resource is the thing which gives an API a purpose, but it's (perhaps) important to know which resource you've seen and what its type is. They're both pieces of information which any API consumer is going to need (the argument goes). Once you decide that they need to be common you need a common JSON datatype for them (because integers look different from strings in JSON).
The JSONAPI format is designed so that a single well-written client could actually process multiple different APIs with little or no alteration. It can only do that if it's able to distinguish between what's important for the client itself to process versus what's important for the client to pass on to a third party (e.g. stuff to display or manipulate in a UI).
Further on in the specification it talks about how type and id, taken together, form a namespace for any fields within the resource itself. So, for a client to use that information in any meaningful way it needs to know what the type of the id and type fields are.
Ultimately, the writers of the JSONAPI spec have said that the id and type fields are universally useful regardless of their value in the resource / API itself. As such, they both need to have reasonably well-defined semantics regardless of what makes sense for any individual API. Strings and integers behave differently so you need to make a choice. And Strings are a bit more flexible across APIs so they've made that call. They could probably have gotten away with choosing integers but they probably decided that it would be a bit restrictive in the cases where people really did want a String as an id.
Lets say you have a set of resources which look like:
/v1/API/Events
/v1/API/Transactions
where Events look like:
{
"id":1,
"name":"blah",
"date":"2010-01-11",
"duration":1231231,
"transaction_id":3
}
except that the transaction_id can be left out or set to null
Transactions look like:
{
"id":1,
"name":"transaction_name",
"date":"2015-01-01"
}
Now here is the issue, there are some times where it would be beneficial to be able to get a Tranaction and it's events at the same time. It would definitely be beneficial to be able to POST a Transaction with it's events. i.e.
{
"name":"new_transaction_one",
"events": [
{
"name":"blob",
"date":"2010-01-01,
"duration":10
},
{
"name":"blob_2",
"date":"2010-01-010,
"duration":15
},
]
and it would also be useful to be able to make a GET request like:
/v1/API/Transactions/1?withEvents=Y
Other options would be to have another resource:
/v1/API/TransactionsWithEvents
But if you have objects with several different sets of child records, you would have to have a lot of different combinations. I also don't like that they have different paths event though we are talking about the same resource.
I'm leaning towards using query parameters in the GET request but I'm wondering if there are any gotchas.
Here is a case where typical RESTful API scaffolding reaches it's limits. It would probably be preferable to create some convenience methods (especially for POST) that can work across the different resources, for example, creating a transaction and associated events in a single POST. You will find that most any service with a relative level of complexity needs to have the convenience methods to prevent the user from having to (using same example) create the transaction, read the transaction id from response, then create events, then create transaction to event relations.
Tying it back to your example, that may mean you have a method like
POST /v1/API/CreateTransactionWithEvents
You may not need similar convenience endpoint for the case of returning events with transactions, as I think your parameter string approach may make sense here since you are just enriching the data returned from the record with related events.
GET /v1/API/Transactions/{ID}?withEvents=1
This is a bit more of a gray area and really subject to what works best within your other API's (so you are not doing something totally different), with providing clear API to clients, etc.
Just think of typical resource-related endpoints (i.e for Transactions and Events) as the main backbone for your RESTful service, with adding convenience methods as appropriate to address specific resource CRUD use cases that are not easily handled by backbone endpoints. This could be cases where you want to prevent client from making a series of API calls to get to something you might provide in a single call, or when you need to do something like atomically added records across resources with a single API call.
Let's say we have a site where we have a list of items. On each of these items you can start a couple of different process that will result in somekind of output related to the item in question. How should you design for the most appropriate use of the http verbs? What I would like to have is multiple links per item and each link trigger one of the actions, but in my scenario that doesn't match the HTTP-VERB get, which will be used if I am using links. On the other hand, I don't want to have buttons which all are in a separate form with different actions.
It's somewhat hard to explain but hopefully you understand, it should be some best practices to apply here.
You should NOT use GET. GET requests should be safe which means they are intended only for information retrieval and should not change the state of the server. (i.e. things like logging are OK, but things that actually update the state of the application are a no-no.) Think of a crawler going over your application. Anything you wouldn't mind a crawler going through is fine for GET, but that doesn't sound like your situation (because you said, "start a couple of different processes", but I could be misinterpreting your use case).
That leaves PUT, DELETE and POST. PUT and DELETE must be idempotent, meaning that multiple identical requests should have the same effect as a single request. So if you had a request that updated a person's name, for example, if you called it once or 100 times, the person's name would still be the same, so it is idempotent.
POST is the most flexible verb. If the processes you are kicking off are not safe or idempotent (or even if they are) you can use POST, which simply doesn't guarantee anything about safety or idempotency. The disadvantages there are:
If you use POST when GET is more semantically correct, it is less communicative of the intent of your request, since POST usually means you are sending a payload.
You just couldn't take advantage of the web's caching infrastructure that makes it so scalable.
In the past, I have used POST with query args to specify custom actions. It made sense in my use case because I had a majority of custom actions needing to pass a payload. Since you do not want to use buttons, you can use GET with query args to specify the different actions, but you have to be very careful that the action you are taking does not have any side effects and is idempotent. As noted in the comment by #jhericks below, there are many things in the network that assume that GET's are safe and may repeat GET's.
From a pure RESTful perspective though, this is not ideal. Your items will have a specific URI and GET on the URI will return the items representation. Running actions on the item is effectively a change in the state of the item representation and that should be done with a POST(or a PUT depending on who you ask and if your web server supports PUT). In real life though, using query args is an easy work around and it may make sense to your use case.
Im not sure i fully understand your question.
But here's a quick paragraph which might help you.
REST is about making smart clients and simple servers. GET, PUT, DELETE represent the basic operations of file access at the lowest level. What you should be doing is completely ignoring anything the server can offer and be offloading that work onto clients.
So, the question is, why is the server being triggered to do many things. why can't the client do all of these things itself.
Mike Brown