GraphQL

GraphQL

官方描述:
GraphQL 既是一種用於 API 的查詢語言也是一個知足你數據查詢的運行時。 GraphQL 對你的 API 中的數據提供了一套易於理解的完整描述,使得客戶端可以準確地得到它須要的數據,並且沒有任何冗餘,也讓 API 更容易地隨着時間推移而演進,還能用於構建強大的開發者工具。javascript

優勢

  1. GraphQL可讓咱們經過請求控制返回的字段,以此來減小restful api的設計理念帶來的請求屢次的問題。
    好比咱們要獲取指定id的文章相關信息,包括標題、做者、發佈時間以及前兩條評論;同時加載當前用戶信息。
// 兩趟查詢,難以拓展
GET /user/111
GET /article/1001?comment=2
// 一趟查詢,易於擴展
{
  article (id: 1001){
    title,
    author,
    time,
    comments (first: 2)
      nickname,
      time,
      content
    }
  },
  user (id: 111){
    nickname,
    photo,
    sign
  }
}
  1. 可拓展性
    前端自由選擇返回
  2. 不須要額外代碼處理冗餘字段
  3. 提供 schema
    schema 能夠在運行時被獲取到, 相似thrift的idl文件,比較清楚,簡單的方法甚至不須要文檔。
  4. 調試比較方便

缺點

  1. 緩存麻煩
    https://graphql.cn/learn/caching/
    Relay 和 apollo-client 是兩個graphql的前端模塊,能夠幫你在前端緩存數據。
  2. GraphQL 在前端如何與視圖層、狀態管理方案結合,目前也只有 React/Relay 這個一個官方方案。
    換句話說,若是你不是已經在用 Node + React 這個技術棧,引入 GraphQL 成本略高,風險也不小,這就很大程度上限制了受衆。
    並且 FB 官方就只有一個 Node.js 的 reference implementation,其餘語言都是社區作的。
  3. 每個 field 都對數據庫直接跑一個 query,會產生大量冗餘 query,雖然網絡層面的請求數被優化了,但數據庫查詢可能會成爲性能瓶頸。
    FB 自己沒有這個問題,由於他們內部數據庫這一層也是抽象掉的,寫 GraphQL 接口的人不須要顧慮 query 優化的問題。
    如何解決?
    DataLoader, DataLoader 的主要功能是 batching & caching,幫你合併請求。
  4. 須要服務端的全力配合
  5. GraphQL不存在鑑權方案,須要自行解決

get start

GraphQL爲express和koa提供了插件,能夠方便的搭建GraphQL服務器。
看下面的代碼:前端

var express = require('express');
var graphqlHTTP = require('express-graphql');
var { buildSchema } = require('graphql');

// 使用 GraphQL schema language 構建 schema
var schema = buildSchema(`
  input MessageInput {
    content: String
    author: String
  }

  type Message {
    id: ID!
    content: String
    author: String
  }

  type Query {
    getMessage(id: ID!): Message
  }

  type Mutation {
    createMessage(input: MessageInput): Message
    updateMessage(id: ID!, input: MessageInput): Message
  }
`);

// 若是 Message 擁有複雜字段,咱們把它們放在這個對象裏面。
class Message {
  constructor(id, {content, author}) {
    this.id = id;
    this.content = content;
    this.author = author;
  }
}

// 映射 username 到 content
var fakeDatabase = {};

var root = {
  getMessage: function ({id}) {
    if (!fakeDatabase[id]) {
      throw new Error('no message exists with id ' + id);
    }
    return new Message(id, fakeDatabase[id]);
  },
  createMessage: function ({input}) {
    // Create a random id for our "database".
    var id = require('crypto').randomBytes(10).toString('hex');

    fakeDatabase[id] = input;
    return new Message(id, input);
  },
  updateMessage: function ({id, input}) {
    if (!fakeDatabase[id]) {
      throw new Error('no message exists with id ' + id);
    }
    // This replaces all old data, but some apps might want partial update.
    fakeDatabase[id] = input;
    return new Message(id, input);
  },
};

var app = express();
app.use('/graphql', graphqlHTTP({
  schema: schema,
  rootValue: root,
  graphiql: true,
}));
app.listen(4000, () => {
  console.log('Running a GraphQL API server at localhost:4000/graphql');
});

咱們經過GraphQL提供的語法,創建一個schema,相似typescript的interface。
有多種基本類型:ID, String, Int, []。
Query是查詢操做,Mutation是增刪改操做。
因此這裏,咱們提供了一個getMessage的方法,返回Message類型的信息。
Mutation類型這裏咱們提供了createMessage,和updateMessage兩個方法。java

定義完schema,還須要定義方法的處理:
定義root對象進行函數處理。node

執行node index.js。而後訪問localhost:4000/graphql就能夠看到相應的調試頁面。(前提是graphiql:true)。

這個調試頁面仍是很方便的。
打開調試頁面,先create一個message:

而後查詢這個message:
mysql

type 爲query的時候,能夠不寫query。git

前端請求

var dice = 1;
var sides = 6;

var query = `query RollDice($dice: Int!, $sides: Int) {
  rollDice(numDice: $dice, numSides: $sides)
}`;

fetch('/graphql', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  },
  body: JSON.stringify({
    query,
    variables: { dice, sides },
  })
})
  .then(r => r.json())
  .then(data => console.log('data returned:', data));

這是官網給的試例。fetch請求,把query傳過去就能夠啦,若是請求的是個函數,須要傳參variables。如上dice和sides兩個參數github

鏈接數據庫

這裏給個鏈接mysql的schema和rootValuesql

var { buildSchema } = require('graphql');

const mysqlCon = require('./mysql');

// 使用 GraphQL schema language 構造一個 schema
let count = 0;
var schema = buildSchema(`
    type UserInfo {
        id: ID!
        name: String
        uid: Int
        age: Int
        sex: String
        createdTime: String
        updatedTime: String
        description: String
    }
    type Query {
        getUsers: [UserInfo]
        getUserById(id: ID!): UserInfo
    }
    type Mutation { 
        invokeCount: Int
    }
`);

// root 爲每一個端點入口 API 提供一個解析器
var root = {
    async getUsers() {
        count += 1;
        let users = await mysqlCon.pifySelect('select * from Tab_User_Info');
        console.log(users);
        return users;
    },
    async getUserById({id}) {
        let users = await mysqlCon.pifySelect(`select * from Tab_User_Info where id=${id}`)
        return users[0];
    },
    invokeCount() {
        return count;
    }
};

module.exports = {
    root, 
    schema,
}

mysql.jsdocker

const mysql = require('mysql');

const connection = mysql.createConnection({
    host: '127.0.0.1',
    port: 3306,
    user: 'root',
    password: '123456',
    database: 'Test_User'
});

connection.connect(function(err) {
    if (err) {
        console.error('error: ' + err.stack);
        return;
    }
});

Object.defineProperty(connection, 'pifySelect', {
    value: function(sql) {
        return new Promise((resolve, _)=>{
            connection.query(sql, function (error, results) {
                if (error) console.log('mysql select err:', error);
                resolve(results);
            }); 
        })
    }
})

module.exports = connection

用docker搞個mysql環境:typescript

docker pull mysql:5.6
docker run -p 3306:3306 --name mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.6

docker exec -it mysql bash進入bash而後初始化一下mysql,插幾個數據用來操做。
用koa-graphql或者express-graphql啓一個服務,就能夠直接訪問啦。

使用方

facebook, twitter,github,咱們公司(toutiao)部分業務在用。

相關文章
相關標籤/搜索