Introduction to GraphQL - Part I

Introduction to GraphQL - Part I

What is Graph QL

GraphQL gets its name as it gives us a query language to traverse our applications data graph and produce the query results tree. Our application data can easily be represented as graph of nodes liked by edges. For example we are working on employee management system here, that has each employee and his associated department, in this case one department can have multiple employees but one employee belongs to a single department. We can have the graph for this data in our application as in figure. The query that we specify in GraphQL starts from the root and traverses the edges(employee, name, department etc) to get to the nodes that have data.

No alt text provided for this image


Does that mean we can only use graph databases when working with graphQL?? Answer is NO. GraphQL is datastore agnostic which means it does not force us to use any particular type of database/source in order for the queries to process the results. It can be so diverse that we can fetch some part of the result form a miro-service call and others from SQL/noSQL databases without out client applications knowing anything.

More formally GraphQL can be thought of as a query language for our API. It gives the power to build descriptive APIs where the client describes what he/she needs and gets the data relevant to the query. It can even be described as a wrapper over REST, to make REST APIs more efficient and understandable.

Why do we need GraphQL when we have REST

Before answering this question lets first take a look at what is REST

REST stands for representational state transfer. REST is not a protocol but an architectural style for building APIs. RESTful architecture has 6 constraints.

  1. Uniform Interface - Following the principle of generality a RESTful architecture need to separate service side implementation from clients hence there should be a uniform interface or contract that the service and client agree on and can be followed for easy communication. There are 4 constraints that make the RESTful architecture have uniform interface.

Identification of resources - Use a URI(uniform resource identifier) to identify a resource

Manipulation of resources through these representations - Use HTTP standards for communication like GET, POST etc.

Self-descriptive messages?- Use the MIME type and standard RDF(Resource definition framework for resource encoding) to describe the resources

Hypermedia as the engine of application state? (HATEOAS) - Provide the URI information as links in resource itself to decouple clients from application specific URI knowledge

2. Client - Server - It allows for separation of concern and hence independent evolution of clients and servers.

3. Stateless - The request should have full information about the state of the resource and should not rely on server to have any knowledge of previous state.

4. Cacheable - The resource must declare itself cacheable if it can be cached. The caching can be done on client or server side.

5. Layered System - REST makes it possible to have a layered system with business layer separated from data and authentication layers, hence promoting decoupling and more robust software design.

6. Code on demand - REST can return executable code when needed and not just static resources.

The above gives a basic idea of what a RESTful architecture should be. But why did we need GraphQL can be answered once we look at what were the issues in REST APIs that graphQL tries to address.

  1. Too many REST end points - Each REST endpoint returns a fixed response hence we need to have multiple endpoints to satisfy various client needs. GraphQL on the hand has just one endpoint that returns information based on what client asked for.
  2. In REST the server defines the response structure. GraphQL being declarative allows the clients to declare their needs and returns information based on client needs and specifications instead of being dictated by servers.
  3. REST often has over-fetching or under-fetching problem(also called as the n+1 problem) since the server decided the response structure client using an endpoint might be fetching a lot more information that is actually needed, making the fetch a slow operation this is what happens when a client is over-fetching. Under-fetching or n+1 problem occurs when the client needs one extra call for each call made to fetch the resource information as the initial call has not enough information to suffice the client needs. To deal with both these issues GraphQL lets the client declare the needs and gives only as much information as needed by the client.
  4. One thing that remains constant is change and same goes for our endpoints and the data they need to return. With REST the server resource structure needs to be changed and this change is also needed at the client side who was consuming that resource. But in graphQL client describes the query structure and hence needs no cascading server changes, given we respect the same schema as earlier.

These are some advantages that we get from GraphQL by declarative fetching over imperative fetching.

The next question that comes up is How GraphQL is able to achieve all this through a single endpoint.

GraphQL Schema and Resolutions

GraphQL is strongly typed and it uses its own Schema Definition Language (SDL) to define a schema that describes how the data can be queried and what all operations are allowed. This also adds an advantage of graphQL being implementation language agnostic. The SDL is called GraphQL Schema Definition Language. The schema can be thought of as an agreed contract between clients and servers.

type Employee {
    id: ID
    name: String
    dept: Department
}        

Above we define object type Employee.

id, name and department are the fields on the object type Employee and name field will have String values and can even be null. If name would have to have a value always when queried for, then it would be of the type String! where ! promises non nullable values. IDs in GraphQL are generally server generated identifiers and the type is not String. Department is another object type. So fields need not be just scalar types but they can even be other object types. GraphQL supports scalar types like Boolean, Float, String and Int. We can even define our own enums and custom scalar types like dates.

In a similar fashion we can define other object types and fields.

Every field on the GraphQL object can have zero or more arguments.

type Starship {
  id: ID!
  name: String!
  length(unit: LengthUnit = METER): Float
}        

Here in the example above where a field has one argument.

We discussed above the object type in GraphQL but there are special types as well. These behave as entry points and hence are called special. These are query and mutation. While query type is used to read data, mutation can be used to create, update and delete the data. Hence using both query and mutation GraphQL is able to support the CRUD operations that we had with REST.

schema {
    query: Query
    mutation: Mutation
}

type Mutation{
    createEmp(input: EmployeeInput): Employee
    deleteEmp(id:ID!):Employee
}

type Query {
    empById(id: ID): Employee
}

