淺淺地談 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》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

Host Your Web Site In The Cloud

Host Your Web Site In The Cloud

Jeff Barr / SitePoint / 2010-9-28 / USD 39.95

Host Your Web Site On The Cloud is the OFFICIAL step-by-step guide to this revolutionary approach to hosting and managing your websites and applications, authored by Amazon's very own Jeffrey Barr. "H......一起来看看 《Host Your Web Site In The Cloud》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试