Monoliths and decoupled architectures
The GraphQL query language
In this chapter, we review traditional web applications, the classic MVC pattern based on views, models, and controllers.
We begin to outline use cases, benefits, and drawbacks of decoupled architectures. We explore the foundations of REST, look at how it compares to GraphQL, and learn that REST APIs are not only RESTful after all.
Monoliths and MVC
For at least two decades, traditional websites and applications all shared a common design based on the Model-View-Controller pattern, abbreviated MVC.
MVC has variations, like the Model-View-Template pattern employed by Django. In Django’s MVT, the data comes still from the database, but the view acts like a controller: it gets data from the database through the ORM and injects the result in a template, which is returned to the user. MVC and its variations are well and alive: all the most popular web frameworks like .NET core, Rails, Laravel, and Django itself employ this pattern with success. However, in recent times we are seeing the spread of decoupled applications based on a service-oriented architecture.
What Makes a Decoupled Architecture?
A decoupled architecture is a system that abides to one of the most important rules in software engineering: separation of concerns.
To muddle things further, frameworks like Angular adopt the Model-View-Controller pattern even for structuring frontend code. In a single-page application, we can find the same MVC design, which replicates the backend structure. You can already guess that one of the disadvantages of a purely decoupled architecture is, to some extent, code duplication. Having defined what is a decoupled architecture, let’s now talk about its use cases.
Why and When to Decouple?
Interactive dashboards with heavy JS-driven interactions
Static site generation
With Django, you can build all sorts of things involving machine-to-machine communication. Think of an industrial application to collect data from sensors that can be later aggregated in all sorts of data reporting. Such dashboards can have a lot of JS-driven interactions. Another interesting application for decoupled architectures are content repositories . Monoliths like Django, CMS like Drupal, or blogging platforms like WordPress are good companions for static site generators. We explore this topic in detail later.
Websites with little or no JS-driven interactions
Content-heavy websites with search engine optimization in mind
As you will see in Chapter 7, frameworks like Next.js can help with search engine optimization for single-page apps by producing static HTML. Other examples of frameworks employing this technique are Gatsby and Prerenderer.
Hypermedia All the Things
The foundation for almost any decoupled frontend architecture is the REST architectural style .
HTTP Methods with the Corresponding Effect on a Given Resource Present on the Backend
Partial update resource
To refer to this set of HTTP methods we also use the term CRUD, which stands for Create, Read, Update, and Delete. As you can see from the table, some HTTP verbs are idempotent, meaning that the result of the operation is always stable. A GET request for example always returns the same data, no matter how many times we issue the command after the first request.
When creating a new resource with POST instead, we can send a request body alongside the request. Depending on the operation type, the API can respond with an HTTP status code, and with the newly created object. Common examples of HTTP response code are 200 OK and 201 Created, 202 Accepted. When things don’t go well, the API might respond with an error code. Common examples of HTTP error codes are 500 Internal Server Error, 403 Forbidden, and 401 Unauthorized.
Hypermedia as the engine: When requesting a resource, the response from the APIs must also include hyperlinks to related entities or to other actions.
Stateless: The communication between client and server should not use any data stored on the server.
Cacheable: The API should leverage HTTP caching as much as possible.
Uniform interface: The communication between client and server should use a representation of the resources involved, and a standard language for the communication.
It’s worth taking a quick detour to dive deeper into each of these rules.
Hypermedia as the Engine
In the original dissertation, this constraint is buried under the Uniform Interface section, but it’s crucial for understanding the real nature of REST APIs.
A JSON Response with Hyperlinked Relationships
A JSON Response with Pagination Links
Another interesting feature of the Django REST framework is the browsable API, a web interface for interacting with the REST API. All these features make Django REST framework Hypermedia APIs ready, which is the correct definition for these systems.
The second constraint, client-server separation, is easily achievable.
A compliant REST API should be stateless.
Stateless means that during the communication between client and server, the request should not use any context data stored on the server. This doesn’t mean that we can’t interact with the resources exposed by the REST APIs. The constraint applies to session data, like session cookies or other means of identification stored on the server. This strict prescription urged engineers to find new solutions for API authentication. JSON Web Token, referred to as JWT later in the book, is a product of such research, which is not necessarily more secure than other methods, as you will see later.
A compliant REST API should take advantage of HTTP caching as much as possible.
An HTTP Response with Cache Headers
Another method for enabling HTTP caching involves the Last-Modified header. If the server sets this header, the client can in turn use If-Modified-Since or If-Unmodified-Since to check the resource’s freshness.
301 Moved Permanently
404 Not Found
206 Partial Content
Moreover, responses with the Authorization header set aren’t cached by default, unless the Cache-Control header includes the public directive. Also, as you will see later, GraphQL operates mainly with POST requests, which aren’t cached by default.
Uniform interface is one of the most important rules of REST.
One of its tenets, representations, prescribes that the communication between client and server, for example to create a new resource on the backend, should carry the representation of the resource itself. What this means is that if I want to create a new resource on the backend, and I issue a POST request, I should provide a payload with the resource.
A POST Request
Here we use JSON as the media type, and a representation of the resource as the request body. Uniform interface refers also to the HTTP verbs used to drive the communication from the client to the server. When we talk to a REST API, we mainly use five methods: GET, POST, PUT, DELETE, and PATCH. These methods are also the uniform interface, that is, the common language we use for client-server communication. After reviewing REST principles, let’s now turn our attention to its alleged contender, GraphQL.
An Introduction to GraphQL
GraphQL appeared in 2015, proposed by Facebook, and marketed as a replacement for REST.
A JSON Response from a REST API
A GraphQL Query
A JSON Response from the Previous Query
A GraphQL Query over a POST Request
A GraphQL Mutation
A GraphQL Subscription
A Simple GraphQL Schema
Graphene, with its code-first approach to building GraphQL services
Ariadne, a schema-first GraphQL library
Strawberry, built on top of data classes, code-first, and with type hints
All these libraries have integrations with Django. The difference between a code-first approach and a schema-first approach to GraphQL is that the former promotes Python syntax as a first-class citizen for writing the schema. The latter instead uses a multi-line Python string to represent it. In Chapters 10 and 11, we work extensively with GraphQL in Django with Ariadne and Strawberry.
Monoliths are systems acting as a whole unit to serve HTML and data to the users
REST APIs are in reality Hypermedia APIs because they use HTTP as the communication medium, and hyperlinks for providing paths to related resources
GraphQL is a strong contender for REST