GraphQL has been mentioned more and more in recent years, and has recently been mentioned at several tech conference front-end sessions. There is a bit of mystery about something that is not easy to guess from its name. So I decided to find out what GraphQL really is.

What is a GraphQL?

First of all, GraphQL comes from Facebook, and if you’re like me, you’ve probably heard of something called Structured QL. WHAT? So that’s SQL.

  • Well, andSQLThe same,GraphQLIs a query language (Query Language)
  • And the sameSQLThe same thing,GraphQLIt’s a set of norms, likeMySQLisSQLA set of implementations of,Apollo.Relay. Is alsoGraphQLSpecification implementation
  • withSQLThe difference is,SQLThe data source for GraphQL is a database. The data source for GraphQL can be a variety of REST apis, a variety of services/microservices, or even a database

Here’s a picture from Apollo to illustrate GraphQL’s place in the Web architecture

Several points in time

  • The GraphQL specification was open-source in 2015
  • The Subscriptions operation was added to the code in 2017

Then why is it calledGraph?

Graph stands for Graph, and in GraphQL, everything is a Graph, which means you can model your business model as a Graph.

Diagrams are powerful tools for turning many real-world phenomena into models because they are similar to our natural mental models and verbal descriptions of basic processes.

This involves Graph theory, Graph Database and other knowledge. If you are interested, you can learn some songs.

This gives us a general idea of GraphQL. So let’s take a look at GraphQL in general.

What problem does GraphQL solve for us?

In short, from a front-end perspective, many articles have mentioned replacing Restful apis with a little more specificity:

  • Customization of API fields, fields on demand
  • Aggregation of APIS, all data in one request
  • The back end no longer needs to maintain the version number of the interface
  • Complete type verification mechanism, providing a more robust interface
  • .

Before we understand how these advantages are achieved, let’s take a quick look at GraphQL

The front end learns GraphQL

As a front-end developer, more often than not, there are only two operations we need to care about from a front-end perspective:

  • Query: In GraphQL, this keyword belongs to one of the schemas. It means that you want to perform a query action, i.e. add, delete, change, query

  • Mutation: indicates that the action you want to perform is add, delete, and change

Query and mutation are collectively called schemas. In fact, a SUBSCRIPTION strategy was added into the SPEC in 2017, which made it easier for us to implement the push function

Here we illustrate how these two operations are used using two scenarios of an internal company sharing platform.

Query operation

First of all, in the most basic scenario, the home page of the sharing platform needs to call an interface to obtain all the sharing list. At present, this interface is called as follows:

GET /api/share/allShares
Copy the code

return

{
    "shares": [{"shareId": 1238272."title": "Share with Vue3.0"."desc": "Vue3.0 is coming. What new features will it bring?"."where": "Conference room 6F-19"."startTime": 1548842400
        },
        {
            "shareId": 1238272."title": "What is the experience of writing app pages with Flutter?"."desc": "My first experience writing APP pages with Flutter, a cross-platform framework"."where": "Conference room 6F-17"."startTime": 1548842400
        },
        {
            "shareId": 1238272."title": "Cordova principle"."desc": "Let's learn about Cordova."."where": "Conference room 6F-19"."startTime": 1548842400}}]Copy the code

So instead of GraphQL, we could write it like this

query {
	shares {
		title
		desc
		where
		startTime
	}
}
Copy the code

Yi? If we want to skip to the details page, we need to know the id of the share

Giving a name to a query is a good habit
query findAllShares {
	shares {
		# give id an alias called shareId
		shareId: id
		title
		desc
		where
		startTime
	}
}
Copy the code

At this point, a basic query operation is complete.

paging

In general, if there is a large amount of data in a list class, we will use paging to fetch the data, rather than fetching all the data at once. For example, if we use the traditional interface call method, we will usually call the interface like this:

GET /api/share/allShares? star=0&limit=10
Copy the code

return

{
    "allShares": {
        "totalCount": 3."shares": [{"shareId": 1238272."title": "Share with Vue3.0"."desc": "Vue3.0 is coming. What new features will it bring?"."where": "Conference room 6F-19"."startTime": 1548842400
            },
            {
                "shareId": 1238273."title": "What is the experience of writing app pages with Flutter?"."desc": "My first experience writing APP pages with Flutter, a cross-platform framework"."where": "Conference room 6F-17"."startTime": 1548842400
            },
            {
                "shareId": 1238274."title": "Cordova principle"."desc": "Let's learn about Cordova."."where": "Conference room 6F-19"."startTime": 1548842400}}}]Copy the code

