about 1 month ago

GraphQL一言以蔽之就是 query or mutation object 上面的特定的 field

Schema & Query

Schema, 指的是 server side 定義好的資料結構
Query, 指的是 client side 符合 schema 的查詢語言

Ex1
# Schema
type User {
  name
}

type Query {
  me: User
}

# Query
query {
  me {
    name
    age
  }
}

Field & Type

Type 定義了 query(或 mutation)的資料應該長什麼樣子

object 上的 field 都會都對應的 type, 可以分成兩大類

  1. Scalar Type: 基礎型別, 如 Int, String, Boolean
  2. Object Type: 物件型別, 屬於自定義資料結構, 並可以展開

用 OO 來類比 GraphQL

Class Person {
  constructor(name) {
      this.name = name;
    }
}

const user = new Person('Tom')
user.name // => Tom

Person 是一個 Class, user 是他的 instance, 該 instance 有一個 field name, 值為 Tom

延續 Ex1

User 是一個 Object Type, me (filed) 使用 User 作為 Type, me 擁有一個 scalar type name

Resolver

Resolver 定義當取得資料的實作

// data

const usersData = [
  { name: 'Tim' },
  { name: 'Tom' }
]

// schema

gql`
  type User {
    name: String
  }
  
  # Query 作為 query 的進入點
  type Query {
    users: User
  }
`

// resolver

{
  Query: {
    users: () => usersData
  }
}

// query

{
  users {
    name
  }
}

// result

{
  data: {
    [
      { name: 'Tim' },
      { name: 'Tom' }
    ]
  }
}

Operation Name, Aliases, Fragment, Arguments, Variables

Argments.js
// data

const usersData = [
  {
    name: 'Tim',
    id: 1
  },
]

// schema

gql`
  type User {
    name: String
    id: Int
  }
  
  # Query 作為 query 的進入點
  type Query {
    user(name: String!): User
  }
`

// resolver

{
  Query: {
    user: (root, args, context) => {
      const { name } = args;
      return usersData.find((user) => user.name === name)
    }
  }
}

// query

{
  user(name: 'Tim') {
    id
  }
}

// result

{
  data: {
    id: 1
  }
}
Variables
# 使用 Argments.js  data, resolver, schema

// query

query($name: String!){
  user(name: $name) {
    id
  }
}

// query variable

{
  "name": "Tim"
}

//result 

 Argments.js 的結果相同

Operation Name 改善 query 的可讀性, 接在 query 後面, 不影響 query 的結果

Operation Name
query WhatIsTimId($name: String!){
  user(name: $name) {
    id
  }
}

Aliases 可以一次 query 多個同 object typefield

query AllUserId($name1: String!, $name2: String!){
  firstUser: user(name: $name1) {
    id
  },
  secondUser: user(name: $name2){
    id
  }
}

// result
{
  "data": {
    "firstUser": {
      "id": "1"
    },
    "secondUser": {
      "id": "2"
    }
  }
}

Fragment: 將 query 片段封裝起來

query AllUserId($name1: String!, $name2: String!){
  firstUser: user(name: $name1) {
    ...userData
  },
  secondUser: user(name: $name2){
    ...userData
  }
}

fragment userData on User{
  id
}

Mutation

// data

const usersData = [
  {
    name: 'Tim',
    id: 1
  },
]

const postsData = [
  {
    authorId: 1,
    title: 'first post'
  }
]

// schema

gql`
  type User {
    name: String
    id: Int
  }

  type Post {
    author: User
    title: String
    content: String
  }
  
  type Query {
    user(name: String!): User
    posts: [Post]
  }
`

// resolver

{
  Query: {
    user: (root, args, context) => {
      const { name } = args;
      return usersData.find((user) => user.name === name)
    }
  },
  Post: {
    author: (parent, args, content) => {
      const { authorId } = parent;
      postsData.push({
        authorId: 1,
        title,
        content
      })
      return postsData[postsData.length - 1]
    }
  }
}

// mutation

mutation updatePosts{
  addPost(title: "mutation is awosome!", content: "hello world"){
    title
    author{
      id
    }
  }
}

// result

{
  "data": {
    "addPost": {
      "title": "mutation is awosome!",
        "author": {
        "id": "1"
      }
    }
  }
}

// query

query AllPosts{
  posts{
    title
  }
}

// result

{
  "data": {
    "posts": [
      {
        "title": "My first post"
      },
      {
        "title": "mutation is awosome!"
      }
    ]
  }
}

input Object Type

// schema

// 注意是使用 input, 而不是一般的 type

input AddPostInput {
  title: String!
  content: String
}

// Mutation 定義

type Mutation {
  addPost(input: AddPostInput): Post
  # 不使用 Object Type 而喂入參數
  # addPost(title:String, content: String): Post
}

// resolver

  Mutation: {
    addPost: (root, args, context) => {
      const { input } = args;
      const { title, content } = input;
      posts.push({
        authorId: 1,
        title,
        content,
      })
      return posts[posts.length - 1]
    }
  }
  
// query

mutation updatePosts($input: AddPostInput) {
  addPost(input: $input) {
    title
    author {
      id
    }
  }
}

// variable

{
  "input":{
    "title": "mutation is awosome!",
    "content": "hello world"
  }
}

Resolver 參數 (parent, args, context)

  1. parent 為此 field 上一層的資料, 也就是該 field 存在於某個物件下, 該物件即為 parent。
  2. args 為 client side 的 query 對此項 fied 傳進來的參數。
  3. context 類似於全域變數的概念,在單一 query 內所有 field 都可享用的資源,通常會放些 user 資料或 ORM 方法。

ref1
ref2

← Redux Thunk
 
comments powered by Disqus