background

This year, I am working on an app about merchants, which is a client software for offline promoters to use, including the complete life cycle of merchants from Internet access to audit, rejection and submission to Internet access maintenance. But the story is not so simple…

doubt

With the gradual improvement of app, more and more problems have been encountered, such as too long interface loading, too many times of initialization page requests and other minor problems have been highlighted. So I started the optimized path, the first step is to start from the API request, take a closer look at the contents of each API to return and have been wondering why interface always return back a lot of data, such as the detailed information I need to have a merchant, but the interface to the merchant stores information, all information related to all kinds of other information together back to back, For example, the merchant details page is fine, but the merchant list interface still returns so much data, you can imagine how much more complex the list.

Later after asked the students learned to compatible with the demand on the web, an interface to provide content for multiple platforms, which greatly increased the content and processing logic, the return of the interface and the demand is constantly changing, so it is better to take out all the fields can be used to output, so that every time before and after the change of demand are linked together.

reflection

Knowing the result, this is indeed a “good” reason to deal with the result, but really can only be so? Is there a better solution? Let’s start with a summary of the current problems:

  • Compatibility with multiple platforms leads to field redundancy
  • A page requires multiple calls to the API to aggregate data
  • Frequently changing requirements make it difficult to simplify logic for a single interface

The above three problems do not seem complicated, but they can be easily solved according to the previous logic. Take the first one for example, when multiple platforms need compatibility, interfaces of different platforms can be provided, for example:

http://api.xxx.com/web/getUserInfo/:uid
http://api.xxx.com/app/getUserInfo/:uid
http://api.xxx.com/mobile/getUserInfo/:uidCopy the code

Or it can be controlled by different parameters:

http://api.xxx.com/getUserInfo/:uid? platfrom=webCopy the code

While this is a convenient solution, it actually leads to an increase in back-end logic, requiring different logic code to be maintained for different platforms.

The second problem is that a page requires multiple calls to the interface to aggregate data, which can also be solved by adding more interfaces:

http://api.xxx.com/getIndexInfoCopy the code

Or reuse requests over HTTP2, but these methods either add work or have compatibility issues. What else?

If you remember about databases, you can actually use SQL to do these things, but what if you abstracted the back end into a database?

I can SELECT any field I want. If a page needs multiple data sources to fill it, it’s just a combination of SQL statements. It’s not solved the above three questions, and whatever the front-end needs change, as long as we maintain a superset of the data, so every time just let the front changes the query, the back-end here also don’t need to be synchronized to add fields to an interface or something, then the solution had, that how to put the back-end abstract into a database?

To solve

This is GraphQL that Facebook opened source in 2015

What is this thing? Specific introduction directly to see its official website is good, I will not say here, directly to see how to use it.

Since my middle layer is based on Koa2, I will demonstrate it on Koa2.

npm install graphql koa-graphql --saveCopy the code

Now we can use graphQL in koA, and then we can configure the routing. Following the example above, we can write:

"use strict";
const router = require('koa-router') ();const graphqlHTTP = require('koa-graphql');
const GraphQLSchema = require('./graphql');
const renderGraphiQL = require('.. /utils/render_graphiQL');
const graphqlModule = graphqlHTTP((request) => ({
    schema: GraphQLSchema,
    graphiql: false,
    context: {token: request.header.authorization, platform: request.query.platform},
    formatError: error => ({
        type: 'graphql',
        path: error.path,
        message: error.message,
        locations: error.locations ? error.locations[0] : null})})); router.all('/graphql', graphqlModule);Copy the code

Let’s take a look at the graphqlModule object and see what it contains. The first is the schema, which is our main parsing logic. All requests through graphqlModule are passed in here for parsing and processing. Context is our context. If we need to get a user token in each function, we can assign it to it. The requirement is that this must be an Object object, and the entire Request object will be passed by default if we do not specify it. The last commonly used attribute is formatError, which is the property for formatting errors. We can customize our error returns to suit our business needs.

