Zagnieżdżanie endpointów - relacje.

0

Tworzę proste API do platformy zawierającej wpisy z różnych blogów. Zastanawiam się nad strukturą endpointów. Część modelu nad którą się zastanawiam to relacje między encjami: Blog, Post i Komentarz. Gdzie:

  • Komentarz nie może istnieć bez Postu
  • Post nie może istnieć bez Bloga

Nasuwają mi się dwa rozwiązania, ale nie wiem które wybrać:

Pierwsze

/blogs/{blogId}
/blogs/{blogId}/posts/{postId}
/blogs/{blogId}/posts/{postId}/comments/{commentId}

Drugie:

/blogs/{blogId}
/blogs/{blogId}/posts/{postId}
/posts/{postId}/comments/{commentId}

Moje wątpliwości co do wyboru rozwiązania wynikają z faktu, że czym głębsze zagnieżdżenia, tym więcej pracy związanej z obsługą powiązanych encji np:
W pierwszym rozwiązaniu dodając komentarz musiałbym za każdym razem sprawdzać czy podany w ścieżce blog istnieje. W drugim natomiast wystarczy, że sprawdzę post do którego chcę dodać komentarz (zakładam że post nie może istnieć bez bloga tzn. usuwając bloga usuwam też posty i komentarze - więc spójność zostanie zachowana).

1

Tak z praktycznego punktu widzenia:

  1. Skoro mam postId, to po co mi bookId? Gdyby było wiele do wielu albo jakieś relatywne numerowanie postów, to może wtedy, aby uzyskać jednoznaczność.
  2. Nie odzwierciedlaj schematu bazy danych w API. Pathy to jedno, a schemat bazy i walidacja - drugie.
2

Nie wiem czy pobieranie komentarza po id się w praktyce przydaję. Wydaje mi się, że zwykle pobiera się listę komentarzy dla
danego postu zwykle z paginacją.

/posts/{postId}/comments/{commentId}

Tutaj też de facto trzeba by sprawdzić czy taki post istnieje wystarczyłoby tak naprawdę:

/comments/{commentId}

Polecam przeczytać: https://github.com/allegro/restapi-guideline#user-content-minimize-resources-nesting

0

Mam jeszcze jedno pytanie. Podzieliłem już sobie endpointy na konkretne metody w kontrolerach. I zastanawiam się czy dobrze Was zrozumiałem. Postawiłem sobie takie wymagania odnośnie bloga:

  • pobrać wszystkie blogi (do tego jakaś paginacja)
  • pobrać konkretny blog wraz z wszystkim kolekcjami
  • pobrać tylko posty danego bloga
  • pobrać tylko userów bloga

Chce to zrobić w ten sposób:

Blog Controller

GET /blogs
GET /blogs/{blogId}   // pobiera cały obiekt blog wraz z kolekcjami
GET /blogs/{blogId}/posts   // pobiera tylko powiązaną kolekcje obiektów post
GET /blogs/{blogId}/users

Dla postów analogicznie
Post Controller

GET /posts
GET /posts/{postId}
GET /posts/{postId}/comments
itd...

Czy dobrze to zrozumiałem?

1

GET /blogs/{blogId}/users a co to robi? O jakich userów bloga chodzi?

0

@.andy:
Dotyczy userów tego bloga. Czy w ten sposób ma wyglądać endpoint, który pobierze tylko powiązane kolekcje dla zadanego obiektu nadrzędnego. np. wszystkie posty zadanego bloga, wszystkich użytkowników bloga itd. Dodatkowo zakładam że to odpowiedzialność kontrolera obiektu nadrzędnego?

0

@travis.chigurh: Zależy od klienta, który będzie to wyświetlał / konsumował.

1

@lookacode1: Konsumpcja:

  • pobrać wszystkie blogi (list view + paginacja)
  • pobrać konkretny blog wraz z wszystkim kolekcjami (strona details z np, ruchem na stronie, ilość postów itd)
  • pobrać tylko posty danego bloga (list view + paginacja)

Czy powyższe założenia są poprawne co do ich umieszczenia w wyżej wymienionych kontrolerach?

1

@travis.chigurh: Seems legit.

0

Dzięki wszystkim za pomoc :)

0

Pojawił się kolejny problem z endpointem tworzenia postów do konkretnego bloga. Wcześniej chciałem stworzyć post w następujący sposób:

POST /blogs/{blogId}/posts

Jednak jak chcę unikać zagnieżdżonych endpointów, więc myślę o czymś takim:

POST /posts

Ale jak w takim razie przekazać Id powiązanego Bolga? w body? w headerze?

1

Myślmy. Na stole masz 3 opcje:

  1. Przekazać id bloga w path
  2. Przekazać id bloga w body
  3. Przekazać id bloga w headerze

Która jest najbardziej naturalna, tj. bez zakładania gaci przez głowę?

Btw. Czy to nie jest jakieś zadanie rekrutacyjne?

0

@Charles_Ray
Do wczoraj wybrał bym 1. Jednak bardziej naturalne wydaje się body więc 2.

nie jest to zadanie, sam próbuje się uczyć i wiem że nic nie wiem :)

//edit:
chcę też zrobić API nie na zasadzie by działało, ale również miało sens w kontekście realnych projektów

3

Ja bym poszedł w opcję (1), chociaż (2) też wydaje się OK. Opcja (3) to totalny folklor ;)

2

Pierwsze wydaje się bardziej naturalne.

1 użytkowników online, w tym zalogowanych: 0, gości: 1