Node 雖然支援大部分 ECMAScript 2015 語法,但更新的 ECMAScript 版本則不確定,且預設還是要使用 require()
與 CommonJS,若學 Frontend 使用 Babel,則 Apollo GraphQL 也能享受 ECMAScript 最新語法。
Version
macOS Catalina 10.15.2
WebStorm 2019.3
Node 13.2.0
Apollo GraphQL 2.9.6
Babel 7.6.4
Install Package
$ yarn add apollo-server graphql
$ yarn add @babel/core @babel/cli @babel/preset-env @babel/node --dev
$ yarn add nodemon rimraf cross-env --dev
安裝 Apollo GraphQL 與 Babel 所需 package。
$ yarn add apollo-server graphql
安裝 Apollo GraphQL 與 GraphQL。
$ yarn add @babel/core @babel/cli @babel/preset-env @babel/node --dev
安裝 Babel 相關 package,由於只是轉譯用,安裝成開發用的 devDependency
即可。
其中 @babel/node
負責將 ES6+ 轉譯成 Node 能執行的 js。
$ yarn add nodemon rimraf cross-env --dev
安裝 nodemon 可監控檔案修改重新 Babel 轉譯與重啟 Node。
安裝 rimraf 可令 yarn clean
跨平台刪除 dist 目錄。
安裝 cross-env 可跨平台設定 NODE_ENV
環境變數。
Babel Configuration
.babelrc
{
"presets": [
"@babel/preset-env"
]
}
在 project 根目錄下建立 .babelrc
,使用剛剛安裝的 @babel/preset-env
的設定將 ES6+ 轉譯。
Nodemon Configuration
nodemon.json
{
"exec": "cross-env NODE_ENV=development yarn dev",
"watch": ["src/*"],
"ext": "js, json"
}
- exec:當有變動時,將執行
yarn dev
- watch:Nodemon 將持續觀察的目錄
- ext:Nodemon 將持續觀察的 extension
watch
與ext
可視實際需求加以修改
Apollo GraphQL
src/index.js
import { ApolloServer, gql } from 'apollo-server'
let data = [
{ title: 'FP in JavaScript', category: 'FP'},
{ title: 'RxJS in Action', category: 'FRP'},
{ title: 'Speaking JavaScript', category: 'JS'}
]
let typeDefs = gql`
type Query {
books(category: BookCategory!): [Book]
}
type Book {
title: String
category: BookCategory
}
enum BookCategory {
FP
FRP
JS
}
`
let books = (_, { category }) => data.filter(x => x.category === category)
let resolvers = {
Query: {
books
}
}
let apolloServer = new ApolloServer({ typeDefs, resolvers })
apolloServer.listen()
.then(({ url }) => `GraphQL Server ready at ${ url }`)
.then(console.log)
第 1 行
import { ApolloServer, gql } from 'apollo-server';
使用 ES6 的 import
而非 require
。
Yarn Script
package.json
"scripts": {
"serve": "nodemon",
"start": "node ./dist/index.js",
"dev": "babel-node ./src/index.js",
"prod": "yarn clean && cross-env NODE_ENV=production yarn build && yarn start",
"clean": "rimraf dist",
"build": "babel ./src --out-dir dist",
"docker:build": "yarn clean && cross-env NODE_ENV=production yarn build && docker build -t apollo-babel:$npm_package_version .",
"docker:up": "docker-compose up -d",
"docker:down": "docker-compose down"
},
本文最關鍵的就是 Yarn script 配置。
"serve": "nodemon",
yarn serve
執行 nodemon
。
"start": "node ./dist/index.js",
yarn start
使用 node
執行 Babel 轉譯後的 js 並啟動 Apollo GraphQL。
"dev": "babel-node ./src/index.js",
yarn dev
使用 babel-node
將 ES6+ 轉譯成 Node 能執行的 js 在記憶體內,並啟動 Apollo GraphQL。
"prod": "yarn clean && cross-env NODE_ENV=production yarn build && yarn start",
yarn prod
依序執行以下步驟:
yarn clean
刪除dist
目錄yarn build
設定NODE_ENV
為production
,並使用 Babel 轉譯成 Node 能執行的 jsyarn start
使用node
執行 Babel 轉譯後的 js 並啟動 Apollo GraphQL
"clean": "rimraf dist",
yarn clean
刪除 dist
目錄。
"build": "babel ./src --out-dir dist"
yarn build
使用 Babel 將 src
目錄下的 ES6+ 轉譯成 Node 能執行的 js 到 dist
目錄下。
將來要包進 Docker 的 js 也是
dist
目錄,而非src
目錄
"docker:build": "yarn clean && cross-env NODE_ENV=production yarn build && docker build -t $npm_package_name:$npm_package_version .",
yarn docker:build
與 yarn prod
類似,差異只在最後 docker:build
包成 image 而非啟動 Apollo GraphQL。
使用了 $npm_package_name
與 $npm_package_version
直接讀取 package.json
的 name
與 version
。
Summary
雖然 Yarn script 內分的很細,但其實常用的 script 只有 3 個:
yarn serve
:development 時使用,存檔後 Nodemon 會自動 Babel 轉譯,以babel-node
重新啟動 Apollo GraphQLyarn build
:development 時使用,以babel
將src
目錄下所有 js 重新轉譯到dist
目錄下yarn docker:build
:產生 Apollo GraphQL 的 Docker image
Dockerfile
dockerfile
FROM node:lts-alpine
WORKDIR /usr/app
COPY package.json .
RUN yarn install --production
COPY dist/* ./
CMD [ "node", "index.js" ]
在 server
目錄下建立 dockerfile
,由於我們要另外安裝 Apollo GraphQL,且將自己寫的 js 包進 Docker image,因此不可直接使用 docker-compose.yml
。
第 1 行
FROM node:lts-alpine
使用最新 LTS 版的 node:lts-alpine
為基底建立 image。
建議使用
alpine
為 production image,size 會小很多
第 2 行
WORKDIR /usr/app
將 working directory 切換到 /usr/app
,相當於:
mkdir /usr/app
cd /usr/app
稍後 COPY
、RUN
與 CMD
都會在此目錄下。
第 3 行
COPY package.json .
將根目錄的 package.json
複製到 Docker 內的 /usr/app
,因為要使用 yarn install
安裝 Apollo GraphQL。
第 4 行
RUN yarn install --production
執行 yarn install
安裝 dependencies
下的 package。
不需安裝 Babel 所需套件進 Docker,因此加上
--production
第 5 行
COPY dist/* ./
將 Babel 編譯過的 js 複製進 Docker 內的 /usr/app
。
第 6 行
CMD [ "node", "index.js" ]
執行 Docker 內的 /usr/app/index.js
啟動 Apollo GraphQL。
Docker Compose
docker-compose.yml
version: "3"
services:
graphql:
image: apollo-babel:${GQL_TAG}
container_name: MyGraphQL
restart: always
ports:
- ${GQL_PORT}:4000
使用 docker-compose.yml
啟動 Apollo Server。
.env
GQL_TAG=1.0.0
GQL_PORT=4000
設定 docker-compose.yml
的環境變數。
Start Development Server
$ yarn serve
使用 yarn serve
啟動 Apollo GraphQL,適合 development 使用。
Babel Transpile
$ yarn build
使用 yarn build
命令 Babel 將 src
目錄下所有 js 轉譯成 Node 能執行的 js 到 dist
目錄下。
Build Docker Image
$ yarn docker:build
使用 yarn docker:build
命令 Babel 重新轉譯,並切包成 Docker image。
GraphQL Playground
http://localhost:4000
Conclusion
- Apollo GraphQL 也能享受 ES6+ 最新語法與 ES module,只要設定好 Babel 環境即可
Sample Code
完整範例可在我的 GitHub 上找到
Reference
Karim Aya, How to enable ES6 (and beyond) syntax with Node and Express