普通的Query 是去查询数据库,而GraphQL 是去查询API, 这个可以给予开发他们所需的所有数据,不多不少
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符合规范,服务器会返回您请求的那几栏数据,其他的多余的数据,将不会传送,这个极大的提高了数据利用效率。
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 一定程度上解决了这个问题。
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 可以让小步快跑这件事做的更加彻底。
缓存是一种可以让用户获取常用数据的机制。用Restful API的时候,数据可以以URL为单位进行缓存,效率还是挺好的,而以点或者栏的颗粒度进行缓存,难度就大的多了。
当你仅仅需要一个大表中的一部分,特别是一小部分时,用GraphQL 可能还是一个不错的主意, 但是如果你需要的是全部信息,这条信息里面包含成百上千条数据,那么这种请求就会极其费时,性能问题就会很突出。
对于复杂的query 或者说返回值很大的情况,RESTFUL API可能是个更好的选择。
需要一个GraphQL 的层级来动态管理这些服务,其中最核心的就是配置这个schema
当我们进行读数据操作时,需要执行的语句如下:
{
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的形式在了另一个文件夹中。在真正的应用中,这些方程可以变成数据库的查询语句,或者其他服务的调用。
当我们进行写操作的时候,进行操作的语句如下:
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
});
同样的为了简化复杂性,我们的数据存在了隔壁的文件夹之中;数据库的增删改查变成了隔壁文件的删改。
实现的原理和JS端一样,但是由于Java是一个强类型语言,我们需要把相应的类型定义成POJO class。
如果我们的GraphQL 语句 如下:
type Link {
url: String!
description: String!
}
type Query {
allLinks: [Link]
}
schema {
query: Query
}
那么我们需要定义Link
和 Query
两个 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
}
}