全端網站設計範例:後端新增 Apollo server

本篇為「全端網站架構」中的後端範例及細節。需先建立專案(請參閱上一篇:全端網站設計範例:後端專案建立)。

由於此專案前後端用 GraphQL 作為 API 的形式,考慮到便利性,前後端都會使用 Apollo 所提供的框架。

主要參閱 Get started with Apollo Server 這篇官方教學文來實做。

安裝 Apollo Server

我們使用 Apollo Server 4

1
$ npm install @apollo/server graphql

定義 GraphQL schema

1
$ touch src/schemas.graphql

打開 src/schemas.graphql 並編輯其內容,建立最基本的範例

1
2
3
type Query {
hello: String!
}

定義 Resolver

1
2
$ mkdir src/resolvers
$ touch src/resolvers/index.ts

打開 src/resolvers/index.ts 並編輯其內容,建立最基本的範例

1
2
3
4
5
export default {
Query: {
hello: (): string => 'world',
},
};

建立 Apollo Server

編輯 src/index.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import * as fs from 'fs';
import * as path from 'path';
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import resolvers from './resolvers';

const server = new ApolloServer({
typeDefs: fs.readFileSync(path.join(__dirname, 'schemas.graphql'), 'utf8'),
resolvers,
});

(async () => {
const { url } = await startStandaloneServer(server, { listen: { port: 5000 } });
console.log(`Server is running at ${url}`);
})();

建立一個 Apollo Server 需要提供 GraphQL schema 的定義,由於 .graphql 無法被直接 import,我們用讀檔的方式把 schema 讀進來。另外再將 resolvers 傳入 Apollo Server,最後呼叫 listen() 啟動伺服器監聽即可。

最後瀏覽 http://localhost:5000 (根據環境變數 PORT 調整),在 Apollo Server v3 後的版本中,會導頁至他們的新版 Query Tool - Apollo Sandbox,就可以進行 Query 的開發操作了(原本 v2 中是用 GraphQL Playground 這個 tool)。

ESLint 可能問題

import resolvers from './resolvers' 時遇到 import/no-unresolved

1
error    Unable to resolve path to module './resolvers'  import/no-unresolved

參閱 StackOverflow - Using eslint with typescript - Unable to resolve path to module,在 .eslintrc.json 中加上以下程式碼即可解決問題

1
2
3
4
5
6
7
8
9
{
"settings": {
"import/resolver": {
"node": {
"extensions": [".js", ".jsx", ".ts", ".tsx"]
}
}
}
}

import resolvers from './resolvers' 時遇到 import/extensions

1
error    Missing file extension "ts" for "./resolvers"  import/extensions

參閱 Typescript eslint - Missing file extension “ts” import/extensions,在 .eslintrc.json 中加上以下程式碼即可解決問題

1
2
3
4
5
6
7
8
9
10
11
12
"rules": {
"import/extensions": [
"error",
"ignorePackages",
{
"js": "never",
"jsx": "never",
"ts": "never",
"tsx": "never"
}
]
}

加上 Logging 機制

記錄 Log 在專案中也是必不可少的機制,這裡我們使用 npmlog 來記錄。

1
2
$ npm install npmlog
$ npm install -D @types/npmlog

index.ts 中的 console.log 替換成 npmlog。

1
2
3
4
5
import log from 'npmlog';

server.listen({ port: process.env.PORT || 5000 }).then(({ url }) => {
log.info('index', `Server is running at ${url}`);
});

加上時間

預設的 log 是沒有列出時間的,可以參考這個 issue npmlog - Add option to log timestamp 來加上時間。在 index.ts 中加上以下程式碼

1
2
Object.defineProperty(log, 'heading', { get: () => new Date().toUTCString() });
log.headingStyle = { bg: '', fg: 'white' };

印出的 log 就會像是:Wed, 25 Aug 2021 08:39:51 GMT info index Server is running at http://localhost:5000/

參考資料

  1. Get started with Apollo Server
  2. npmlog

CHANGELOG

日期 敘述
2024-03-04 更新使用的工具版本、建立專案細節
2021-08-20 初版