2

I´m currently creating my first restful API for a WebApp and I´m at a point where I´m not sure, how to best structuring it.

Background Information

I´m developing a Holiday planer for a Company. The company is separated in a few departments. So some users are in Department A and some in B for example. The department information would be accessible /department/ and the users in /users/.

All Users can make a holiday request. Let´s stay this would be accessible /holiday/. For each department there is a minStock of Users that must stay in the Company, so that our product support is still alive ;) As you can see there is a close relation between the departments, users and request/ confirmed holiday bookings.

Possible way of doing it, or not?

At one point I have to display in my UI which Users of a specific department already have requested a holiday, so this could be either /holiday/department/:departmentID or /department/holiday/:departmentID. Probably I´m totally wrong here and you would design it completely different.

I don´t want to put too much information here and just stay at this simple example. I´m excited to know how you would do it when just looking at this information.

If I missed something, just ask me in the Comments and I will do my best to provide you everything you need to help me!

Of course at some point in my APP I will need all holiday request´s without filtering for a department. I´m not sure if this will influence the overall design, just want to add this here.

  • Be careful when designing your /holiday resource : your purpose is to model "what is holidays" when doing it. You should not consider business logic such as your minStock of User when you are designing something (here a REST resource) – AilurusFulgens Nov 2 '16 at 15:19
  • 1
    Let me know if I'm wrong. Holiday is linked to User and holiday can not exist without the user. Holiday is only related to a departament through a user. So there's no direct relationship. Am I right? – Laiv Nov 2 '16 at 18:57
  • yes this is totally true! – el solo lobo Nov 3 '16 at 8:16
2

If you are trying to be as REST-compliant as possible, I suggest taking account these 2 hints. (So far, they worked fine to me):

  1. The URI is a representation of a resource within a hierarchy (this is a personal preference)

    As @AilurusFulgens commented in my answer:

    URI should stay an identifier, not provide any meaning

    True. However, we still want it to be readable and some readings might look unnatural from our point of view. IMO, hierarchies brings a little bit of readability. But I agreed that they can lead to wrong assumptions and cause coupling between client-server.

    I recomend to read the comments bellow and visit the page that has been shared. It clarify things around this subject.

  2. URI parameters (query strings) are intended to be used to modify the output. For instance, filtering, pagination, etc.

I would avoid:

GET /holidays/department/:departmentID
GET /department/holidays/:departmentID

Why would I? Because these two seems to me the less readable.

My proposal

I. From the holidays prespective:

GET /holidays #all the holidays
GET /holidays?from=timestamp&to=timestamp&user.department=x
GET /holidays?from=timestamp&to=timestamp&status=confirmed&user.department=x

Due to what we are doing is to retrieve holidays based on certain conditions, we put these conditions within the query instead of within the path (It might interest URI Schema)

II. From the department prespective:

GET /department/:departmentID/holidays?from=timestmap&to=timestmap
GET /department/:departmentID/holidays?from=timestmap&to=timestmap&status=confirmed

Here /department/:departmentID is already conditioning holidays

Finally, there's a 3rd way.

Json-RPC - like service.

GET /ws/holidays/getPlan

The getPlan is supposed to be the remote procedure (function) and the arguments go into the request body. Sending a request body within a GET request might seems somewhat inappropriate (I would not care). But it's possible.

Solutions to the previous "drawback":

I. Use POST

II. Use request parameters

GET /ws/holidays/getPlan?from=timestamp&to=timestamp&department=x

GET /ws/holidays/getPlan?from=timestamp&to=timestamp& status=confirmed&department=x

