comprex
comprex

Reputation: 763

Spring Boot REST path mapping

I'm just thinking, what is the best practice to create PATH mapping for rest service. Let's say we have following paths:

/users POST
/users/1 PATCH, GET
/users/1/contacts GET, POST
/users/1/contacts/1 GET, PATCH

The question is - what is the best practice to create controllers. For example we have UserController where we technically could put all these mappings. Or - we should create seperate controllers (UserController, ContactsController). f.e UserController below, if we put everything under.

@RequestMapping("users")
@RestController
public class UserController {

    @RequestMapping(method = RequestMethod.POST)
    public ResponseEntity<Void> createUser() {}

    @RequestMapping(method = RequestMethod.GET)
    public User getUser() {}

    @RequestMapping(value = "{id}/contacts", method = RequestMethod.GET)
    public List<Contact> getContacts() {}

    @RequestMapping(value = "{id}/contacts", method = RequestMethod.POST)
    public ResponseEntity<Void> createContact() {}

    .....
}

And if we create separate controllers, how paths should be organized then? Probably it's a silly question, but i will be glad, if someone could share experience.

Upvotes: 12

Views: 26847

Answers (2)

blurfus
blurfus

Reputation: 14031

I am a fan for separate controllers as they remove coupling between Users and Contacts (for example) - what if later you want to use Contacts in a separate context? (i.e. independent of the User they belong to)

If they are separated, the paths would look very similar to what you have:

Users

/users GET, POST
/users/{user-id} PATCH, GET

Contacts

/contacts GET, POST
/contacts/{contact-id} GET, PATCH

If there is a dependency between Contacts and Users (and in this case, it looks like there is one), you can have it like this:

/contacts/for-user/{user-id} GET, POST
/contacts/for-user/{user-id}/{contact-id} GET, PATCH

This allows you to add contacts to other things (not just users) and users to other contexts (regardless of their contacts)

For example, Users of a coffee maker

/coffee-maker/users GET

Or if the coffee maker fails, who do we contact for repair?

/coffee-maker/contacts GET

Probably a contact for a coffee maker repair is also a User (but they don't have to be)

One more thing, you can turn it around and say that a contact is for an appliance (i.e. a coffee maker)

  /contact/for-appliance/{appliance-id} GET

My point being that if you decouple the contacts from the users, you can assign contacts to other entities instead of forcing the relationship to be User -> Contacts - unless, of course, you don't want to decouple them because of business rules or some reason

In any case, beware that there are many ways of doing the mapping

Upvotes: 1

Sergii Getman
Sergii Getman

Reputation: 4381

Lets suggest that number of entities related to User will increase in future. So it obvious that it is better to split it according to entities:

UserController -> UserService -> UserRepository,

ContactController -> ContactService -> ContactRepository,

FriendshipController -> FriendshipService -> FriendshipRepository

From my experience, User Controller

@RestController
@RequestMapping("/user")
public class UserController extends AbstractController {

...

   @RequestMapping(method = RequestMethod.POST)
   public ResponseEntity<?> createUser(@RequestHeader("X-Auth-Token") Optional<String> @RequestBody User user) {

...

   @RequestMapping(method = RequestMethod.GET)
   public ResponseEntity<?> listUsers(@RequestHeader("X-Auth-Token") Optional<String> authToken) {
...

related to user scope Friendship controller:

@RestController
@RequestMapping("/user/{id}")
public class FriendshipController extends AbstractController {

...

@RequestMapping(value = "/friendship/code", method = RequestMethod.POST)
    public ResponseEntity<?> generateCodeForUser(@PathVariable("id") long id) {

...

 @RequestMapping(value = "/friendship/code", method = RequestMethod.GET)
    public ResponseEntity<?> retrieveCodeForUser(@PathVariable("id") long id) {

...

Not sure it is axiom, but help me organize my code.

Upvotes: 13

Related Questions