淺淺地談 GraphQL

栏目: 前端 · 发布时间: 6年前

内容简介:GraphQL is a new API standard that provides a more efficient, powerful and flexible alternative to REST. It was developed and open-sourced by Facebook and is now maintained by a large community of companies and individuals from all over the world. At its
GraphQL - A Query Language for APIs

GraphQL is a new API standard that provides a more efficient, powerful and flexible alternative to REST. It was developed and open-sourced by Facebook and is now maintained by a large community of companies and individuals from all over the world. At its core, GraphQL enables declarative data fetching where a client can specify exactly what data it needs from an API. Instead of multiple endpoints that return fixed data structures, a GraphQL server only exposes a single endpoint and responds with precisely the data a client asked for.

这是官方关于 GraphQL 的介绍,浓缩成一句话解释: an API defines how a client can load data from a server

大概可以达到这些优点:

① It lets the client specify exactly what data it needs. / 由前端决定需要哪些资料 ② It uses a type system to describe data. / 资料是具有严谨的型态规范 ③ It makes it easier to aggregate data from multiple sources. / 只需要单一的 API 端口

GraphQL is the better REST

跟传统的 REST 的相比,可以用这张图解释:

淺淺地談 GraphQL

根据 REST 的做法,我们依照每一个 Resource 为单位(通常 depend on 资料库的表格)。也就是说,前端需要一个资源时即需要呼叫一次 API,N 个资源时需要呼叫 N 次。可能会隐性的增加 Request 与增加新资源的开发成本。 RESTFul 分为几个阶段:Request → URL Route → Handle Controller -> Multiple Endpoint

在 GraphQL 的架构中将 URL Route → Handle Controller 的动作直接压缩设计在 API 中透过 Schema 跟 Resolver 取代,将 Resources 视为是一个 Graph,仅需要透过 One Endpoint API 就可以做到对资料存取的操作。像这样使用:

淺淺地談 GraphQL

为什么要用 GraphQL?

目前应用程式的发展是很快速的,资料的定义也很难在一开始就设定得很完美,很常都是前、后端同时开发同时串接。而因为 API 的兴起,一个后端通常需要应付很多前端平台,像是网页前、后台、Android、iOS app 之类的。多个呈现画面的前端平台仰赖于一个后端与一个资料库的来源,RESTFul 其实是一个相对舒适的被动解法。反正后端就是开好在那边,请大家依照文件各取所需就好。但是随着开发时程拉长、规模变大之后,会发现不同的前端可能会有不同的要求,此时后端可能就需要一个每个来源进行调整或是吵架 (?)

GraphQL 可以优雅地处理这个问题,大概有以下几点好处:

① No more Over and Less - Overfetching and Underfetching / 前端需要什么自己决定,可以避免 Response 太肥或不足的问题 ② Benefits of a Schema & Type System / 资料类型的定义可以让达到初步的验证效果 ③ Rich open-source ecosystem and an amazing community / 开源的社群资料很多,也有许多有趣的配套解法可以混搭

Thinking in graphs

前面有提到, GraphQL 将资源视为一个 Graph,这边的资源可以想成是资料库中的每一种资料。

淺淺地談 GraphQL

怎么开始、怎么使用

GraphQL 由几个部分所组成:

  • TypeDefs
  • Resolvers
  • Queries (Query、Mutation)
  • Schema

=> API contain Schema contain Queries contain TypeDefs and Resolvers

TypeDefs

资料可以定义成下面这样子,Query 跟 Mutation 作为 Graph 的 Root,其中在包含 Resource 的组合:

type Query {
  person: person!
  persons: [person]
}
type Mutation {
  create(name: String!, age: Int!): String
}

type Person {
  name: String!
  age: Int!
}
复制代码

每一种资料都可以设置属性与类型,可以当作初步的验证!

Resolvers

可以想成一个 Function,负责解析怎样的需求该对应怎样的操作。

Queries (Query、Mutation)

Queries 分为 Query、Mutation 两种,定义前端的 API 要如何跟后端沟通。

  • Fetching Data with Queries
query {
  Person(last: 1) {
    name
  },
  Persons {
    id
    name
  }
}

# Return:
# {
# "data": {
# "Person": {
# name: "Sarah"
# },
# "Persons": [{
# "id": 1, "name": "Johnny"
# }, {
# "id": 2, "name": "Sarah"
# }]
# }
# }

