SDL

GraphQL SDL

SDL(Schema Definition Language)是标准的GraphQL Schema的表示方式,在编程中我们往往会将其转化为GraphQL.jsGraphQLSchema对象,或者其他语言中的等效描述对象。

Schema中定义了我们可以查询或者操作的数据属性与类型以及它们之间的关系,为开发者提供了清晰的接口数据格式定义;这种强类型定义也赋能了像GraphiQL这样能够自动补全的工具,并且促进了其他的譬如IDE集成插件、查询验证、代码生成、自动Mock等工具的出现。最常见的GraphQL Schema的表示方式就是Schema Definition Language, SDL,即 GraphQL specification 中提及的字符串模式,有点类似于传统的IDL(Interface Definition Language)或者SDL(Schema Definition Language)。在GraphQL中,对于数据模型的抽象是通过Type来描述的,对于接口获取数据的逻辑是通过Schema来描述的。GraphQL中每一个Type有若干Field组成,每个Field又分别指向某个TypeType又可以分为Scalar Type(标量类型)Object Type(对象类型)

Node中的Schema对象

GraphQLSchema对象

SDL是标准的GraphQL Schema的表示方式,在编程中我们往往会将其转化为GraphQL.jsGraphQLSchema对象,或者其他语言中的等效描述对象。

const { graphql, buildSchema } = require("graphql");

// 使用 GraphQL schema language 定义 Schema
const schema = buildSchema(`
  type Query {
    hello: String
  }
`);

// 为每个 API 端点提供解析函数
const root = {
  hello: () => {
    return "Hello world!";
  }
};

// 执行查询请求,并且获取结果
graphql(schema, "{ hello }", root).then(response => {
  console.log(response);
});

另一种常见的Schema的表示方式即是GraphQL.jsGraphQLSchema对象,该类型对象才能够被服务端或者客户端的解析代码所使用:

const {
  GraphQLObjectType,
  GraphQLSchema,
  GraphQLNonNull,
  GraphQLInt
} = require("graphql");

const queryType = new GraphQLObjectType({
  name: "Query",
  fields: {
    posts: {
      type: postType
    },
    author: {
      name: "author",
      type: authorType,
      arguments: { id: { type: new GraphQLNonNull(GraphQLInt) } }
    }
  }
});

// ... postType and authorType defined similarly

const schema = new GraphQLSchema({
  query: queryType
});

Apollo GraphQL

Apollo GraphQL为我们提供了全栈式的GraphQL开发工具与良好的体验,也可以看做GraphQL标准的一种实现方式。graphql-tag 提供了GraphQL的查询辅助工具,能够将某个查询转化为GraphQLAST表示:

import gql from "graphql-tag";

const query = gql`
  {
    user(id: 5) {
      firstName
      lastName
    }
  }
`;

// query is now a GraphQL syntax tree object
console.log(query);

// {
//   "kind": "Document",
//   "definitions": [
//     {
//       "kind": "OperationDefinition",
//       "operation": "query",
//       "name": null,

graphql-tools 则提供了完整的Schema生成与合并工具,支持Resolver, Interface, Union, Scalar:

import { buildSchema, printSchema, makeExecutableSchema } from "graphql-tools";

const sdlSchema = `...`;

// 仅仅生成 Schema 对象
const graphqlSchemaObj = buildSchema(sdlSchema);

// 生成可用于服务端的 Schema 对象
const graphqlSchemaObj = makeExecutableSchema({
  typeDefs: sdlSchema,
  resolvers: {
    Query: {
      author: () => ({ firstName: "Ada", lastName: "Lovelace" })
    }
  }
});

// 转化为 SDL
console.log(printSchema(graphqlSchemaObj));

类型基础

类型声明

GraphQL中使用 type 关键字来指定类型名,类型还可以继承一到多个接口:

type Post implements Item {
  # ...
}

某个属性域包含了名称与类型,该类型可以是内建或自定义的标量类型,也可以是Schema中自定义的类型;对于非空的属性域可以使用 ! 来标记:

