Let's say I have a REST api which allows to list the customer resources paginated:
/customers?page=0&pageSize=10
At the same time, I'd like to allow the consumer of the API to retrieve the full list of customers, what's the best practice here?
I guess that the page parameter might be optional, if present, the client want pagination enabled, if the page is not present, pagination should not be enabled.
The issue I see with this approach is that the response most probably should be different, I mean, in case they want to retrieve all customers, the pagination information should not be present in the response, I think.
One more approach could be to just have different API's:
/customers?page=0&pageSize=10
/customers/all
A bit weired I know.
Related
My HTTP API requires the frontend to pass an accountId on API calls due to the fact we support admin users who are not tied to a single account and could be querying any account.
Initially I implemented this as a header the issue here is that caching would not work.
The current implementation looks like api.com/endpoint?accountId=123 whilst this works well - I would like to understand if this is the correct approach when implementing a RESTful HTTP API.
UPDATE:
Based on a comment - this is for GET
IMO, you did things "the right way" (TM) by putting this information in a HTTP header -- that seems to be the correct place for this to live. However your caching system currently doesn't care about HTTP headers, so that leaves you with a practical problem (which doesn't really care about "the right way"). So...
Based on that, it sounds like you have two options:
Fix your caching to include specific headers as part of the cache key, or
Add relevant information to the URL for caching purposes
I'd suggest (1) where you update the cache key to be a hash of the URL + other relevant information that would result in a different result (in this case, the HTTP header including the account ID or session information). This allows you to keep putting information in the right place while also not causing issues for cached pages.
Of course this might not be possible, so the only practical solution I can offer you is (2) where you pull stuff into the URL in order to support caching. I think this is an anti-pattern, but it will get the job done.
For exmaple: We have a REST API which provides data for multiple consumers. The most case is like: Frontend (accessable for everyone), admin/management (for admins only), mobile app.
These ones differs in the response data which the REST API delivers. For example, in the admin request, the API should respond the email of the user. In the frontend not. Or the mobile app, shouldnt receive unnecessary data, which are not displayed in the views.
My idea was to set adapters in front of the REST API, but this won't work, if you don't have multiple domains available (e.g. api.xyz.com, api-manage.xyz.com).
I think this is a common way, which most of the app needs. I don't want to build multiple APIs to cover this case.
Is there any way on the application side, for example with middleware? Or I know there is a role based approach, but I dont think, this is enought abstract, because the role doesnt decide which device it is.
There is a little solution:
Role based decision which fields the user can be retrieve. Additionally the consumer has to put a header, or a queryparam like "frontend", "admin", "mobile", to identify which data will be returned. This is independent of the "which the user is able". It's just for optimization.
Are there any other solutions?
Your API should not know about your clients but it should offer the possibility for the clients to do what they want/need. Let's say you need to display a list of product with details. On the desktop you might bring 100 products but in mobile only 10. So the api need to provide a configurable paging. Also in the desktop you might get more information than on mobile. So here if you have an entity product, in the desktop you will get all the fields( multiple attributes for example) but for the mobile you get only the name and one attribute (for example the price) to minimize the payload size.
So the API need to be generic but give clients possibility to use it based on their needs.
Background:
I have a single page application that pulls data from a REST API. The API is designed such that the only URL necessary is the API root, ie https://example.com/api which provides URLs for other resources so that the client doesn't need to have any knowledge of how they are constructed.
API Design
The API has three main classes of data:
Module: Top level container
Category: A sub-container in a specific module
Resource: An item in a category
SPA Design
The app consuming the API has views for listing modules, viewing a particular module's details, and viewing a particular resource. The way the app works is it keeps all loaded data in a store. This store is persistent until the page is closed/refreshed.
The Problem:
My question is, if the user has navigated to a resource's detail view (example.com/resources/1/) and then they refresh the page, how do I load that particular resource without knowing its URL for the API?
Potential Solutions:
Hardcode URLs
Hardcoding the URLs would be fairly straightforward since I control both the API and the client, but I would really prefer to stick to a self describing API where the client doesn't need to know about the URLs.
Recursive Fetch
I could fetch the data recursively. For example, if the user requests a Resource with a particular ID, I could perform the following steps.
Fetch all the modules.
For each module, fetch its categories
Find the category that contains the requested resource and fetch the requested resource's details.
My concern with this is that I would be making a lot of unnecessary requests. If we have 100 modules but the user is only ever going to view 1 of them, we still make 100 requests to get the categories in each module.
Descriptive URLs
If I nested URLs like example.com/modules/123/categories/456/resources/789/, then I could do 3 simple lookups since I could avoid searching through the received data. The issue with this approach is that the URLs quickly become unwieldy, especially if I also wanted to include a slug for each resource. However, since this approach allows me to avoid hardcoding URLs and avoid making unnecessary network requests, it is currently my preferred option.
Notes:
I control both the client application and the API, so I can make changes in either place.
I am open to redesigning the API if necessary
Any ideas for how to address this issue would by greatly appreciated.
Expanding on my comment in an answer.
I think this is a very common problem and one I've struggled with myself. I don't think Nicholas Shanks's answer truly solves this.
This section in particular I take some issues with:
The user reloading example.com/resources/1/ is simply re-affirming the current application state, and the client does not need to do any API traversal to get back here.
Your client application should know the current URL, but that URL is saved on the client machine (in RAM, or disk cache, or a history file, etc.)
The implication I take from this, is that urls on your application are only valid for the life-time of the history file or disk cache, and cannot be shared with other users.
If that is good enough for your use-case, then this is probably the simplest, but I feel that there's a lot of cases where this is not true. The most obvious one indeed being the ability to share urls from the frontend-application.
To solve this, I would sum the issue up as:
You need to be able to statelessly map a url from a frontend to an API
The simplest, but incorrect way might simply be to map a API url such as:
http://api.example.org/resources/1
Directly to url such as:
http://frontend.example.org/resources/1
The issue I have with this, is that there's an implication that /resource/1 is taken from the frontend url and just added on to the api url. This is not something we're supposed to do, because it means we can't really evolve this api. If the server decides to link to a different server for example, the urls break.
Another option is that you generate uris such as:
http://frontend.example.org/http://api.example.org/resources/1
http://frontend.example.org/?uri=http://api.example.org/resources/1
I personally don't think this is too crazy. It does mean that the frontend needs to be able to load that uri and figure out what 'view' to load for the backend uri.
A third possibility is that you add another api that can:
Generate short strings that the frontend can use as unique ids (http://frontend.example.org/[short-string])
This api would return some document to the frontend that informs what view to load and what the (last known) API uri was.
None of these ideas sound super great to me. I want a better solution to this problem, but these are things I came up with as I was contemplating this.
Super curious if there's better ideas out there!
The current URL that the user is viewing, and the steps it took to get to the current place, are both application state (in the HATEOAS sense).
The user reloading example.com/resources/1/ is simply re-affirming the current application state, and the client does not need to do any API traversal to get back here.
Your client application should know the current URL, but that URL is saved on the client machine (in RAM, or disk cache, or a history file, etc.)
The starting point of the API is (well, can be) compiled-in to your client. Commpiled-in URLs are what couple the client to the server, not URLs that the user has visited during use of the client, including the current URL.
Your question, "For example, if the user requests a Resource with a particular ID", indicates that you have not grasped the decoupling that HATEOAS provides.
The user NEVER asks for a resource with such-and-such an ID. The user can click a link to get a query form, and then the server provides a form that generates requests to /collection/{id}. (In HTML, this is only possible for query strings, not path components, but other hypermedia formats don't have this limitation).
When the user submits the form with the ID number in the field, the client can build the request URL from the data supplied by the server+user.
I have recently started testing rest based web services. I am wondering what are common issues faced while testing them.
I generally look for
response status (http code is among 200/400/500)
response headers (cache control, response type, content length )
if expected fields / values are present in json response.
I want to know what else to look for and issues in general while testing rest based web services
This is actually a very broad question but, here are the things that I look for while testing rest API's:
Response codes, Response headers
Check if Authentication/Authorization is correct based on user privileges
Check to see if the supplied value can break the output
Intentionally pass different Data-Type values to see if proper handling is done or not
Use load testing tools to see how many concurrent requests an API can handle
Again these are just a few things that will depend on the type of API you are building. Feel free to add more as you progress.
There are many, but let me share a few that aren't even specifically API issues but a full API integration test will help find it.
We did an integration API test where a publisher's product listing endpoint was hit, and then we randomly picked 100 of those products. Every few weeks we would find a bunch of bad product ID's. How was it possible? They would update the database but the product listing endpoint was cached and had bad information for hours until it was refreshed automatically.
Another time we found an e-commerce company in Brooklyn that, on average, had 3,500 products out of their 50k+ were missing a category. This meant 3,500 products not showing up if you browsed by categories, and potential lost sales.
We have a whole case study of this stuff on our website. Weird and unexpected stuff happens all the time. Test for everything and catch as much as you can. Every object in the payload should be verified, it may seem like overkill but there are platforms that can do all that work for you.
Here are suggestions based on my experience while testing Restful APIs.
Make sure that you are validating proprietary headers if you are using with your APIs.
Confirm that you are including correct location header in the response so that Rest API caller can use that for verification or for subsequent calls.
Check that location header protocol is correct i.e as per your design. (http/https). It's very important for subsequent calls.
I'm having some trouble with RPC style verbs starting to appear in my API.
My question is: Is it okay to have these verbs, "Submit", "SendForApproval", "Accept" and "Reject" in a Restful API? Is that going against the Restful principles? What's best practice for naming these Actions?
Our business case:
I have an order. It starts out at status "pre-ordered". I want to then make a call to validate the order against our business logic. Depending on the validation results, I then either submit it or send it for approval. The approval process happens externally to our solution and then when it has been through the approval process the Api is called to mark the order as either Accepted or Rejected.
There is a bit more going on than simply updating statuses - we send things to print queues, spin off tracking documents etc.
So the URI's that we have defined are
https://api.site.com/orders // A list of orders
https://api.site.com/orders/{orderId} // An order
https://api.site.com/orders/{orderId}/ValidationErrors // Get a list of validation results
https://api.site.com/orders/{orderId}/Submit // Submit the order for processing
https://api.site.com/orders/{orderId}/SendForApproval // Send to supervisor
https://api.site.com/orders/{orderId}/Accept // Supervisor Accepts the order for processing
https://api.site.com/orders/{orderId}/Reject // Supervisor can Reject the order
You must avoid inserting verbs in your URIs as much as you possibly can. Furthermore, I disagree with user1438038, because the QueryString filters should be used for search purposes instead of specifying actions.
I believe that there're better approaches to solve this problem. First off, take into account that you shouldn't model your RESTful API in a way that it's highly tied to your business objects (supposing that you're using a OO approach). Moreover, remember that in a REST perspective, everything you expose is a resource rather than objects with its state and behavior.
I'm going to suggest you a different approach but I'm not sure if it's going to fulfill your system requirements but maybe it can be useful to give you some ideas. In your case, you can have multiple resources like PreOrders, Orders, Supervisors...
https://api.site.com/preorders
GET - Display all pre-orders.
POST - Create a new pre-order.
Creating a new order:
https://api.site.com/preorders/1
POST - approves the pre-order. This action's gonna create a new Order.
Displaying all orders that are waiting for approval (by the supervisor with ID=1).
https://api.site.com/supervisors/1/orders
Searching for approved and canceled orders:
https://api.site.com/supervisors/1/orders?status=canceled
https://api.site.com/supervisors/1/orders?status=approved
To cancel or approve an order:
https://api.site.com/supervisors/1/orders/1 (DELETE cancels the order)
https://api.site.com/supervisors/1/orders/1 (POST approves the order)
I'm unsure if it's the best approach to solve your problem but I'm pretty sure it's better than using verbs in your URI.
I'd be fine with that, we are using a very similar setup for order management.
However, another possibility would be passing an action parameter, such as:
https://api.site.com/orders/{orderId}/?action=accept
https://api.site.com/orders/{orderId}/?action=reject
...
The general idea of REST is to have resources (such as order or customer). What you want is more of a RPC semantics, but there is no right or wrong. Your approach is reasonable.
Not is a good practice have verbs in REST uris.
So, if you want change a status in any order, you may send a PUT/POST to https://api.site.com/orders/{orderId} updating only the values you want for your bussines logic.