GraphQL入門

1.1 接口開發方式

  • restful
  • GraphQL

1.2 restful 接口問題

  • 接口粒度比較細、不少場景須要調用屢次才能完成一個功能
  • 接口擴展、維護成本高
  • 接口響應的數據格式沒法預知、目前基本都是使用json

1.3 GraphQL概述

GraphQL 是一種用於 API 的查詢語言、也是一個知足你數據查詢的運行時
是一種接口開發標準、支持常見的服務器開發語言
複製代碼

1.4 GrapgQL 優點

  • 精確獲取須要的信息
  • 經過單個請求獲取各類資源
  • 經過類型系統描述查詢
  • 強大的調試工具

二. 上手玩玩

2.1 服務端

  • 建立服務端項目目錄git

  • 初始化項目 npm init -ygithub

  • 建立入口文件 index.jsweb

  • 安裝依賴包數據庫

    • Apollo-server-expressexpress

    • expressnpm

    • graphqljson

      npm install apollo-server-express body-parser express graphql graphql-tools -S
      複製代碼
  • 複製官方案例代碼到index.js數組

  • 啓動bash

2.2 客戶端

{
  hello
}
複製代碼

三.基於apolloServer開發

apolloserver 是一個實現了 GraphQL 規範的框架、能夠基於apolloserver 快速開發基於 GraphQL 的服務端接口、而且方便客戶端進行接口調用服務器

3.1 開發

  • 基本開發步驟-服務端
    • 初始化服務端項目
      • 建立項目目錄
      • 建立入口文件
      • 初始化項目
    • 安裝依賴
    • 定義類型
    • 解析數據
    • 實例化apollo對象
    • 整合express並監聽端口、提供web服務

3.2 對象類型定義規則

3.2.1 對象類型定義與字段

  • GraphQL提供了一套完善的類型系統、能夠約束服務端服務端能夠提供哪些數據類型供客戶端查詢使用
  • 經過 type 關鍵字定義類型、type 以後是類型名稱(自定義名稱)
# 課程類型
type Course {
  cname: String
  score: Float
}

# 學生類型
type Student{
  name: String
  scores:[Course]
}
複製代碼
  • 注意事項
    • 花括號中是對象的字段信息
    • 屬性名稱是自定義的
    • 屬性名後面的類型爲標量類型(內置類型)
    • GraphQL 使用 # 註釋

3.2.2 基本參數

  • 對象類型上的每個字段均可能有零個或者多個參數
type Query {
  hello: String
  # 定義字段參數 能夠有默認值
  stu(n: Int = 12): Student
}


# 獲取參數(經過 resolver 函數的第二個參數獲取客戶端傳遞的參數數據)
const resolvers = {
  Query: {
    stu: (obj, args) => {
      console.log(args)
    }
  }
}

# 客戶端字段傳遞
{
  stu(n: 13){
    name
    age
  }
}
複製代碼
  • 注意事項
    • 參數能夠指定默認值、若是有默認值則參數是可選的
    • 客戶端能夠經過 resolvers 函數的第二個參數獲取傳遞的數據

3.3.3 內置類型

  • 查詢和變動類型
內置類型中有兩個特殊的類型:Query(主要用於查詢操做) 和 Mutation(主要用於變動操做 -- 增、刪、改 )

每個GraphQL 服務有一個 Query 類型、也可能有一個 mutation 類型、這兩個類型本質上也是對象類型、惟一的區別就是他們做爲客戶端訪問的入口
複製代碼
  • 標量類型 — 用於表示基本的字段數據、表示查詢數據的葉子節點
    • Int
    • Float
    • String
    • Booleab
    • ID 惟一標識符,隨機生成一個字符串、不須要能認識
  • 枚舉類型 — 是一種特殊的標量、枚舉類型限制在一個特殊的可選值集合內
enmu Fovour {
	SWIMING
	GODING
	SINGING
}
複製代碼

上述定義表示只能獲取三種值之1、獲取其餘類型的值是不能夠的

  • 列表和非空
    • [] 表示列表
    • !歎號表示非空
type Student{
	name: String!
	scores:[Score]!
}
複製代碼

myField:[String!] 表示數組自己能夠爲空、可是其不能有任何空值成員

myField:[String]! 表示數組自己不能夠爲空、可是其能夠包含空值成員

3.3.4 輸入類型

參數也能夠是複雜類型、主要用於變動 mutation 場景 — 須要客戶端傳遞輸入類型

# 輸入類型
  input UserInfo{
    name: String
    pwd: String
  }

  #用戶類型
  type User {
    id: ID
    name: String
    pwd: String
  }
複製代碼

定義 mutation、能夠用於接收客戶端傳遞的input類型數據