复制代码
  • Writing Data with Mutations
mutation {
  create(name: "Bob", age: 36) {
    id
  }
}

# Return:
# {
# "data": {
# "create": {
# "id": 3
# }
# }
# }
复制代码

Schema

Schema 定义 API 的抽象层,将最上层的 Query Root 往下视为一个 API Graph。

简单总结如下:

  • TypeDefs ➞ 定义资料 & Query 的型态
  • Resolvers ➞ 定义操作资料的方式
  • Queries (Query、Mutation) ➞ 资料操作的最上层
  • Schema ➞ 定义 API 的抽象层

实践第一个 GraphQL Server 开始

目前常见的 GraphQL API Server 大概有以下几种做法与分别来自不同的套件:

graphql
graphql
graphql-tools
apollo-server
graphql-yoga

这边都是以 Node + Express 为范例,也先将资料来源假设是静态变数,实务应用的时候可以把 Resolver 改为直接存取资料库的 Function。

第一种写法:GraphQLObjectType + GraphQLSchema

// 1. 先定义资料来源 & 操作资料的 resolvers
let users = [
  { id: 0, name: 'Tom', sex: 0, },
  { id: 1, name: 'Bob', sex: 0, },
  { id: 2, name: 'Alick', sex: 1, },
];

const userResolver = ({id}) => users.filter(u => u.id == id)[0];
const usersResolver = () => users;

// 2. 利用 GraphQLObjectType 定义 data 的 TypeDefs

const userType = new graphql.GraphQLObjectType({
  name: 'user',
  fields: {
    id: { type: graphql.GraphQLInt },
    name: { type: graphql.GraphQLString },
    sex: { type: graphql.GraphQLInt },
  }
})
const usersType = new graphql.GraphQLList(userType)

// 3. 再利用 GraphQLObjectType 定义 Query 的 TypeDefs 且将 resolvers 定义在其中

const queryType = new graphql.GraphQLObjectType({
  name: 'Query',
  fields: {
    user: {
      type: userType,
      resolve: (_, args) => userResolver(args),
      args: {
        id: { type: graphql.GraphQLInt }
      },
    },
    users: {
      type: usersType,
      resolve: () => usersResolver()
    }
  }
})

// 4. 利用 GraphQLSchema 将 Query 封装成 Schema

const schema = new graphql.GraphQLSchema({
  query: queryType
});
// 5. 利用 graphqlHTTP 将 Schema 封装成 API

app.use(
  '/',
  graphqlHTTP({
    schema: schema,
    graphiql: true
  })
);
复制代码

第二种写法:buildSchema + typeDefs + rootValue

// 1. 先定义资料来源 & 操作资料的 resolvers
let users = [
  { id: 0, name: 'Tom', sex: 0, },
  { id: 1, name: 'Bob', sex: 0, },
  { id: 2, name: 'Alick', sex: 1, },
];
const userResolver = ({id}) => users.filter(u => u.id == id)[0];
const usersResolver = () => users;

const resolvers = {
  Query: {
    user: (_, args) => userResolver(args),
    users: () => usersResolver()
  },
}

// 2. 利用 template string 定义 data 跟 Query 的 TypeDefs

const typeDefs = `
    type Query {
        user(id: Int!): User
        users: [User]
    },
    type User {
        id: Int
        name: String
        sex: Int
    }
`;

// 3. 利用 buildSchema 将 Query 封装成 Schema

const schema = graphql.buildSchema(typeDefs);

// 4. 利用 graphqlHTTP + rootValue 将 Query 与 resolver 串接且封装成 API

app.use(
  '/',
  graphqlHTTP({
    schema: schema,
    rootValue: {
      user: userResolver,
      users: usersResolver
    },
    graphiql: true
  })
);
复制代码

第三种写法:makeExecutableSchema + typeDefs + resolvers

// 1. 先定义资料来源 & 操作资料的 resolvers
let users = [
  { id: 0, name: 'Tom', sex: 0, },
  { id: 1, name: 'Bob', sex: 0, },
  { id: 2, name: 'Alick', sex: 1, },
];
const userResolver = ({id}) => users.filter(u => u.id == id)[0];
const usersResolver = () => users;

