Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RESTful API - Handling nested relations

Let's say I'm building an API to handle appointments. An appointment consists of a Doctor, a Patient, and a set of Cares.

If I want to create an appointment, what should the POST data look like? Please note that only the appointment would get created with this query. The doctor, patient, and cares entries already exist in the database.

I've got 2 options:

The short one

{
  doctor_id: 8,
  patient_id: 4,
  cares_ids:
  [1,7]
}

The longer one

{
  doctor: {
    id: 8,
    name: 'Dr James Brown',
    phone: '107-102-304',
    address: '16th avenue'
  },
  patient: {
    id: 4,
    name: 'Mr Elvis',
    pathology: 'Blah Blah.'
  },
  cares:
  [
    {
      id: 1,
      name: 'Dental cares'
    },
    {
      id: 7,
      name: 'Back pain'
    }
  ]
}

The short one feels cleaner, as we are not sending useless data like name and address etc. But the second one feels more natural/semantic in my front-end code.

For example in my application, I would only have to do:

appointment.doctor = selected_doctor
// Instead of doing
appointment.doctor_id = selected_doctor.id

Are there any RESTful best practices for this kind of case?

like image 530
gkpo Avatar asked Mar 27 '26 06:03

gkpo


1 Answers

In fact, you're facing to the issue of relations. OData provides solution to this problem called "navigation properties". Perhaps this could give you some hints on the way to fixed this problem. You can have a look at these links for more details: http://www.odata.org/getting-started/basic-tutorial/ and http://www.odata.org/getting-started/advanced-tutorial/

In fact, there are several issues here:

  • Design the representation you want to have when getting an appointment
  • Design the representation to create or update an appointment
  • Design the representation to assign (single cardinality - "doctor" and "patient") / add and remove (multiple cardinality - "cares) a link to an appointment (for example doctor)

I think that the complete representation you give in your question corresponds to what you expect to have when getting an appointment, i.e. the appointment data included the data of relations like doctor, patient and cares.

Such hints aren't all necessary to link an appointment to other elements like doctor and patient. Only the identifiers matter. I think that you could have a different representation for the appointment creation. At this level setting the reference to element is enough. So you could have somethinhg like that:

POST /appointments
{
  doctor-ref: http://.../doctors/8,
  patient-ref: http://.../patients/4,
  cares-ref: [ http://.../cares/1, http://.../cares/7 ]
}

or

POST /appointments
{
  [email protected]: http://.../doctors/8,
  [email protected]: http://.../patients/4,
  [email protected]: [ http://.../cares/1, http://.../cares/7 ]
}

In the same way, you could define additional resources to be able to update these links after the appointment was created without sending all the content. We could imagine something like that:

  • Single cardinality

    PUT /appointments/<appointmentid>/doctor
    { [email protected]: http://.../doctors/8 }
    
  • Multiple cardinality

    POST /appointments/<appointmentid>/cares
    { metadata.ref: http://.../cares/8 }
    DELETE appointments/<appointmentid>/cares
    { metadata.ref: http://.../cares/8 }
    

You can notice that you can also choose not to retrieve all appointment data for an appointment (if it's something interesting for you). Here is the different representations you could have:

  • Complete

    {
      doctor: {
        id: 8,
        name: 'Dr James Brown',
        phone: '107-102-304',
        address: '16th avenue'
      },
      patient: {
        id: 4,
        name: 'Mr Elvis',
        pathology: 'Blah Blah.'
      },
      cares:
        [
          {
            id: 1,
            name: 'Dental cares'
          },
          {
            id: 7,
            name: 'Back pain'
          }
       ]
     }
    

    }

  • Parial with links / references

    {
      [email protected]: http://.../doctors/8,
      patient: {
        id: 4,
        name: 'Mr Elvis',
        pathology: 'Blah Blah.'
      },
      [email protected]: [ http://.../cares/1, http://.../cares/7 ]
    }
    

    or

    {
      [email protected]: http://.../doctors/8,
      patient: {
        id: 4,
        name: 'Mr Elvis',
        pathology: 'Blah Blah.'
      },
      cares:
        [
          {
            id: 1,
            name: 'Dental cares'
          },
          {
            id: 7,
            name: 'Back pain'
          }
       ]
     }
    

    }

This feature corresponds in OData to the query parameter $expand.

You could have a look at this link: http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/odata-v4/entity-relations-in-odata-v4. See sections "Getting Related Entities", "Creating a Relationship Between Entities" and "Deleting a Relationship Between Entities". It could give hints for your representation.

The last part of the question is related to the way to build the representation (for creation for example). I don't know exactly the technology you use but in Java, you can do something like that appointment.doctor = selected_doctor and adapt a bit the serialization of your objects to create something like that [email protected]: http://.../doctors/8 from an instance of Doctor. For example with Jackson2, it's called custom serializer (see this link http://www.baeldung.com/jackson-custom-serialization for example).

Hope it helps you, Thierry

like image 176
Thierry Templier Avatar answered Mar 28 '26 23:03

Thierry Templier