# GraphQL services can be written in any language.
# We'll use the "GraphQL schema language"
# it's similar to the query language, and allows us to talk about GraphQL schemas in a `language-agnostic` way.
# `与语言无关`
# agnostic 不可知
Object types and fields
type Character {
name: String!
appearsIn: [Episode]!
}
# `Character` is a `GraphQL Object Type`, meaning it's a type with some fields.
# Most of the types in your schema will be object types.
# `name` and `appearsIn` are `fields` on the `Character` type.
# That means that name and appearsIn are the only fields that can appear in any part of a GraphQL query that operates on the Character type.
# `String` is one of the built-in `scalar types`
# these are types that resolve to a single scalar object, and can't have sub-selections in the query.
# scalar types, 标量类型
# `String!` means that the field is `non-nullable`,
# meaning that the GraphQL service promises to always give you a value when you query this field.
# exclamation mark, 感叹号
Arguments
type Starship {
id: ID!
name: String!
length(unit: LengthUnit = METER): Float
# defined argument: `unit`
# default value: `METER`
# scalar types: `Float`
}
# All arguments are named.
# all arguments in GraphQL are passed by name specifically.
# In this case, the `length` field has one defined argument, `unit`.
# Arguments can be either required or optional.
# When an argument is optional, we can define a `default value` - if the unit argument is not passed, it will be set to `METER` by default.
The Query and Mutation types
Most types in your schema will just be normal object types, but there are two types that are special within a schema:
# Every GraphQL service has a `query type` and may or may not have a `mutation type`.
schema {
query: Query
mutation: Mutation
}
# These types are the same as a regular object type, but they are special because they define the entry point of every GraphQL query.
query {
hero {
name
}
droid(id: "2000") {
name
}
}
# That means that the GraphQL service needs to have a Query type with hero and droid fields:
type Query {
hero(episode: Episode): Character
droid(id: ID!): Droid
}
# Mutations work in a similar way
# you define fields on the Mutation type, and those are available as the root mutation fields you can call in your query.
# A GraphQL object type has a name and fields, but at some point those fields have to resolve to some concrete data.
# 一个 GraphQL 对象类型具有名称和字段,但在某些时候,这些字段必须解析为某些具体数据。
{
hero {
name
appearsIn
}
}
# GraphQL comes with a set of default scalar types out of the box:
# 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: The ID scalar type represents a unique identifier, often used to refetch an object or as the key for a cache.
# The ID type is serialized in the same way as a String;
# however, defining it as an ID signifies that it is not intended to be human‐readable.
# In most GraphQL service implementations, there is also a way to specify custom scalar types.
# Then it's up to our implementation to define how that type should be serialized, deserialized, and validated.
`scalar Date`
Enums, enumeration types are a special kind of scalar that is restricted to a particular set of allowed values.
enum Episode {
NEWHOPE
EMPIRE
JEDI
}
# This means that wherever we use the type Episode in our schema, we expect it to be exactly one of NEWHOPE, EMPIRE, or JEDI.
# in a language like JavaScript with no enum support, these values might be internally mapped to a set of integers.
# 在没有枚举支持的JavaScript语言中,这些值可能在内部映射到一组整数。
Lists and Non-Null
# But when you use the types in other parts of the schema, or in your query variable declarations,
# you can apply additional `type modifiers` that affect validation of those values.
type Character {
name: String!
appearsIn: [Episode]!
}
# we're using a `String type` and marking it as `Non-Null` by adding an exclamation mark, `!` after the type name.
# The Non-Null type modifier can also be used when defining arguments for a field,
query DroidById($id: ID!) {
droid(id: $id) {
name
}
}
# We can use a `type modifier` to mark a type as a `List`, which indicates that this field will return an `array` of that type.
# In the schema language, this is denoted by wrapping the type in `square brackets`, [ and ].
# The Non-Null and List modifiers can be combined.
myField: [String!]
# This means that the list itself can be null, but it can't have any null members.
myField: null // valid
myField: [] // valid
myField: ['a', 'b'] // valid
myField: ['a', null, 'b'] // error
# defined a Non-Null List of Strings:
myField: [String]!
# This means that the list itself cannot be null, but it can contain null values:
myField: null // error
myField: [] // valid
myField: ['a', 'b'] // valid
myField: ['a', null, 'b'] // valid
# You can arbitrarily nest any number of Non-Null and List modifiers, according to your needs.
# 可以根据需要随意嵌套任意数量的非空和列表修饰符。
{
"data": {
"droid": null
}
}
{
"data": {
"droid": null
}
}
Interfaces
An Interface is an abstract type that includes a certain set of fields that a type must include to implement the interface.
interface Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
}
# Star Wars trilogy 星球大战三部曲
# This means that any type that implements Character needs to have these exact fields, with these arguments and return types.
type Human implements Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
starships: [Starship]
totalCredits: Int
}
type Droid implements Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
primaryFunction: String
}
query HeroForEpisode($ep: Episode!) {
hero(episode: $ep) {
name
primaryFunction
}
}
query HeroForEpisode($ep: Episode!) {
hero(episode: $ep) {
name
... on Droid {
primaryFunction
}
}
}
{
"ep": "JEDI"
}
{
"errors": [
{
"message": `
Cannot query field \"primaryFunction\" on type \"Character\".
Did you mean to use an inline fragment on \"Droid\"?
`,
"locations": [
{
"line": 4,
"column": 5
}
]
}
]
}
{
"data": {
"hero": {
"name": "R2-D2",
"primaryFunction": "Astromech"
}
}
}
Union types
Union types are very similar to interfaces, but they don't get to specify any common fields between the types
# Note that members of a union type need to be concrete object types;
# you can't create a union type out of interfaces or other unions.
union SearchResult = Human | Droid | Starship
{
search(text: "an") {
... on Human {
name
height
}
... on Droid {
name
primaryFunction
}
... on Starship {
name
length
}
}
}
{
search {
... on Human {
name
height
}
... on Droid {
name
primaryFunction
}
... on Starship {
name
length
}
}
}
input types look exactly the same as regular object types, but with the keyword input instead of type:
input ReviewInput {
stars: Int!
commentary: String
}
# This is particularly valuable in the case of `mutations`, where you might want to pass in a whole object to be created.
mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
createReview(episode: $ep, review: $review) {
stars
commentary
}
}
# The fields on an input object type can themselves refer to input object types,
# but you can't mix input and output types in your schema.
# Input object types also can't have arguments on their fields.
{
"ep": "JEDI",
"review": {
"stars": 5,
"commentary": "This is a great movie!"
}
}
{
"data": {
"createReview": {
"stars": 5,
"commentary": "This is a great movie!"
}
}
}
Schemas and Types
http://graphql.org/learn/schema
Type system
Type language
Object types and fields
Arguments
The Query and Mutation types
Scalar types
Enumeration types
Lists and Non-Null
Interfaces
Union types
Input types