DEV Community

loading...

Unleash the power of REST with JsonApiDotNetCore

Petar Radošević
Dad of five, software engineer with pen and paper. Architecture and APIs at Degreed.
Updated on ・5 min read

Reason for celebration, after two years of development, there is another major release of JsonApiDotNetCore (JADNC), the 4.0 release.

This release packs a lot of powerful features backed by 1000+ tests! To show off the amazing work by the team, I decided to write a three-part blog series where I'll demo its ease of use and capabilities by building a new API.

But before we start, let me convince you why you should still use JSON:API and don't need to go to GraphQL for that powerful API.

Levels

REST APIs come in levels and as developers like to do, it starts counting at 0.

Level 0 means it's using HTTP as the transport layer for remote communications. That's it.

Level 1 is where we add resources. Instead of talking to a single endpoint, let's say /api, you get multiple endpoints, /pizzas and /pastas.

Level 2 is when we add verbs. Communicate your intent on the resource by using HTTP verbs. Use GET when you know it is a read operation — does not make changes on the server — and POST when you want to change state.

In the final level, Level 3, the server adds URIs to the response which tell the client where to go if they want more information.

For some time the software industry has been releasing level 3 APIs and created specifications which guide you on the best practices. The most popular specification is the JSON:API specification, which you could guess, is used by us.

I also need to address another giant in this space. In 2015, Facebook released GraphQL to solve problems they experienced with the shift to mobile. GraphQL reduced the amount of network bandwith you need by its query capabilities. You can combine multiple requests into one, and get back the exact payload you need.

The downside of GraphQL was that it stepped away from some of the best practices on the web 👎.

You don't get Level 1 capabilities, where we have multiple endpoints, or Level 2, where we use HTTP verbs to communicate your intent.

Although GraphQL can use HTTP as a transport, it always uses a single endpoint. This means that decades of hardware and software and their caching solutions on top of HTTP standards can't cache GraphQL queries easily.

However! The communities' embrace of GraphQL showed us that people are happy to make these sacrifices for the increase in power they gained as consumers of APIs. Being able to reduce the amount of requests by introducing powerful query capabilities is an amazing thing to have.

In part 2 and 3 of this series we will show you how JSON:API gives you those capabilities, without loosing some of the benefits of REST. Before we do that, let's first cover the basics and get a project up and running.

Getting started with JSON:API

Let's start with creating a web project and installing the JsonApiDotNetCore library:

dotnet new webapi -o MyApi
cd MyApi
dotnet add package JsonApiDotnetCore
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
Enter fullscreen mode Exit fullscreen mode

You can run the project with dotnet run and a webserver will spin up and be available at https://localhost:5000. It won't serve anything useful, for that we need to add models.

Let's add our first model by creating a Person.cs file and adding a Person:

using JsonApiDotNetCore.Resources;
using JsonApiDotNetCore.Resources.Annotations;

namespace MyApi
{
    public class Person : Identifiable
    {
        [Attr]
        public string Name { get; set; }
    }
}
Enter fullscreen mode Exit fullscreen mode

There are two important things to note here. First we make Person implement the IIdentifiable<TId> interface by inheriting from Identifiable. This assumes our model has a unique key called Id with integer values.

Next step is adding Entity Framework Core to the mix.

Entity Framework Core

Entity Framework Core is a powerful ORM developed by Microsoft and JsonApiDotNetCore makes good use of it. To add the database as a storage mechanism for our model we need to create a new AppDbContext.cs file with the following:

using Microsoft.EntityFrameworkCore;

namespace MyApi
{
    public class AppDbContext : DbContext
    {
        public AppDbContext(DbContextOptions<AppDbContext> options)
            : base(options) { }

        public DbSet<Person> People { get; set; }
    }
}
Enter fullscreen mode Exit fullscreen mode

The DbSet is used to be able to query and save the Person model to the database.

Finally, we need to wire up the endpoint by creating a controller. When we ran dotnet new webapi -o MyApi, it created a controller and a model for us. Let's delete those two files as we won't need them.

rm WeatherForcast.cs
rm Controllers/WeatherForecastController.cs
Enter fullscreen mode Exit fullscreen mode

And create a new controller in Controllers/PeopleController.cs.

using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Controllers;
using JsonApiDotNetCore.Services;
using Microsoft.Extensions.Logging;

namespace MyApi.Controllers
{
    public class PeopleController : JsonApiController<Person>
    {
        public PeopleController(
                IJsonApiOptions options,
                ILoggerFactory loggerFactory,
                IResourceService<Person> resourceService)
            : base(options, loggerFactory, resourceService)
        { }
    }
}
Enter fullscreen mode Exit fullscreen mode

In the above code we see that the method receives IJsonApiOptions. It provides global options you can set in your application startup and lets you configure things like JSON casing and if clients are allowed to generate their own IDs.

Second we see the IResourceService, which hooks up the API to the persistence layer of the model.

To tie it all together we can enable JsonApiDotNetCore during startup of the project. Go to your Startup.cs file and replace ConfigureServices with the following:

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    // Add the Entity Framework Core DbContext like you normally would
    services.AddDbContext<AppDbContext>(options =>
    {
        // Use whatever provider you want, this is just an example
        options.UseSqlite("Data Source=my-api.db");
    });

    // Add JsonApiDotNetCore
    services.AddJsonApi<AppDbContext>();
}
Enter fullscreen mode Exit fullscreen mode

This uses the AppDbContext we created earlier and uses SQLite as our database. Entity Framework Core supports MS SQL Server, SQLite, PostgreSQL, MySQL and Cosmos DB out of the box.

Finally, let's replace the default Configure method as well in our Startup.cs file with the following:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, AppDbContext context)
{
    context.Database.EnsureCreated();

    if (!context.People.Any())
    {
        context.People.Add(new Person
        {
             Name = "John Doe"
        });
        context.SaveChanges();
     }

     app.UseHttpsRedirection();
     app.UseRouting();
     app.UseJsonApi();
     app.UseEndpoints(endpoints => endpoints.MapControllers());
}
Enter fullscreen mode Exit fullscreen mode

The UseJsonApi installs middleware for handling the incoming requests.

This is everything that is required to have a JSON:API, let's give it a run with dotnet run and send a request:

curl -k [https://localhost:5001/people](https://localhost:5001/people) | jq

This returns a 200 OK with a beautiful payload:

{
  "links": {
    "self": "https://localhost:5001/people",
    "first": "https://localhost:5001/people"
  },
  "data": [
    {
      "type": "people",
      "id": "1",
      "attributes": {
        "name": "John Doe"
      },
      "links": {
        "self": "https://localhost:5001/people/1"
      }
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

What's next

This is all that's needed to get a JSON:API up and running. You can find all the code of this part on Github under the part-1 branch.

In our next post I'll explore the power of relationships and how JSON API can reduce the amount of requests we need to make to get the data we need.

Discussion (0)