GraphQL的代码实践

1. Good and Bad on GraphQL

普通的Query 是去查询数据库,而GraphQL 是去查询API, 这个可以给予开发他们所需的所有数据,不多不少

i. Good side of GraphQL

a. 精准抓取数据

GraphQL minimizes the amount of data which is transferred across the wire by being selective about the data depending on the client application’s needs. Thus, a mobile client can fetch less information, because it may not be needed on a small screen compared to the larger screen for the web application.

传统的Restful 请求是按着endpoint分的,每一个endpoint的请求可成功可失败,但是如果成功,返回的结果是相同的;

GraphQL 只有一个endpoint,你可以想这个endpoint发送相应的Query, 只要Query符合规范,服务器会返回您请求的那几栏数据,其他的多余的数据,将不会传送,这个极大的提高了数据利用效率。

2.兼容性

GraphQL embraces these new trends as it can be used to connect the backend application and fulfill each client’s requirements ( nested relationships of data, fetching only the required data, network usage requirements, etc.) without dedicating a separate API for each client.

对于当代的后端开发,常常一个后台API需要被Web 前端,iOS 和安卓等多个客户端调用,而每个客户端需要的数据其实是不同的,对于移动端,不同尺寸的手机,我们显示的信息也是不同的,这就造成了很多时候信息的冗余。

冗余的信息一方面占用了不必要的内存,另一方面加长了响应返回的时间,降低了用户体验,GraphQL 一定程度上解决了这个问题。

3. 更小颗粒度的迭代

In GraphQL, it is possible to deprecate API’s on a field level. When a particular field is to be deprecated, a client receives a deprecation warning when querying the field. After a while, the deprecated field may be removed from the schema when not many clients are using it anymore.

在互联网行业,迭代周期很重要,小步快跑的互联网行业普遍适用的开发流程,而Graph QL 可以让小步快跑这件事做的更加彻底。

ii. Bad side of GraphQL

1. 缓存

缓存是一种可以让用户获取常用数据的机制。用Restful API的时候,数据可以以URL为单位进行缓存,效率还是挺好的,而以点或者栏的颗粒度进行缓存,难度就大的多了。

2. 性能问题

当你仅仅需要一个大表中的一部分,特别是一小部分时,用GraphQL 可能还是一个不错的主意, 但是如果你需要的是全部信息,这条信息里面包含成百上千条数据,那么这种请求就会极其费时,性能问题就会很突出。

对于复杂的query 或者说返回值很大的情况,RESTFUL API可能是个更好的选择。

2. JS 端代码实现

需要一个GraphQL 的层级来动态管理这些服务,其中最核心的就是配置这个schema

i. 数据读取操作

当我们进行读数据操作时,需要执行的语句如下:

{
  posts{
    id
    title
    author{
      name
    }
  }
}

此处我们用了一个express 和express-graphql 来搭建中间层

const app = express();
app.use('/', graphqlHTTP({
  schema: schema,
  graphiql: true //set to false if you don't want graphiql enabled
}));

而schema的配置方法如下:

const AuthorType = new GraphQLObjectType({
  name: "Author",
  description: "This represent an author",
  fields: () => ({
    id: {type: new GraphQLNonNull(GraphQLString)},
    name: {type: new GraphQLNonNull(GraphQLString)},
    twitterHandle: {type: GraphQLString}
  })
});

const PostType = new GraphQLObjectType({
  name: "Post",
  description: "This represent a Post",
  fields: () => ({
    id: {type: new GraphQLNonNull(GraphQLString)},
    title: {type: new GraphQLNonNull(GraphQLString)},
    body: {type: GraphQLString},
    author: {
      type: AuthorType,
      resolve: function(post) {
        return _.find(Authors, a => a.id == post.author_id);
      }
    }
  })
});

// This is the Root Query
const BlogQueryRootType = new GraphQLObjectType({
  name: 'BlogAppSchema',
  description: "Blog Application Schema Query Root",
  fields: () => ({
    authors: {
      type: new GraphQLList(AuthorType),
      description: "List of all Authors",
      resolve: function() {
        return Authors
      }
    },
    posts: {
      type: new GraphQLList(PostType),
      description: "List of all Posts",
      resolve: function() {
        return Posts
      }
    }
  })
});

// This is the schema declaration
const BlogAppSchema = new GraphQLSchema({
  query: BlogQueryRootType 
  // If you need to create or updata a datasource, 
  // you use mutations. Note:
  // mutations will not be explored in this post.
  // mutation: BlogMutationRootType 
});

PostType 中,我们定义了如何取数据,在以上的代码中,我们为了简化代码,所有的数据都是以JSON的形式在了另一个文件夹中。在真正的应用中,这些方程可以变成数据库的查询语句,或者其他服务的调用。

ii. 数据写,更新和删除操作

当我们进行写操作的时候,进行操作的语句如下:

mutation createNewQuote {
  createQuote(input: {quote: "When Charles turned 18, his parents sent him aboard."}) {
    id
    quote
  }
}

此处我们还是用了一个express 和express-graphql 来搭建中间层

