Search code examples
restapi-design

REST API POST: passing foreign key in url vs json body


Let's say I have two objects: Student and Homework. Homework has Student as fk:

CREATE TABLE student (
    id serial PRIMARY KEY,
    name varchar(100)
)

CREATE TABLE homework (
    id serial PRIMARY KEY,
    topic varchar(255),
    student_id int REFERENCES student(id)
)

Is there any convention for what is the right way to structure the API endpoints for CRUD?
If I want to create a new homework for a student, I could send a json body with student id

{
    "student_id": 1,
    "topic": "topic
}

to POST https://website.com/api/v1/homework.

Or I could send

{
    "topic": "topic
}

to POST https://website.com/api/v1/students/{student_id}/homework and take student id from URL.

In second case I would be sending a POST request with incomplete fields and in first case I would have one extra endpoint (since I would need /students/{id}/homework anyway to fetch particular student's homework.)


Solution

  • You have two entities Student and Homework. A Homework entity belongs to Student.

    So the more semantically correct approach would be:

    • Create Homework Endpoint:

      POST https://website.com/api/v1/students/{student_id}/homeworks
      
    • Delete Homework EndPoint:

      DELETE https://website.com/api/v1/homeworks/{homework_id}
      

    There are no agreed rules but this is generally a widely followed pattern:

    If A owns B, and you want to create a new B entity, your path will be like /A/{A_Id}/B.

    • Boilerplate Request:

      POST /ParentEntity/ParentId/ChildEntity
      

    Now B was created and you have an id associated with it, so you can directly alter B (say for any mutation operation, POST, DELETE, PUT, PATCH).

    DELETE /B/{id}
    
    PUT /B{id}
    

    OR

    POST /B/{id}/delete
    
    POST /B/{id}/update
    

    (this one is followed in StackOverflow Docs, where your request intention/action is defined in URL suffix instead of being defined by your HTTP method)


    Why do we do directly alter B? Why not do something like:

    DELETE /A/{Aid}/B/{Bid}
    

    Because /A/{Aid} would be redundant information. Since it is guaranteed that {Bid} would always be unique even though multiple B entities can belong to a single A entity.


    Additional References:

    You can see the API pattern Stackoverflow has used for their APIs here for any future reference. https://api.stackexchange.com/docs?tab=category#docs