A New Architecture for APIs Part II — Declarative Implementation
We recently wrote an article about the new API Architecture — where composition is built in. This architecture comes naturally to GraphQL, but the implementations in support of it widely differ. In this paper, we discuss a declarative approach to this API architecture.
Declarative Specification and Assembly of Graphs
Let’s take the analogy of database systems, where the owner of the database might create two tables and a view that “stitches” the two tables like this:
Here are two tables that are declaratively specified, customer and order (and their implementation is left to the database system). Then a view is defined that “assembles” the two tables, again declaratively, by linking
Not only are the specifications simple and clean, but because the implementation is understood by the database system, queries submitted like the following can be optimized for execution.
select * from customerWithOrders where email=”email@example.com”
For example, if the customer table is small, it might be table scanned. If it is large, and an index exists on
customer.email, it might be used. And several other such options exist; the database decides the right execution plan.
A declarative approach for GraphQL takes a similar approach, but with one specific twist: The tables above are “virtual tables,” pointing to some backend that might be a REST API, a SOAP API, a real database or even another GraphQL backend. In the real world, GraphQL does not throw out investments, it builds on them.
So the equivalent of the above would look like, in a declarative GraphQL implementation (this is pseudo syntax, not real syntax):
You can immediately spot the 1:1 correspondence with databases, except for the implementation specifications of the virtual tables. Fundamentally, you are assembling your GraphQL API with a series of declarative building blocks — each producing a small graph against a domain or a backend — and stitching them together with another piece of declarative code.
Now imagine that there is a specification, say @hide:
By only exposing the view, *all* implementation details are hidden. The API, with that view, can be externalized to other teams or to third parties. This API might get deployed to
As a final piece of the puzzle, you could combine these views into an even higher level of construct, but while that can be done by one team, the interesting aspect is that it can also be done across teams. Imagine a second team, say the stores team, constructing a GraphQL API about physical stores, their timings, etc. It might build its API out of a database for the physical store locations and provide directions using Google Maps. It might look something like this:
This API might get deployed to
Just like each of the
storesAndDirections APIs had some stitching within them, we can stitch across them.
The above (pseudocode again) says that there are two GraphQL backends, and they are stitched together. For this layer, it does not matter how each of these subgraphs is built.
As you can see, with the above approach, you have the declarative build and composition of Lego blocks for either graphs, or graph of graphs. The latter is what is called federation.
The above can, of course, be done to any level — it all depends on how much autonomy teams, and a team of teams want:
It Is All about Choice
The more declarative your system is, the better the performance, code simplicity and governance. But the good thing about GraphQL and of the declarative approach is that it is not “all or nothing.” That means large organizations do not have to standardize on one technology. How it federates across graphs might be different than how individual teams build their graphs.
However, it is very clear that:
- Declarative assembly of graphs is the right approach for federation, and is now a standard practice for the federation tier, though there are various federation stitching models.
- While programmatic or imperative ways of building a graph are common (open source libraries exist in various languages), declarative approaches offer the speed of implementation and built-in performance optimizations that are often unmatched by any imperative open source library.
The writing is on the wall: GraphQL will be built and assembled declaratively. It doesn’t matter where or how you start your declarative journey — be it the federated graph, or be it how a team builds their graph. The dividends in increased velocity, better performance, reduction in code complexity and much better and faster evolution would lead to an even faster ride into “It’s the API economy, stupid!”