This approach comes with an interesting advantage. The response. It's not bound to any resource. It's neither user , holidays nor department. It can be a mixup or something more complex, with many more details (like the "minStock" of the department, empty spots, etc).

  • That's clearly not "REST compliant" to say your point n°1. URI should stay an identifier, and not provide any meaning (hierarchy or whatever) because that's the purpose of resources representations. Of course, you could not strictly apply to REST... but so far, you will not be "REST compliant". – AilurusFulgens Nov 3 '16 at 12:15
  • Yes, you are right. That's why I said 'as possible'. The OP's confussion is caused by the perception of the URI readability over his data hierarchy. I suggested to stick to his real hierarchy (what I use to do to keep my APIs as simple and alike each other as I can) and move modifiers to the request parameters. – Laiv Nov 3 '16 at 12:30
  • @AilurusFulgens I have edited the answer to clarify those points that are personal preferences. Also removed statements that could lead to confusion. Just for the record: From my point of view, hierarchy brings readability. Maybe because I tend to think in such data structures (has, belongs, depends on, etc..). But as I said, this is my point of view – Laiv Nov 3 '16 at 13:24
  • 1
    True, URI name and hierarchy are for readability purposes. Of course, it's easier to think that hierarchies imply a relation between resources. But a client that use your API is not supposed to think like that and to infer which type of relation a hierarchy means (i.e it's "out of band" knowledge). Thus you will convey the semantic of the relations between resources in the resources representations. See also item 4 of a blog post from Roy Fielding: roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven – AilurusFulgens Nov 3 '16 at 15:52
  • Failure here implies that clients are assuming a resource structure due to out-of band information. I found this to be interesting. The URI should not lead to the client to such assumption, what also means that, if you want to ensure a sort of coupling, this is the way to go. In my latests projects I have been asked to implement some of these hierarchies (forcing the client to go through process till reach the final URI. Like a workflow).I have assumed that it was the natural way to go. I see know I have been coupling server and client apps. – Laiv Nov 3 '16 at 17:43
4

Welcome to the weird world of middleware!

A RESTful api wants to make things pleasant, especially if other developers are using it. In general, each resource should point at a particular, discreet object, almost 100% of the time a noun.

So, you're actually on the right track. GET /v1/holiday/ should get a list of holidays from the entire system while GET /v1/holiday/134 should get the holiday with id 134. What i've found helpful in the past is creating a table with each resource on the left, and the different verbs you want to use across the top. Then, fill in the cells with what that combination of Verb and Noun should do. Its ok to cross out things at this stage too, for example if you want to avoid a user deleting things like user resources.

Another note that if you have a HAS-A relationship, you should nest them. In your example, a particular holiday request is owned by a user, so that request should probably live at GET /v1/user/901/request/123.

This is a great blog post about pragmatic APIs, and generally the pattern I follow when implementing a new one.

The only other thing i'll note is i'm a huge fan of versioning API. This way, i can put together the v1 version at https://yourdomain.com/v1/resources and then if there's problems or we wnat to make a ton of changes, we can later push out https://yourdomain.com/v2/resources and not affect existing implementations.

Best of luck!

  • Your example: /v1/user/request/123 should more likely be: /v1/user/456/request/123 to indicate to which user the holiday belongs in a HAS-A relation. – Luc Franken Nov 3 '16 at 8:34
  • ooh, good catch! I'll update the answer to reflect that! – Adam Wells Nov 3 '16 at 12:46
  • I have a feeling that you intended to write v2 instead of v1 in your last URL. – Vincent Savard Nov 3 '16 at 12:53
  • I fixed that. Shh. the answer was perfect. >_> – Adam Wells Nov 3 '16 at 17:07
1

A bit or naming might help. You may have one table for holidays but there are some different REST resources:

user
department
holiday-request
holiday (not a real table in first example, there is just on holidays table)

For example you could then go for:

GET /v1/holiday-requests -> List of all requests
GET /v1/holiday-requests/{requestId} -> {id: 123, status: pending}

GET /v1/users/{userId}/holiday-requests -> List of all requests for this user
GET /v1/users/{userId}/holidays -> List of all booked holidays

GET /v1/departments/{depId}/holiday-requests -> All requests for this holiday
GET /v1/departments/{depId}/holidays -> All booked holidays for department

POST /v1/holiday-requests {userId: 123, date: 2016-11-02}
  Possible results:
    201 created -> Request ok
    401 conflict -> Trying to request holiday which is not possible anymore because limit is reached.

PATCH /v1/holiday-requests/{requestId} {status: accepted} -> Make it a real holiday, and so count agains minStock. 
So now a new resource becomes available: /v1/holidays/{requestId}

GET /v1/holiday-requests/{requestId} -> {id: 123, status: accepted}

You can also give holidays and holiday-requests separate id's. That gives more flexibility, then you can do:

PATCH /v1/holiday-requests/{requestId} {accepted: true} 
  -> Make it a real holiday, and so count agains minStock. 
Internally you could do a post agains: /v1/holidays {requestId: {requestId}, date: .... }
So now a new resource becomes available: /v1/holidays/{holidayId}

GET /v1/holiday-requests/{requestId} -> {id: 123, status: accepted, holiday: /v1/holidays/{holidayId}

Easiest thing is to read more about what a resource is in REST. That gives lots of ideas of where you can create more smaller resources.

Edit: As you see I add a url to the related resource. That's called HAETOAS, example here: https://www.infoq.com/articles/webber-rest-workflow on how that thinking process goes.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.