Let’s continue to modify GraphQL’s way of doing pagination:

# paginationquery findAllShares($start: Int! , $limit: Int =10) {
	allShares (start: $start, limit: $limit) {
		totalCount
		shares {
		    shareId: id
		    title
		    desc
		    where
	    	startTime
		}
	 }
}
Copy the code

GraphQL provides a complete solution for Pagination

Next scene, with all the shares listed, you can go to the details page. Currently, the details page has three main query interfaces: get share details, get a list of shared comments, and get a list of shares owned by the sharer. In the traditional way, we need to tune three interfaces:

// Get the details of the share
GET /api/share/:shareId
Copy the code
// Share details return
{
    "shareDetail": {
        "shareId": 1238274."title": "Cordova principle"."desc": "Let's learn about Cordova."."where": "Conference room 6F-19"."startTime": 1548842400."attchments": ""."creatorId": 321."lastUpdateTime": 1548842400."logoUrl": "". }}Copy the code
// Get a list of shared comments
GET /api/share/comments/:shareId
Copy the code
// Share comment list returns
{
    "commentInfo": {
        "totalCount": 5."comments": [{"id": 1."content": "Very good."."userId": 213."commentTime": 1548842400}, {"id": 2."content": "Very good"."userId": 214."commentTime": 1548842400}, {"id": 3."content": "Good"."userId": 216."commentTime": 1548842400}, {"id": 4."content": "Very GOOD!"."userId": 2313."commentTime": 1548842400,}]}}Copy the code
// A list of all shares created by the creator of the share
GET /api/share/shares/:creatorId
Copy the code
// Share creator's full share returns
{
    "hisShares": [{"shareId": 1238272."title": "Share with Vue3.0"."desc": "Vue3.0 is coming. What new features will it bring?"."where": "Conference room 6F-19"."startTime": 1548842400
        },
        {
            "shareId": 1238273."title": "What is the experience of writing app pages with Flutter?"."desc":     "My first experience writing APP pages with Flutter, a cross-platform framework"."where": "Conference room 6F-17"."startTime": 1548842400
        },
        {
            "shareId": 1238274."title": "Cordova principle"."desc": "Let's learn about Cordova."."where": "Conference room 6F-19"."startTime": 1548842400}}]Copy the code

How about GraphQL?

query shareDetailPage($shareId: Int! , $creatorId:ID! , $start: Int! , $limit: Int =10) {
    # Share details
    shareDetail: share (shareId: $shareId) {
        shareId: id
        title
        desc
        where
        logoUrl
        attchments
    }
    
    # Comment message
    commentInfo(shareId: $shareId, start: $start, limit: $limit) {
        totalCount
        comments {
            id
            userId
            content
            commentTime
        }
    }
    
    # TA's Share
    hisShares (creatorId: $creatorId) {
        shares {
            title
            desc
            where
            startTime
        }
    }
}
Copy the code

A query will do.

Mutation operation

Change operations, only one scenario is described here. When we get to the share details page, we may need to edit the share. In the traditional way, we need to call an interface for the update operation:

POST /api/share/update/:shareId
FormData:
title=xxx&desc=xxx&where=xxx
Copy the code

To confirm that the update has been successfully completed, we may call the share details interface again:

GET /api/share/:shareId
Copy the code

Let’s change to GraphQL:

mutation editShareInfo($shareObj: ShareInput!) {
    editShareInfo(shareInfo: $shareObj) {
    shareId: id
    title
    desc
    where
    logoUrl
    attchments
  }
}
Copy the code

In this way, you can directly modify the share content and return the modified share details

Other functions

GraphQL also provides Fragments (Fragments), Inline Fragments (Inline Fragments) and Directives for better reusability as we write the query part of the code. Caching The former two are analogously to the function and anonymous function in JavaScript. Directives determine whether certain fields need to be returned according to the parameters we pass. I won’t go into too much detail here.

How to achieve the above functions?

schema

The above example certainly raises some questions: How do we know which fields to query? What parameters are used? This is where schema comes in.

In layman’s terms, a schema is a protocol, specification, or interface document.

GraphQL specifies that each schema has a root Query and root mutation.

Let’s first look at Root Query, which is the example above

Define a root query
type Query {
    Fields and parameters that can be queried
    shares(start: Int = 0, limit: Int = 10, creatorId: ID): [Share!] ! share(shareId: ID!) : Share! commentInfo(shareId: ID! , start: Int =0, limit: Int = 10): CommentInfo!
}
Copy the code

