This is the final part of our three-part series on the release of JsonApiDotNetCore 4.0! In our first post we showed you how to setup your API and in the second post we added relationships to the mix.

In this final post we are going deep and show you some of the latest additions to the framework and what's coming next.

Filters

One of the powerful new additions to the framework is the ability to have composable filters. Let's say we want to have all todo-items with a priority lower than 2. We could make the following request: GET /todoItems?filter=lessThan(priority,'2')

{
  "links": {
    "self": "<https://localhost:5001/todoItems?filter=lessThan(priority,'2')>",
    "first": "<https://localhost:5001/todoItems?filter=lessThan(priority,'2')>"
  },
  "data": [
    {
      "type": "todoItems",
      "id": "1",
      "attributes": {
        "todo": "Make pizza",
        "priority": 1
      },
      "relationships": {
        "owner": {
          "links": {
            "self": "<https://localhost:5001/todoItems/1/relationships/owner>",
            "related": "<https://localhost:5001/todoItems/1/owner>"
          }
        }
      },
      "links": {
        "self": "<https://localhost:5001/todoItems/1>"
      }
    }
  ]
}

In part 2 we also showed you how to include related resources, and the nice thing is, you can even apply filters on those! Let's say we want to get all people, along with their todo-items of first priority.

First, we need to include the related todo-items. Now we can apply the filter with the following request: GET /people?include=todoItems&filter[todoItems]=equals(priority,'1').

This will give us the following payload, where we only see the todo-items with a priority of one.

{
  "links": {
    "self": "<https://localhost:5001/people?include=todoItems&filter[todoItems]=equals(priority,'1')>",
    "first": "<https://localhost:5001/people?include=todoItems&filter[todoItems]=equals(priority,'1')>"
  },
  "data": [...],
  "included": [
    {
      "type": "todoItems",
      "id": "1",
      "attributes": {
        "todo": "Make pizza",
        "priority": 1
      },
      "relationships": {
        "owner": {
          "links": {
            "self": "<https://localhost:5001/todoItems/1/relationships/owner>",
            "related": "<https://localhost:5001/todoItems/1/owner>"
          }
        }
      },
      "links": {
        "self": "<https://localhost:5001/todoItems/1>"
      }
    }
  ]
}

We have now seen lessThan and equals, but there are more operators you can apply, like contains, any and even not to negate the filter. For a complete list, you can read the filtering chapter in the documentation.

But it becomes even more powerful when we combine multiple conditions together. If we wanted to have all todo-items from people whose name starts with John and have a priority of one, we can make the following request:

GET /todoItems?filter=and(startsWith(owner.name,'John'),equals(priority,'1'))

All of the above is being translated to SQL and nothing is being evaluated in memory! This is done by building up a tree of expressions which are then translated into a LINQ query. You can read more of the work behind the scenes in this PR 792. Magic...

Finally, the filtering on related resources is not only available on filters, but you get those as well for pagination, sorting and even sparse fieldsets. You could go crazy with a request like the following:

GET /api/v1/blogs?include=articles.comments&page[number]=2,articles:3,articles.comments:4&page[size]=10,articles:5

This returns at most 10 blogs at page 2. Per blog, it returns at most 5 included articles, all at page 3. Per blog, per article, returns included comments at page 4 using default page size. Mind blown yet?

Conclusion

Although this blog series may have ended, the development on JsonApiDotNetCore has not. Before I let you go, I would like to highlight an exciting new development called "Atomic Operations". This will allow you to send multiple changes in one request (on the streets people may call it batching).

This will allow you to reduce the amount of requests for doing changes. It's so powerful that it even allows you to establish relationships between operations within a single request, using something called local IDs. Work is progressing on the atomic-operations branch if you would like to follow along.