最近剛完成一個新專案,閒著沒事,想著學點新東西(做前端的人都懂,技術更新實在太快了,不學容易out),聽說GraphQL
現在開始火起來,大有取代傳統Restful API
的方式的趨勢,所以我決定學學。
什麼是 GraphQL
GraphQL 是由 Facebook 創造的用於 API 的查詢語言(這裡查詢語言所指的並不是常規意義上的類似 sql 語句的查詢語言,而是一種用於前後端資料查詢方式的規範)。
GraphQL 既是一種用於 API 的查詢語言也是一個滿足你資料查詢的執行時。 GraphQL 對你的 API 中的資料提供了一套易於理解的完整描述,使得客戶端能夠準確地獲得它需要的資料,而且沒有任何冗餘,也讓 API 更容易地隨著時間推移而演進,還能用於構建強大的開發者工具。
複製程式碼
更多關於GraphQL
的介紹大家可以看這裡
由於GraphQL
只是一套規範,不能直接使用,但社群有了很多程式語言的實現,可以直接拿過來用。這裡我選用了Apollo
。
什麼是 Apollo
Apollo 是基於 GraphQL 的全棧解決方案集合。包括了 apollo-client 和 apollo-server ;從後端到前端提供了對應的 lib 使得開發使用 GraphQL 更加的方便。
複製程式碼
GraphQL
需要前後端一起才能真正發揮它的的作用,我們先在服務端實現它,這裡就要用到apollo-server
。
apollo-server是一個在Node.js
上構建GraphQL
服務端的web中介軟體。支援express
,koa
,hapi
等框架。這裡我用的是koa
。
首先,我們要安裝依賴包
yarn add koa koa-bodyparser koa-router apollo-server-koa graphql graphql-tools
//or
npm install koa koa-bodyparser koa-router apollo-server-koa graphql graphql-tools
複製程式碼
然後,編寫程式碼
// server.js
const Koa = require('koa');
const Body = require('koa-bodyparser');
const router = require('koa-router')();
const {graphqlKoa, graphiqlKoa} = require('apollo-server-koa');
const {makeExecutableSchema} = require('graphql-tools');
const { GraphQLScalarType } = require('graphql');
const { Kind } = require('graphql/language');
const app = new Koa();
const PORT = 8090;
// 模擬資料
const users = [
{
id: 1,
name: 'J.K. Rowling',
date: new Date(2018, 5, 20)
},
{
id: 2,
name: 'Michael Crichton',
date: new Date(2018, 5, 21)
},
];
const typeDefs = `
scalar Date
type User{
id:Int!
name:String!
date: Date!
}
type Query {
users(id:Int!): [User]
user(id:Int!, name: String!):User
}
type Mutation {
addUser(name:String!):User
}
schema {
query: Query
mutation: Mutation
}
`;
const resolvers = {
Query: { // 對應到typeDefs中的 type Query
users(root, args, context) {
return users;
},
user(root, args, context, info) {
return {id: args.id, name: args.name};
}
},
Mutation: { // 對應到typeDefs中的 Mutation
addUser(root, args, context) {
return {id: 2, name: args.name};
}
},
Date: new GraphQLScalarType({ // 自定義標量型別
name: 'Date',
description: 'Date custom scalar type',
parseValue(value) {
return new Date(value); // 從客戶端來的資料
},
serialize(value) {
return value.getTime(); // 傳送給客戶端的資料
},
parseLiteral(ast) {
if (ast.kind === Kind.INT) {
return parseInt(ast.value, 10);
}
return null;
},
}),
};
const myGraphQLSchema = makeExecutableSchema({
typeDefs,
resolvers
});
app.use(Body());
router.post('/graphql', graphqlKoa({
schema: myGraphQLSchema,
}));
router.get('/graphql', graphqlKoa({
schema: myGraphQLSchema,
}));
router.get( // 在瀏覽器裡使用GraphiQL(可以理解成GraphQL領域的postman)
'/graphiql',
graphiqlKoa({
endpointURL: '/graphql',
}),
);
app.use(router.routes());
app.use(router.allowedMethods());
app.listen(PORT, ()=>console.log('app run in localhost:' + PORT));
複製程式碼
接下來,執行下面命令來讓我們的服務端跑起來:
node server.js
app run in localhost:8090
複製程式碼
然後在瀏覽器裡輸入http://localhost:8090/graphiql
,你會看到如下介面:
GraphQL
服務已成功啟動。你可以在介面上進行相應的資料查詢。
服務端已經成功啟動,接下來就是我們的客戶端了,前端本人使用的是React
框架,為了方便所以專案直接使用create-react-app
建立。
建立完成後我們進入專案目錄,接著安裝客戶端GraphQL
查詢所需要的依賴包:
yarn add react-apollo graphql-tag graphql apollo-client apollo-cache-inmemory apollo-link-http
// or
npm install react-apollo graphql-tag graphql apollo-client apollo-cache-inmemory apollo-link-http
複製程式碼
接下來我們修改src/index.js
裡的程式碼:
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import { ApolloProvider } from 'react-apollo';
import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { HttpLink } from 'apollo-link-http';
const httpLink = new HttpLink({ uri: 'http://localhost:8090/graphql' })
const client = new ApolloClient({
link: httpLink,
cache: new InMemoryCache()
})
ReactDOM.render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>
, document.getElementById('root'));
registerServiceWorker();
複製程式碼
然後修改src/App.js
裡的程式碼:
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import gql from 'graphql-tag';
import { graphql } from 'react-apollo';
class App extends Component {
render() {
console.log(this.props);
const { loading } = this.props.data;
if (loading) {
return <div>Loading...</div>
}
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
<p>{this.props.data.user.name}</p>
</div>
);
}
}
export default graphql(gql`
query User{
user(id: 100, name: "zhangsfs") {
id
name
}
users(id: 100) {
id
name
date
}
}
`)(App);
複製程式碼
經常包裝後的App元件會被注入一個名為data
的props
,它包含下面這些欄位:
loading
欄位為true
是表示在正在查詢,為false
表示查詢完成。
然後執行前端程式碼:
yarn start
// Compiled successfully!
// You can now view client in the browser.
// Local: http://localhost:8081/
// On Your Network: http://172.22.228.1:8081/
複製程式碼
在瀏覽器輸入http://localhost:8081/
結果發現頁面報錯,沒能正常執行,我們開啟開發者工具發現了其中一項報錯:
那麼如何才能實現跨域訪問呢?我們需要在修改服務端程式碼讓他支援跨域訪問。
首先在服務端增加一個依賴包:
yarn add @koa/cors@2
複製程式碼
然後修改server.js程式碼:
// server.js
const Koa = require('koa');
const Body = require('koa-bodyparser');
const router = require('koa-router')();
const cors = require('@koa/cors');
...
app.use(Body());
app.use(cors());
...
app.listen(PORT, ()=>console.log('app run in localhost:' + PORT));
複製程式碼
然後重新啟動服務端程式碼,重新整理http://localhost:8081/
,發現專案能正常執行啦。至此一個簡單的GraphQL
查詢就算完成了。
寫在最後
上面只是一個非常簡單的GraphQL
查詢demo,因為本人也是初學,很多東西也還在學習階段。如果大家有什麼好的開發心得,歡迎交流。