const app = express();
app.use('/', graphqlHTTP({
  schema: schema,
  graphiql: true 
}));

以下便是基于graphql 提供的接口来配置schema了,整个流程还是很标准化的

const QuoteType = new GraphQLObjectType({
  name: 'QuoteType',
  description: 'Chuck Norris Quotes',
  fields: {
    id: { type: GraphQLString },
    quote: { type: GraphQLString }
  }
});
const QuoteCreateType = new GraphQLInputObjectType({
  name: 'QuoteCreateType',
  type: QuoteType,
  fields: {
    quote: { type: new GraphQLNonNull(GraphQLString) }
  }
});

const QuoteUpdateType = new GraphQLInputObjectType({
  name: 'QuoteUpdateType',
  type: QuoteType,
  fields: {
    id: { type: new GraphQLNonNull(GraphQLString) },
    quote: { type: new GraphQLNonNull(GraphQLString) }
  }
});

const QuoteDeleteType = new GraphQLInputObjectType({
  name: 'QuoteDeleteType',
  type: QuoteType,
  fields: {
    id: { type: new GraphQLNonNull(GraphQLString) }
  }
});

const ChuckNorrisQueryType = new GraphQLObjectType({
  name: 'ChuckNorrisQuerytype',
  description: 'Chuck Norris Query Schema',
  fields: {
    quotes: {
      type: new GraphQLList(QuoteType),
      resolve: () => Quotes 
    }
  }
});

const ChuckNorrisMutationType = new GraphQLObjectType({
  name: 'ChuckNorrisMutationType',
  description: 'Chuck Norris Query Schema',
  fields: {
    createQuote: {
      type: QuoteType,
      args: {
        input: { type: new GraphQLNonNull(QuoteCreateType) }
      },
      resolve: (source, { input }) => {
        let quoteData = [];
        quoteData.id = uuidv4();
        quoteData.quote = input.quote;

        Quotes.push(quoteData);
        return quoteData;
      }
    },
    updateQuote: {
      type: QuoteType,
      args: {
        input: { type: new GraphQLNonNull(QuoteUpdateType) }
      },
      resolve: (source, {input}) => {
        let quoteData = [];
        quoteData.id = input.id;
        quoteData.quote = input.quote;

        let index = Quotes.findIndex(q => q.id == input.id);
        let update = Quotes.splice(index, 1, quoteData);

        return quoteData; 
      }
    },
    deleteQuote: {
      type: QuoteType,
      args: {
        input: { type: new GraphQLNonNull(QuoteDeleteType) }
      },
      resolve: (source, {input}) => {
        let deleteQuote = _.remove(Quotes, q => q.id == input.id)
        return input;
      }
    }
  }
});

const ChockNorrisSchema = new GraphQLSchema({
  query: ChuckNorrisQueryType,
  mutation: ChuckNorrisMutationType
});

同样的为了简化复杂性,我们的数据存在了隔壁的文件夹之中;数据库的增删改查变成了隔壁文件的删改。

3. Java端的代码实现

实现的原理和JS端一样,但是由于Java是一个强类型语言,我们需要把相应的类型定义成POJO class。

如果我们的GraphQL 语句 如下:

type Link {
  url: String!
  description: String!
}

type Query {
  allLinks: [Link]
}

schema {
  query: Query
}

那么我们需要定义LinkQuery 两个 POJO Class

public class Link {
    
    private final String url;
    private final String description;

    public Link(String url, String description) {
        this.url = url;
        this.description = description;
    }

    public String getUrl() {
        return url;
    }

    public String getDescription() {
        return description;
    }
}
public class Query implements GraphQLRootResolver {
    
    private final LinkRepository linkRepository;

    public Query(LinkRepository linkRepository) {
        this.linkRepository = linkRepository;
    }

    public List<Link> allLinks() {
        return linkRepository.getAllLinks();
    }
}

在之后我们需要创建LinkRepository 来对Link 进行读写操作

public class LinkRepository {
    
    private final List<Link> links;

    public LinkRepository() {
        links = new ArrayList<>();
        //add some links to start off with
        links.add(new Link("http://howtographql.com", "Your favorite GraphQL page"));
        links.add(new Link("http://graphql.org/learn/", "The official docks"));
    }

    public List<Link> getAllLinks() {
        return links;
    }
    
    public void saveLink(Link link) {
        links.add(link);
    }
}

最后我们更新一下GraphQLEndPoint, 把Schema 挂载上去,同时引入我们刚刚写好的LinkRepository Class

@WebServlet(urlPatterns = "/graphql")
public class GraphQLEndpoint extends SimpleGraphQLServlet {

    public GraphQLEndpoint() {
        super(buildSchema());
    }

    private static GraphQLSchema buildSchema() {
        LinkRepository linkRepository = new LinkRepository();
        return SchemaParser.newParser()
                .file("schema.graphqls")
                .resolvers(new Query(linkRepository))
                .build()
                .makeExecutableSchema();
    }
}

验证的话,我们只要输入以下的Query即可,你应该能看见整齐的返回。

{
	allLinks {
		url
	}
}

你可能感兴趣的:(前端技术,Java,Graph,QL,实践)