Let’s start with the simplest Hello World and finish a basic demo.

First, we initiated a POST request on the client side, and then put our query statement in the request body. There are two types of queries in Graphql, which are always query operations starting with Query and mutation modification operations.

query {hello}Copy the code

This is the simplest query, so how does this query get parsed? All Graphql queries are parsed with a Schema, so let’s see what the GraphQLSchema object defined above is.

module.exports = new GraphQLSchema({
    query: new GraphQLObjectType({
        name: 'rootQueryType'.        description: 'Query operation'.        fields: {
            hello: {
                type: GraphQLString,
                description: 'demo demo'.                args: {
                    name: {type: GraphQLString, description: 'Demo parameters'} }, resolve(it, args, context) { return args.name; }}}}),    mutation: new GraphQLObjectType({
        name: 'rootMutationType'.        description: 'Add or modify operation'.        fields:{}})});Copy the code

Let’s take a step by step. First we export a GraphQLSchema object in this file, which is the base of Graphql and contains the two types we need. Then we look at the Query property, which returns a GraphQLObjectType object. This is the basic type for object in Graphql. This object contains name: name (globally unique), description: The description (which is automatically displayed in the documentation, although it’s not required, but I strongly recommend that every Graphql node write it, which is useful for later maintenance and queries), and finally the fields property, which tells you what a Graphql statement says. In the first example, we query query{hello}, which means that we need to check the root node’s Hello property, so we need to write hello property in the query fields, otherwise the query will not work.

Now what does this Hello property contain? The first thing we need to do is specify its type, that’s really important, it’s the return type of Hello, and in this case I’m specifying that it’s going to return a string, and then there are other basic js types like Int, Boolean, and so on, you can look at the documentation, and of course, You can also return GraphQLObjectType in complicated cases, and then description for Hello, and then the args property, if we need to pass in parameters to this query, Then there is the key resolve function, which takes three arguments. The first is the return value, which is often used in nested loops. For example, if Hello has a subattribute, the subattribute will be args.name, and the second will be the query attribute. The third is the context that we started with throughout the request. Here is a complete example:

Request: query{hello(name: "world")}

Response: {"hello": "world"}Copy the code

The mutation operation is similar to the query operation, so I won’t expand on it.

conclusion

Finally, to summarize the solution, is this the best solution? May not, or to the specific business scenarios, in my relationship with all kinds of data in the scene is clear, or can the abstract into the model, I think this is the key point of using Graphql, from the example above we in fact can be found through Graphql we put up every data specification, specifies the type, The nested relationship is determined, which is quite out of place in the javascript-based Node environment. JavaScript itself is weakly typed, so we can modify the data flexibly in the Node service without caring about the return values and parameter values. However, Graphql uses a strongly typed concept to force us to design each data, which may be difficult for some front-end students to accept, but I personally think this kind of idea is actually very reasonable. Moreover, Graphql also supports nested queries. All you need is the object in the fields property. Therefore, we can abstract and separate each data type as much as possible. For example, the role of store manager is the combination of user object and merchant object, which not only makes the logic clear from the relationship, but also facilitates more possible combination conditions.

Graphql seems to have solved my problem perfectly for the first few problems I encountered, but what about more complex scenarios? Or is it cost-effective to retrofit an old project? Although I haven’t encountered it, I think it will eventually work as long as I comb through the data structure carefully, but is Graphql still needed at that time? I don’t know, this blog is not a Chinese document to introduce how to use Graphql. What I want to express is that this idea is a solution to this situation. Now it only solves these problems for me. It’s still so new that in a few years it might be as standard an API as restful, after all GitHub released their Graphql API this year.

Category: Front-end story
Good writing is the best
Pay attention to my
Collect the paper

F-happy



Attention – 4



Fans – 62.

+ add attention

2
0

The «The last:
My front-end story —- roulette goes up (+﹏+)~