详解 GraphQL
刘俊峰
2017-12-01
GraphQL 是什么?
GraphQL 是一种适用于 API 的数据查询语言。
仅仅是一种规范,可以有各种前后端的实现方案。
Facebook 于 2012 年开发,2015 年开源。
GraphQL 的优点
- 一次查询就能获取视图所需的全部数据
- 要什么给什么,没有冗余数据
- 自动生成数据格式的描述文档
- 强类型,自动校验参数类型
- 灵活,一个接口适用多个场景,前后端解耦
- API 可持续进化,不受版本号限制
- 大量的工具生态系统
Architecture
支持多种操作:query 是查询,mutation 是修改
Architecture
GraphQL server with a connected database
Architecture
GraphQL layer that integrates existing systems
GraphQL 与 REST 的对比
GraphQL 与 REST 的对比
GraphQL Sepecification
-
GraphQL schema language
定义类型系统,类似于 SQL create table
但 GraphQL 有层次结构,而不是表格 - GraphQL query language
创建具体的查询,类似于 SQL select 语句
GraphQL schema language
Every GraphQL service has a query type and may or may not have a mutation type.
schema {
query: Query
mutation: Mutation
}
type Query {
hero(episode: Episode): Character
droid(id: ID!): Droid
}
GraphQL schema language
type Character {
name: String!
appearsIn: [Episode]!
}
type Starship {
id: ID!
name: String!
length(unit: LengthUnit = METER): Float
}
Every GraphQL service defines a set of types which completely describe the set of possible data you can query on that service.
GraphQL schema language
-
Scalar types
- Int: A signed 32‐bit integer.
- Float: A signed double-precision floating-point value.
- String: A UTF‐8 character sequence.
- Boolean: true or false.
- ID: A unique identifier.
- Custom scalars, e.g. scalar Date.
GraphQL schema language
-
Enumeration types
-
Union types
-
Lists
-
Non-Null
-
Interfaces
-
Input types
enum Episode {
NEWHOPE
EMPIRE
JEDI
}
union SearchResult = Human | Droid | Starship
interface Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
}
input ReviewInput {
stars: Int!
commentary: String
}
GraphQL query language
-
Fields
-
Arguments
-
Aliases
-
Fragments
-
Variables
-
Directives
-
Mutations
{
hero(id: "1000") {
name
height
# Queries can have comments!
friends {
name
}
}
empireHero: hero(episode: EMPIRE) {
name
}
jediHero: hero(episode: JEDI) {
name
}
}
Query Execution Steps
While query fields are executed in parallel, mutation fields run in series, one after the other.
Returned result mirrors the shape of the requested query, typically as JSON.
GraphQL server does this automatically, developer only need to write resolvers.
- Parse
- Validate
- Execute
Client side solution
yarn add apollo-client apollo-cache-inmemory apollo-link-http react-apollo graphql-tag graphql
// App.jsx
import { ApolloProvider } from 'react-apollo';
import { ApolloClient } from 'apollo-client';
import { HttpLink } from 'apollo-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';
const client = new ApolloClient({
// By default, this client will send queries to the
// `/graphql` endpoint on the same host
// Pass the configuration option { uri: YOUR_GRAPHQL_API_URL } to the `HttpLink` to connect
// to a different host
link: new HttpLink(),
cache: new InMemoryCache()
});
ReactDOM.render(
<ApolloProvider client={client}>
<MyRootComponent/>
</ApolloProvider>,
document.getElementById('root'),
);
// TodoApp.jsx
import { graphql } from 'react-apollo';
import gql from 'graphql-tag';
export default graphql(gql`
query TodoAppQuery {
todos {
id
text
}
}
`)(TodoApp);
function TodoApp({ data: { todos, refetch } }) {
return (
<div>
<button onClick={() => refetch()}>
Refresh
</button>
<ul>
{todos && todos.map(todo => (
<li key={todo.id}>
{todo.text}
</li>
))}
</ul>
</div>
);
}
Text
Server side solution
Server side solution
import { makeExecutableSchema } from 'graphql-tools';
const typeDefs = `
type Author {
id: Int
firstName: String
lastName: String
posts: [Post]
}
type Post {
id: Int
title: String
text: String
views: Int
author: Author
}
type Query {
author(firstName: String, lastName: String): Author
getFortuneCookie: String
}
`;
const resolvers = {
Query: {
author(root, args){
return { id: 1, firstName: 'Hello', lastName: 'World' };
},
},
Author: {
posts(author){
return [
{ id: 1, title: 'A post', text: 'Some text', views: 2},
{ id: 2, title: 'Another post', text: 'Some other text', views: 200}
];
},
},
Post: {
author(post){
return { id: 1, firstName: 'Hello', lastName: 'World' };
},
},
};
export default makeExecutableSchema({ typeDefs, resolvers });
// routes.js
const database = require('./database');
const schema = require('./graphql/schema');
const graphql = new Router();
graphql.post('/graphql',graphqlKoa({ schema, context: {database} }));
graphql.get('/graphiql', graphiqlKoa({ endpointURL: '/graphql' }));
GraphiQL
An in-browser IDE for exploring GraphQL
参考资料
- Graphql 入门
- GraphQL explained
- Why Facebook’s GraphQL Language Should Be on Your Radar
- Introduction to GraphQL – A Query Language for APIs
- The Fullstack Tutorial for GraphQL
- GraphQL 与 GitHub - 开发属于你的 GitHub 应用
- 如何为老板节省几个亿之 graphql 介绍
- GraphQL 技术栈揭秘
- Apollo GraphQL
- Vulcan.js - The full-stack React+GraphQL framework
详解 GraphQL
By Liu Junfeng
详解 GraphQL
- 821