2

I'm writing an API with spring boot, trying to keep it restful but the structure is quite nested. So say I have:

/api/examboard/{ebid}/qualification/{qid}/subject/{sid}/module/{mid}/

I have a controller for every noun that will take in all Id's, the problem with this is that I don't really need an ebid or a qid for modules, they only really need to be concerned with subjects most of the time. The mapping between them all is quite simple. An examboard will have many qualifications, a qualification will have many subjects etc....

Now the problem is say I go for a simpler API design where I only need the parent Id so the Subject controller will also have:

api/subject/{sid}/module 

then I need to include multiple services in my controller based on the way JPA works. As I need to include SubjectEntity based calls and ModuleEntity based calls. However I want to maintain a one to one relationship between my controllers/services and services/repositories. This is why I opted for the longer url as I've mentioned above, but it does seem like overkill. Does anyone have any advice on how I should structure an API like this, most examples are quite small and don't really fit.

2 Answers 2

1

Without knowing more about your models and the relations between them, this answer will have to stay a bit diffuse.

First of all - "it depends". I know, but it really does. The way you should design an API depends heavily on your use cases that will define required access patterns. Do you often need all modules for a subject? Then introduce /subjects/{sid}/modules, if you need the details for a module of a subject in a qualification in an examboard - by all means have a /examboards/{ebid}/qualifications/{qid}/subjects/{sid}/modules/{mid}

As you say there are many relations between your entities. That is fine, but it does not mean that you need your API to capture each of these relations in a dedicated endpoint. You should distiguish between retrieving and modifying entities here. Find below examples for certain operations you might want to have (not knowing your models, this may not apply - let's consider this an illustration)

Retrieve qualifications for an examboard

  1. GET /examboards/{ebid}/qualifications plain and simple
  2. GET /qualifications?ebid={ebid} if you feel you might need sophisticated filtering later on

or create a new qualitication for an examboard

  1. POST /examboards/{ebid}/qualifications with the details submitted in the body
  2. POST /qualifications with the details submitted in the body and making the associated examboard ebid part of the submitted data

or update an existing qualification

  1. PUT /qualifications/{qid} (if this operation is idempotent)
  2. POST /qualifications/{qid} (if it should not be considered idempotent)

or delete qualifications

  1. DELETE /qualifications/{qid} deletes entities, cascade-deletes associations
  2. DELETE /examboards/{ebid}/qualifications clears all qualifications from an examboard, without actually deleting the qualification entities

There are certainly more ways to let an API do all these things, but this should demonstrate that you need to think of your use cases first and design your API around them.

Please note the pluralisation of collection resources in the previous examples. This comes down to personal preference, but I tends to follow the argumentation of Sam Ruby in RESTful Web Services (available as PDF) that collections should be first-class citizens in an API

Usually, there should not be a reason to have 1:1:1 relationships between controllers, services and repositories. Usually, this is not even possible. Now, I don't know the reason why you might want to do this, but following through with this will force you to put a lot of logic into your database queries and models. While this (depending on your setup and skills) may or may not be easily testable, it certainly shifts the required test types from unit (simpler, usually faster, more fine-grained) to integration tests (require more setup, more complex, usually slower), when instead of having the bulk of your business logic in your services you put them into many joins and subselects in your repositories.

Sign up to request clarification or add additional context in comments.

1 Comment

Ya I already found myself relying on OneToMany mappings in the entity, I think I'd rather not have to lazily fetch other entities in almost every case.
1

I will only address your REST API structure question.

As you already pointed out

The problem with this is that I don't really need an ebid or a qid for modules, they only really need to be concerned with subjects most of the time

You need to think of your entities as resources if your entity can stand for itself give it its own top level resource. If instead your entity exists only as a part of another entity build a subresource below its parent. This should correspond with the association type aggregation and composition in your object model design.

Otherwise every entity that is part of a many relationship should also be accessible via a subresource on the other side of the relationship. As I understood you you have a OneToMany relationship between examboard and qualification so we get:

api/examboards/{eid}/qualifications
api/qualifications/{qid}/examboard

Yo could also remove the examboard subresource and include it in the qualification response.

For ManyToMany realtionships you need two subresources:

api/foos/{fid}/bars
api/bars/{bid}/foos

And another resource to manipulate the relationship itself.

api/foosToBars/{fid}+{bid}

Or likewise.

1 Comment

ya I'm going to rever to : api/foos/{fid}/bars api/bars/{bid}/foos. I think its more human readable and simple to work with.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.