雖然 Apollo GraphQL 允許我們在 ECMAScript 內以 String 定義 GraphQL Schema,但實務上 Schema 會相當龐大,比較好的方式是拆成獨立的 *.graphql
方便維護。
Version
macOS Catalina 10.15.2
WebStorm 2019.3.1
Node 12.14.0
Apollo GraphQL 2.9.14
Babel 7.7.7
Import GraphQL 2.7.0
Apollo GraphQL
src/index.js
let { ApolloServer, gql } from 'apollo-server'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 }
]
let typeDefs = gql`
type Query {
books: [Book]
}
type Mutation {
addBook(book: BookInput!): Book
}
type Book {
title: String
price: Int
}
input BookInput {
title: String
price: Int
}
`
let books = () => data
let addBook = (_, { book }) => {
data = [...data, book]
return book;
}
let resolvers = {
Query: { books },
Mutation: { addBook }
}
let apolloServer = new ApolloServer({ typeDefs, resolvers })
apolloServer.listen()
.then(({ url }) => `GraphQL Server ready at ${ url }`)
.then(console.log)
第 9 行
let typeDefs = gql`
type Query {
books: [Book]
}
type Book {
title: String
price: Int
}
`
傳統後端會使用 string 定義 GraphQL schema,然後再透過 gql
將 string 轉成 GraphQL AST。
這種寫法有幾個缺點:
- 實務上 schema 會很龐大,僅使用單一 string 難以維護
- GraphQL 畢竟是獨立語言,藏在 ECMAScript 內稍嫌不妥
- 須在 runtime 將 string 轉成 GraphQL AST,稍微影響速度
比較好的方式是藉由 Import GraphQL
Babel plugin,將 GraphQL schema 獨立成 *.graphql
,在 Babel 轉譯階段將 string 轉成 GraphQL AST。
Package Installation
$ yarn add babel-plugin-import-graphql --dev
使用 Yarn 安裝 babel-plugin-import-graphql
,由於只是給 Babel 使用,安裝成 devDependencies
即可。
Babel Configuration
.babelrc
{
"presets": [
"@babel/preset-env"
],
"plugins": [
"import-graphql"
]
}
除了原本的 presets
外,在 plugins
加掛 import-graphql
。
Apollo GraphQL
src/index.js
import { ApolloServer } from 'apollo-server'
import typeDefs from './schema/index.graphql'
let data = [
{ title: 'FP in JavaScript', price: 100 },
{ title: 'RxJS in Action', price: 200 }
]
let books = () => data
let addBook = (_, { book }) => {
data = [...data, book]
return book;
}
let resolvers = {
Query: { books },
Mutation: { addBook }
}
let apolloServer = new ApolloServer({ typeDefs, resolvers })
apolloServer.listen()
.then(({ url }) => `GraphQL Server ready at ${ url }`)
.then(console.log)
第 2 行
import typeDefs from './schema/index.graphql'
index.js
內不再定義 GraphQL schema,只需從 index.graphql
import 即可。
GraphQL Schema
src/schema/index.graphql
#import 'query.graphql'
#import 'mutation.graphql'
index.graphql
負責統整所有 GraphQL schema。
babel-plugin-import-graphql
自定義 #import
,可在 *.graphql
內再 import 其他 *.graphql
。
src/schema/query.graphql
type Query {
books: [Book]
}
type Book {
title: String
price: Int
}
將 query 部分單獨定義在 query.graphql
。
src/schema/mutation.graphql
type Mutation {
addBook(book: BookInput!): Book
}
input BookInput {
title: String
price: Int
}
將 mutation 部分單獨定義在 mutation.graphql
。
Start Development Server
$ yarn serve
使用 yarn serve
啟動 Apollo GraphQL,babel-plugin-import-graphql
會一併將 *.graphql
轉譯成 GraphQL AST。
Build Docker Image
$ yarn docker:build
$ yarn docker:up
使用 yarn docker:build
命令 Babel 重新轉譯,並打包成 Docker image。
GraphQL Playground
query {
books {
title
price
}
}
可成功在執行 GraphQL query。
Conclusion
- 使用 Import GraphQL 最大優點是將 GraphQL schema 獨立成
*.graphql
,適合大型專案使用,先轉譯成 GraphQL AST 算額外賺到功能 - 至於要如何將 GraphQL schema 拆分,可視實際需求自行調整
Sample Code
完整範例可在我的 GitHub 上找到
Reference
Apollo Docs, Compiling queries with Babel
Daniel Cooke, Import GraphQL files without Webpack