input EmployeeInput{
    name: String
    dept: String
}

type Employee {
    id: ID
    name: String
    dept: Department
}

type Department{
    id: ID
    name: String!
}

        

The above is the schema for a simple employee management system. Where each employee belongs to a department.

Above schema specifies both query and mutation types apart from Employee and Department object types. They just like object types have fields which can have one or more arguments as specified here. Also Note that the entry points to query and mutation all return Employee type object that we have specified in the schema.

Let's see how a GraphQL query with the above schema would look like.

query {
? empById(id: "emp-1") {
? ? id
? ? name
? ? dept {
? ? ? id
? ? ? name
? ? }
? }
}        

Here we specify that we want to read data since we are using the type query. The root of the query is employeeById that takes an argument to fetch the employee by id emp-1. Then we specify the fields that we want to get in the result which includes the id, name and dept of the employee. In dept we further tell what scalar fields we want to display. As can be seen here what is needed is specified or declared by the client based on the schema(contract) that the client and server agreed upon. Thus this allows clients to control what information they need, reducing the number of network calls and the data that flows on the network.

{
    "errors": [],
    "data": {
        "empById": {
            "id": "emp-1",
            "name": "Ane",
            "dept": {
                "id": "dept-1",
                "name": "HR"
            }
        }
    },
    "extensions": null,
    "dataPresent": true
}        

The response to the above query would look as displayed above. The response is in same format as the query. Stating with the result of employeeById and then giving details around values of each field.

mutation{
? deleteEmp(id:"253b6def-e365-43d3-b88d-c510cd05d430"){
? ? ? name
? }
}
        

The above mutation deletes an employee with the given id and returns the name of the deleted employee and not any other field. The word mutation tells that it is one of the create, update or delete operations. The response would look similar to the query response in structure.

{
    "errors": [],
    "data": {
        "deleteEmp": {
            "id": "253b6def-e365-43d3-b88d-c510cd05d430"
        }
    },
    "extensions": null,
    "dataPresent": true
}        

Apart from query and mutations GraphQL has subscription where the clients can get updates pushed to them from the server when the data they care about changes.

Resolution in GraphQL

The next question that comes to mind is how is GraphQL able to getEmployeebyID or perform any of the CRUD operation. There is a concept of resolution in GraphQL where each query or mutation is resolved at the field level. The process starts from the root of the query/mutation, resolves all the fields at the same level concurrently and then the result of the resolution is passed by default to the fields one level below the current level.

query {
? empById(id: "emp-1") {
? ? id
? ? name
? ? dept {
? ? ? id
? ? ? name
? ? }
? }
}        

For example in the above query retrieving information of an employee given its id. The root of the query is employeeById with a parameter id. As specified in the schema it returns an employee object type. This employee object type would be passed as default argument to resolvers for id, name and department. So for name field the resolver could just return name of this employee it got from root resolver execution.

Thus we got a basic idea about what is GraphQL , the issues that are addressed using GraphQL. Some basic elements of GraphQL like the most important schema and SDL.How GraphQL supports all CRUD operations on a single endpoint and how a query/mutation is able to produce result.

What's next

  1. Building a GraphQL server application
  2. Building a GraphQL client application

Sources of knowledge

  • https://graphql.org/graphql-js/basic-types/
  • https://graphql.org/learn/schema/
  • https://restfulapi.net/rest-architectural-constraints/

要查看或添加评论,请登录

Aneshka Goyal的更多文章

  • Introduction to Distributed Tracing

    Introduction to Distributed Tracing

    What is Distributed Tracing? The word tracing is to trace the request as it flows through the system. Since modern…

    2 条评论
  • Introduction to Service Discovery

    Introduction to Service Discovery

    What is Service Discovery? Service Discovery as the name suggests allows us to know or discover where each instance of…

  • Introduction to Micro frontend

    Introduction to Micro frontend

    What is Micro frontend? The term “micro frontends” debuted in the 2016 ThoughtWorks Technology Radar guide. At its…

  • Introduction to Pub-Sub and Streams with Redis&SpringBoot

    Introduction to Pub-Sub and Streams with Redis&SpringBoot

    Publish/Subscribe Problem: Let's say we have synchronous messaging between two components of our system called as…

    2 条评论
  • Introduction to Time Series Database - InfuxDB

    Introduction to Time Series Database - InfuxDB

    What is Time Series Data? As the title of the blog depicts we would be discussing about time series databases and in…

    1 条评论
  • Introduction to Ontology

    Introduction to Ontology

    What is Ontology? An ontology is a formal and structural description of knowledge about a specific domain. Knowledge is…

  • From Java 17 to Java 21 - Features and Benefits

    From Java 17 to Java 21 - Features and Benefits

    Java has been constantly evolving with new features and enhancements. With the recent LTS (Long term support) version…

    2 条评论
  • Vault Authentication and Springboot integration

    Vault Authentication and Springboot integration

    What is Vault? Vault is an identity-based secrets and encryption management system. A secret is anything that we want…

  • Introduction to gRPC with Spring boot

    Introduction to gRPC with Spring boot

    Overview RPC stands for remote procedure calls. In this the client is able to directly invoke a method on server…

    6 条评论
  • Introduction to Triple Crown

    Introduction to Triple Crown

    Organizations are always trying to improve how they work, in order to increase efficiency and reduce errors. This…

    4 条评论

社区洞察

其他会员也浏览了