# 變動類型
type Mutation{
	addUserByInput(userInput: UserInfo): User
}
複製代碼
# 處理客戶端傳遞的input參數
const resolvers = {
  Query: {
    hello: () => 'Hello world!'
  },
  Mutation: {
    addUserByInput: (obj, args) => {
      return {
        id: uuid(),
        name: args.userInput.name,
        pwd: args.userInput.pwd
      }
    }
  }
};
複製代碼
# 客戶端查詢操做
mutation {
	addUserByInput(userInput: {
		name: 'nordon',
		pwd: '123123'
	}){
		id
		name
	}
}
複製代碼
  • 注意事項
    • input 類型主要用於變動操做的數據傳遞

3.3 數據解析規則詳解

###3.3.1 resolver 函數參數用法

resolvers 用於給類型字段提供實際數據

  • resolver 函數參數
    • parent 上一級對象、若是字段屬於根節點查詢類型一般不會被使用
    • args 能夠提供在 GrapgQL 查詢中傳入的參數
    • context 會被提供給全部解析器、而且持有重要的上下文信息。好比當前登入的用戶、數據庫訪問對象
    • info 一個保存與當前查詢相關的字段特定信息以及 schema 詳細信息的值
Mutation: {
    addUserByInput: (parent, args, context, info) => {
      // parent 當前字段的父級對象 -- Query 中的 hello 至關於根節點,例如查學生中的 name、age 就是子節點
      // args 客戶端傳遞過來的參數
      // context 能夠用於操做數據源
      // info 不怎麼用
      return {
        id: uuid(),
        name: args.userInput.name,
        pwd: args.userInput.pwd
      }
    }
  }
複製代碼
  • Parent 參數用法
// 類型定義
type Student {
	name: String
	age: Int
	favour: String
}

// 數據解析
const resolvers = {
  Student: {
    // 提供字段的 resolver 函數、若是不提供會默認生成
    name: (parent) => {
      // return 'wangyao' 會覆蓋父節點的 name
      return parent.name
    },
    favour: (parent) => {
      if(parent.favour === 'swimming'){
        return '洗澡'
      }else{
        return parent.favour
      }
    }
  },
  Query: {
    hello: () => 'Hello world!',
    stu: (parent) => {
      return {
        name: 'nordon',
        age: 18,
        favour: 'swimming'
      }
    }
  },
};
複製代碼
  • 注意事項
    • 經過 resolver 函數的第一個參數 parent 能夠獲取父級對象
    • 若是字段類型是標量類型、會有一個默認的 resolver 函數
    • 默認的 resolver 函數、返回父級對象的字段數據

3.3.2 resolver 函數對接數據源

經過 context 函數更加方便的對接數據源(數據庫、文件、第三方接口)、包括異步操做

  • context 基本用法
// context 能夠用於獲取數據源對象
const context = () => {
	return {
		db: 'dbObj'
	}
}

const server = new ApolloServer({
	typeDefs,
	resolvers,
	context
})
複製代碼
// 在 resolver 函數中能夠經過 context 對象操做數據源
const resolvers = {
	Query: {
		hello: (parent, args, context) => {
			// 經過 context 能夠獲取數據源操做對象
			let ret = context.db
		}
	}
}
複製代碼
  • 讀取文件數據業務模塊
// db.js 代碼:從文件中讀取數據、本質上與從數據庫中讀取數據類型、多能夠異步操做
const path = require('path')
const fs = require('fs')

module.exports.getData = () => {
	const dataPath = path.join(__dirname, 'data.json')
	
	return new Promise((resolve, reject) => {
		fs.readFile(dataPage, (err, data) => {
			if(err){
				reject(err)
			}
			
			resolve(data.toString())
		})
	})
}

// data.json 提供數據源
[{
  "name": "nordon",
  "age": 18
},{
  "name": "wangyao",
  "age": 22
},{
  "name": "bing",
  "age": 23
}]
複製代碼
  • 經過 context 獲取數據源操做的對象
// 從文件中讀取數據做爲數據源、 db.getData() 返回 Promise 實例對象
const db = require('./db.js')

const typeDefs = gql`
  # 學生
  type Student {
    name: String
    age: Int
  }

  # 查詢類型
  type Query {
    hello: [Student!]!
  }
`;

const context = ({req}) => {
	return {
		db: db
	}
}

const resolvers = {
	Query: {
		hello: async (parent, args, context, info) => {
			const ret = await context.db.getData()
			return JSON.parse(ret)
		}
	}
}


const server = new ApolloServer({
	typeDefs,
	resolvers,
	context
})
複製代碼
  • 注意事項
    • context 參數主要用於提供數據源相關的對象、方便進行數據操做(數據獲取與變動)
  • 客戶端開發傳送門 GraphQL 客戶端開發

參考網站

grapgQL官網

apolloserver官網

相關文章
相關標籤/搜索