age: Int!

较为全面的类型定义范例如下:

type Post {
  id: String!
  title: String!
  publishedAt: DateTime!
  likes: Int! @default(value: 0)
  blog: Blog @relation(name: "Posts")
}

type Blog {
  id: String!
  name: String!
  description: String
  posts: [Post!]! @relation(name: "Posts")
}

内省查询结果

GraphQLAPI是被要求自我注释的,每个GraphQL API应可以返回其结构,这就是所谓的内省(Introspection),往往是 __schema 端口的返回结果:

{
  __schema {
    types {
      kind
      name
      possibleTypes {
        name
      }
    }
  }
}

我们可以利用graphql库提供的introspectionQuery查询来进行获取:

const { introspectionQuery } = require("graphql");
// ...
fetch("https://1jzxrj179.lp.gql.zone/graphql", {
  // ...
  body: JSON.stringify({ query: introspectionQuery })
});
// ...

同样的,我们可以将内省的查询结果转化为GraphQL Schema对象:

const { buildClientSchema } = require("graphql");
const fs = require("fs");

const introspectionSchemaResult = JSON.parse(fs.readFileSync("result.json"));
const graphqlSchemaObj = buildClientSchema(introspectionSchemaResult);

Scalar Type |标量类型

GraphQL内建提供了以下标量类型:

  • Int
  • Float
  • String
  • Boolean
  • ID

Enum是较为特殊的标量类型,其包含了可能的值:

enum Category {
  PROGRAMMING_LANGUAGES
  API_DESIGN
}

自定义标量类型

import { makeExecutableSchema } from "graphql-tools";
import GraphQLJSON from "graphql-type-json";

const schemaString = `
scalar JSON

type Foo {
  aField: JSON
}

type Query {
  foo: Foo
}
`;

const resolveFunctions = {
  JSON: GraphQLJSON
};

const jsSchema = makeExecutableSchema({
  typeDefs: schemaString,
  resolvers: resolveFunctions
});

Object Type |对象类型

数组则是用大括号表示:

names: [String!]

Type Modifier |类型修饰

Interface |接口

GraphQL中,interface 是一系列属性域的集合。

Directive |指令

我们可以通过指令来为任意的Schema定义添加额外的信息,指令并没有其固有的含义,每个GraphQL标准的实现都可以定义其独有功能。

name: String! @defaultValue(value: "new blogpost")

GraphQL标准中规范了指令的定义与使用的方式:

directive @deprecated(
  reason: String = "No longer supported"
) on FIELD_DEFINITION | ENUM_VALUE

type ExampleType {
  newField: String
  oldField: String @deprecated(reason: "Use `newField`.")
}

在实际开发中,我们可以使用graphql-tools提供的SchemaDirectiveVisitor来快速开发自定义指令,譬如我们需要某个提示属性域被废弃的@deprecated指令:

import { SchemaDirectiveVisitor } from "graphql-tools";

class DeprecatedDirective extends SchemaDirectiveVisitor {
  public visitFieldDefinition(field: GraphQLField<any, any>) {
    field.isDeprecated = true;
    field.deprecationReason = this.args.reason;
  }

  public visitEnumValue(value: GraphQLEnumValue) {
    value.isDeprecated = true;
    value.deprecationReason = this.args.reason;
  }
}

然后在声明Schema

Fragments

Fragments类似于组件,帮助我们解决代码中请求内容的重复问题,即可将某个元数据声明复用到不同的查询或者修改中。

recentPost {
	title
	description
	author {
		...authorInfo
	}
}

fragment authorInfo as Author {
	id
	name
}

Query & Mutation |查询与更改

QueryMutationGraphQL的默认入口之一,GraphQL Schema中内置的ROOT包含了以下几种:

type Query {}
type Mutation {}
type Subscription {}

schema {
  query: Query
  mutation: Mutation
  subscription: Subscription
}

Arguments |参数

Mutation

GraphQL为我们提供了Mutation类型,以进行数据操作。

下一页