Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

API Design: Caching “partial” nested objects

Let's say we have schools with some data including a name and a list of students, and students with some data including courses they're enrolled in and a reference to their school. On the client:

  • I'd like to show a screen that shows information about a school, which includes a list of all of its students by name.
  • I'd like to show a screen that shows information about a student, including the name of their school and the names of courses they're taking.
  • I'd like to cache this information so that I can show the same screen without waiting on a new fetch. I should be able to go from school to student and back to school without fetching the school again.
  • I'd like to show each screen with only one fetch. Going from the school page to the student page can take a separate fetch, but I should be able to show a school with the full list of student names in one fetch.
  • I'd like to avoid duplicating data, so that if the school name changes, one fetch to update the school will lead to the correct name being shown both on the school page and the student pages.

Is there a good way to do all of this, or will some of the constraints have to be lifted?

A first approach would be to have an API that does something like this:

GET /school/1

{
    id: 1,
    name: "Jefferson High",
    students: [
        {
             id: 1
             name: "Joel Kim"
        },
        {
             id: 2,
             name: "Chris Green"
        }
        ...
    ]
}


GET /student/1

{
    id: 1,
    name: "Joel Kim",
    school: {
        id: 1,
        name: "Jefferson High"
    }
    courses: [
        {
             id: 3
             name: "Algebra 1"
        },
        {
             id: 5,
             name: "World History"
        }
        ...
    ]
}

An advantage of this approach is that, for each screen, we can just do a single fetch. On the client side, we could normalize schools and students so that they reference eachother with IDs, and then store the objects in different data stores. However, the student object nested inside of school isn't a full object -- it doesn't include the nested courses, or a reference back to the school. Likewise, the school object inside of student doesn't have a list of all attending students. Storing partial representations of objects in data stores would lead to a bunch of complicated logic on the client side.

Instead of normalizing these objects, we could store schools and students with their nested partial objects. However, this means data duplication -- each student at Jefferson High would have the name of the school nested. If the school name changed just before doing a fetch for a specific student, then we'd show the right school name for that student but the wrong name everywhere else, including on the "school details" page.

Another approach could be to design the API to just return the ids of nested objects:

GET /school/1

{
    id: 1,
    name: "Jefferson High",
    students: [1, 2]
}


GET /student/1

{
    id: 1,
    name: "Joel Kim",
    school: 1,
    courses: [3, 5]
}

We'd always have "complete" representations of objects with all of their references, so it's pretty easy to store this information in data-stores client side. However, this would require multiple fetches to show each screen. To show information about a student, we'd have to fetch the student and then fetch their school, as well as their courses.

Is there a smarter approach that would allow us to cache just one copy of each object, and to prevent multiple fetches to show basic screens?

like image 265
Michael Marvick Avatar asked Aug 08 '16 03:08

Michael Marvick


1 Answers

You might be mixing two concepts: Storage and Representations. You can give back a non-normalized representation (the first option you suggested) without also storing those "partial" object in your database.

So I would suggest to try to return non-normalized representations, but storing them normalized (if you are using a relational DB).

Also, an improvement suggestion: You may want to use proper URIs instead of Ids in your representations. You probably want the clients to know "where" to get that object from, it's easier therefore to just supply the URI. Otherwise the client needs to figure out how to produce a URI out of an Id, and that usually ends up being hard-coded in the client, which is a no-no in REST.

like image 79
Robert Bräutigam Avatar answered Sep 27 '22 17:09

Robert Bräutigam