Are APIs broken?
APIs have one job when enquired to perform some action or dispatch data from the backend. As long as
one could remember APIs are identified by their URL. To be technically correct it is URI which stands for
Universal Resource Identifier. An example of a URI is as simple as the entry you see in this browser's
address bar. But then we all know it leads to a web page. For conversation's sake if we assume it is an API
one can use a console line client you can use a program like curl or Invoke-RestMethod or Invoke-
WebRequest to enquire. It will look something like this:
$response = Invoke-WebRequest -Uri http://www.examplehost.com/records -Method Get -Body @{p=100}
The identifier
For this API the identifier is the parameter that is passed which is -Uri. We are using the HTTP method of
GET. A query parameter is passed by passing value to the -Body parameter. We are using a short
character, but we can let you in by saying it is the size of the page. For this long, we did not have any
problem with this approach of working with the APIs.
The schema
To cry out loud one can invoke the commotion that we do not know what kind of response we will get. i.e.,
what will be there in $response? We can answer a few by leaning on the HTTP standards. Headers which
are part of any HTTP response e.g., content-type will tell us about the format of the data in
the $response variable. For most practical usage that is all one will look for in the response. The next
important thing which is required for API consumers is the schema of the content in response. The schema
informs us about the purpose of each property and element in the response content. This problem was
solved by introducing metadata endpoints. By the way, the URI is called an endpoint for easy parlance. The
endpoint for the metadata will be different from the one for enquiring or triggering action. This endpoint
emits information about the data structure and its purpose in simple English as a description for respective
fields. Now this is possible in XML Web Services, OpenAPI etc.
If you want a peek about the meta it will look like this –
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified"
targetNamespace="http://example.com/hr/schemas"
xmlns:hr="http://example.com/hr/schemas">
<xs:element name="ReferralRequest">
<xs:complexType>
<xs:sequence>
<xs:element ref="hr:Referral"/>
<xs:element ref="hr:Employee"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Referral">
<xs:complexType>
<xs:sequence>
<xs:element ref="hr:FirstName"/>
<xs:element ref="hr:Lastname"/>
</xs:sequence>
</xs:complexType>
</xs:element
<xs:element name="Employee">
<xs:complexType>
<xs:sequence>
<xs:element ref="hr:Number"/>
…
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Number" type="xs:integer"/>
<xs:element name="FirstName" type="xs:NCName"/>
<xs:element name="LastName" type="xs:NCName"/>
</xs:schema>
This is an example of a schema for an XML web service which is glorified by the word SOA services.
Though, not limited to it. OAS uses JSON data structure to accomplish this exact purpose.
That puts the problem in our rearview mirror. Let us throw ourselves into another problem. This is specific
to the domain. Say we need the API for two different purposes. One where we want to know the first name
of the referral for a quick filter capability. We also use this same API to fetch the list of all employees and
their referrals. There is nothing wrong with making these calls repeatedly. But every time we make a call,
we get all the details of the employee number, first name and last name etc. But we are interested in part of
it for different purposes.
The query
We feel this is not a problem in the first place. Always when data is serialized there is some redundancy or
extra that tags along. Sometimes it is the protocol or serialization format and sometimes it is the data itself
like in the case above. To tackle situations like this over time we have devised various solutions – Cache
comes to our mind first, Accepting the response as is and dropping not needed fields comes second.
The data
With the API and the fields that are sent as part of the response to a query, one still has to work with the
data and interface with the database. This is done using the ODBC equivalent drivers and query languages
like SQL and its variants are put to work. A typical example will for this will look like –
…
using (OleDbConnection connection = new OleDbConnection(connectionString))
using (OleDbCommand command = new OleDbCommand("…", connection))
{
try
{
connection.Open();
OleDbDataReader reader = command.ExecuteReader();
while (reader.Read())
{
//Build the response type here
}
reader.Close();
}
catch (Exception ex)
{
//Build the error response type here
}
}
…
The challenge with this is the SQL-like syntax used to generate the relevant query to fetch data. The boiler
plating to enumerate and package data to C# types and dispatch them to the client. ORM solved many of
the problems here but in the process introduced a few as well. But we have got a handle on this problem.
GraphQL
It is a specification that defines how APIs are defined and clients interact with the APIs. It is a novel
approach from the foundries of Facebook. They have been an instant hit with the developers who rely on
JavaScript to get their job done. But mind you GraphQL is not limited by a single language. It has
implementations in Java and C# too.
Schema
Claims to rethink the way we see APIs. Starting from making an API recognisable by the schema it works
with. Though it does not say to replace the URI but think about it URI does not yield much about the API it
is the schema which gives it a purpose. In that manner of speaking, it claims to be schema first. Thus, one
does not have a separate endpoint for the schema. Thus, a schema is no different from the API and is part
of it. Take a look at the C# code for defining one using the graphql-dotnet
using System;
using System.Threading.Tasks;
using GraphQL;
using GraphQL.Types;
using GraphQL.SystemTextJson;
public class Program
{
public static async Task Main(string[] args)
{
var schema = Schema.For(@"
type Query {
ReferralRequest: {
Referral: {
…
},
Employee: {
…
}
}
");
var json = await schema.ExecuteAsync(_ =>
{
_.Query = "{ ReferralRequest }";
_.Root = …;
});
Console.WriteLine(json);
}
}
The schema is exposed from the same URI as the service used to deliver data. The _.Root is the place
where data is placed but for that, we need to work with resolvers. Resolvers are elements in the GraphQL
specification that instruct how to fill data for an attribute. It could be a single query or a complex one with
multiple joins. That brings us to the data. As apparent we will still need an ORM. Like Entity framework or
SQL command to get the data.
The query
In the modern day of low-power devices with limited bandwidth to data, the ability to get exactly what is
asked for, from the server is a blessing. GraphQL aces in that capability. So a query like this sent from the
client to server will fetch just the first-name –
…
var json = await schema.ExecuteAsync(_ =>
{
_.Query = "{ ReferralRequest : {Referral: {FirstName}}}";
_.Root = …;
});
…
All other data elements in the otherwise valid schema will not be returned in the _.Root. There is more to
query than the ability to filter the required elements. Things like mutation, fragments help to accelerate the
journey to actual data. We will get to them in our next dispatch.