const resolvers = {
  Query: {
    user: (_, args) => getCourse(args),
    users: () => getCourses()
  },
}

// 2. 利用 template string 定义 data 跟 Query 的 TypeDefs

const typeDefs = `
    type Query {
        user(id: Int!): User
        users: [User]
    },
    type User {
        id: Int
        name: String
        sex: Int
    }
`;

// 3. 利用 makeExecutableSchema 将 Query 跟 resolver 封装成 Schema

const schema = makeExecutableSchema({
  typeDefs,
  resolvers,
});

// 4. 利用 graphqlHTTP 将 Schema 封装成 API

app.use(
  '/',
  graphqlHTTP({
    schema: schema,
    graphiql: true
  })
);
复制代码

第四种写法:ApolloServer + typeDefs + resolvers

// 1. 先定义资料来源 & 操作资料的 resolvers
let users = [
  { id: 0, name: 'Tom', sex: 0, },
  { id: 1, name: 'Bob', sex: 0, },
  { id: 2, name: 'Alick', sex: 1, },
];
const userResolver = ({id}) => users.filter(u => u.id == id)[0];
const usersResolver = () => users;

const resolvers = {
  Query: {
    course: (_, args) => getCourse(args),
    courses: () => getCourses()
  },
}
// 2. 利用 template string 定义 data 跟 Query 的 TypeDefs

const typeDefs =gql`
    type Query {
        user(id: Int!): User
        users: [User]
    },
    type User {
        id: Int
        name: String
        sex: Int
    }
`;

// 3. 利用 ApolloServer 将 Query 跟 resolver 封装成 Schema
const server = new ApolloServer({
  typeDefs,
  resolvers,
});

// 4. 利用 applyMiddleware 将 server 作为 express 的 Middleware API

const app = express();
server.applyMiddleware({ app });
复制代码

第五种写法:GraphQLServer + typeDefs + resolvers

// 1. 先定义资料来源 & 操作资料的 resolvers
let users = [
  { id: 0, name: 'Tom', sex: 0, },
  { id: 1, name: 'Bob', sex: 0, },
  { id: 2, name: 'Alick', sex: 1, },
];
const userResolver = ({id}) => users.filter(u => u.id == id)[0];
const usersResolver = () => users;

const resolvers = {
  Query: {
    course: (_, args) => getCourse(args),
    courses: () => getCourses()
  },
}

// 2. 利用 template string 定义 data 跟 Query 的 TypeDefs

const typeDefs = `
    type Query {
        user(id: Int!): User
        users: [User]
    },
    type User {
        id: Int
        name: String
        sex: Int
    }
`;

// 3. 利用 makeExecutableSchema 将 Query 跟 resolver 封装成 Schema API

const server = new GraphQLServer({
  typeDefs,
  resolvers,
})

server.start()
复制代码

架构与实现

官方将常见的使用案例分为三种,也提出三种可以导入的方式:

① GraphQL server with a connected database ② GraphQL layer that integrates existing systems ③ Hybrid approach with connected database and integration of existing system

GraphQL 也可以分为前端与后端:

① A GraphQL server that serves your API. => 套件有: Apollo Client、Relay ② A GraphQL client that connects to your endpoint. => 套件有: express-graphql、 apollo-server、 graphql-yoga

除了前后端之外,也可以加一些额外的工具:

  • 前端后端间也可以有一个 Cache Gateway: Apollo Engine
  • 后端与资料库间也可以用一个 ORM Layer:Prisma、join-monster
淺淺地談 GraphQL

以上所述就是小编给大家介绍的《淺淺地談 GraphQL》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

算法图解

算法图解

[美] Aditya Bhargava / 袁国忠 / 人民邮电出版社 / 2017-3 / 49.00元

本书示例丰富,图文并茂,以让人容易理解的方式阐释了算法,旨在帮助程序员在日常项目中更好地发挥算法的能量。书中的前三章将帮助你打下基础,带你学习二分查找、大O表示法、两种基本的数据结构以及递归等。余下的篇幅将主要介绍应用广泛的算法,具体内容包括:面对具体问题时的解决技巧,比如,何时采用贪婪算法或动态规划;散列表的应用;图算法;K最近邻算法。一起来看看 《算法图解》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

URL 编码/解码
URL 编码/解码

URL 编码/解码

SHA 加密
SHA 加密

SHA 加密工具