The data type

If you’re familiar with TypeScript or Flow, this might sound familiar, and yes, it means what you think. The type of the parameter is indicated after the parameter of each field that can be queried. Used to indicate that the parameter cannot be null. [] indicates that the query returns an array. [] indicates the type of the array.

We’ve also seen some types that don’t exist in TypeScript, such as ID, which we’ll just think of as a String. Similar to JavaScrpit or TypeScript, GraphQL has several basic types. In GraphQL, they are called Scalar types, including: Int, Float, String, Boolean, and ID (unique identifier type). GraphQL also allows you to customize scalar types, such as Date, by implementing serialization, deserialization, and validation.

Object type

In the root query definition above, we also see some business-specific types, such as Share and Comment, collectively referred to as object types. The object type is also the basic component of the Schema in GraphQL. It tells us what objects are available on the service and what fields the object has. All we need to do next is define these object types until they are all base types.

Define the object type of Share
type Share {
  id: ID!
  title: String!
  desc: String!
  startTime: Int!
  where: String
  attchments: String
  logoUrl: String
  creatorId: ID!
  lastUpdateTime: Int
  is_delete: Int
  score: Int
  createTime: Int!
}

# define comment message object typestype CommentInfo { totalCount: Int! comments: [Comment!] ! }Define the comment object type
type Comment {
  id: ID!
  content: String!
  commentTime: Int!
  userId: ID!
  shareId: ID!
}

Copy the code

This completes our schema definition.

Other types and functions

GraphQL also has Enumeration types and Union types. GraphQL also provides Interface functionality for code reuse. I won’t go into too much detail here.

perform

The GraphQL convention requires a resolver function for each field in Root Query and Root Mutation. And wrapped as an object to expose, like this:

const resolvers = {
    // write the query operation field resolver function
    Query: {},
    // Write the change operation field in the resolver function
    Mutation: {},
}

export default resolvers
Copy the code

Let’s go ahead and complete:

// Some async function to load data
import { loadSharesFromDB, loadShareById, loadCommentsByShareId } from './datasource'

const resolvers = {
    // write the query operation field resolver function
    Query: {
        shares: (parent, { start, limit, creatorId }, context, info) = > {
            return loadSharesFromDB(start, limit, creatorId)
                    .then(...)
        },

        share: (parent, { shareId }, context, info) = > {
            return loadShareById(shareId)
                    .then(...)
        },

        commentInfo: (parent, { shareId, start, limit }, context, info) = > {
            returnloadCommentsByShareId(shareId, start, limit) .then(...) }},// Write the change operation field in the resolver function
    Mutation: {
         // ...}},Copy the code

For mutation operations, we do the schema first:

# define Mutation root entrytype Mutation { editShareInfo(shareInfo: ShareInput!) : Share! } input ShareInput { id: ID! title: String! desc: String! where: String }Copy the code

Then, complete the resolver function:

import { updateShareInfo, loadShareById } from './datasource'

const resolvers = {
    Query: {
        // ...
    },
    
    Mutation: {
        editShareInfo: (parent, { shareInfo }, context, info) = > {
            // Update the share details, then get the updated share details
            return updateShareInfo(shareInfo.id, shareInfo)
                    .then(loadShareById(shareInfo.id))
        },  
    },
}

export default resolvers
Copy the code

At this point, we have implemented the simple GraphQL Server.

conclusion

There’s a lot more to explore and use in GraphQL. For example, if you use a Schema builder to generate object types, you can mark a field as obsolete and give a reason why. In this way, during version iteration, users of the old version can be friendly prompted to upgrade to the latest interface. Through some detection methods, we can easily know the frequency of the use of the old version, so that we can conveniently delete this field at a certain time.

If there’s a downside to GraphQL, it’s that it’s really not that easy to get started with, and there are still a lot of pitfalls for those on the back end, such as caching and performance issues. Fortunately, GraphQL is not as scarce as it was a few years ago, and there are more and more resources and solutions available to GraphQL, both officially and in the community.

Anyway, purely for front-end ER, if the last technological revolution of front-end was the popularization of SPA, I believe that when the next revolution comes, there will be the shadow of GraphQL.

Some links
  • GraphQL Tutorials
  • GraphQL Chinese tutorial
  • Why isn’t GraphQL popular on Zhihu?
  • graphpackA 0 configurationGraphQL serverTools, very suitable for beginners to understandGraphQL, and provides a Codepen way to edit code online, on which the examples in this article are based