※ 什麼是Apollo GraphQL Server?
Apollo GraphQL Server 是一個將GraphQL的標準轉化為實際可用的工具和框架,可以在Node.js 常用的中介軟體像是 Express 或 Fastify 所建立的伺服器中,輕鬆加入和設定 Apollo GraphQL Server 來處理 GraphQL 請求。它是一個強大的 GraphQL 引擎,可以連接資料庫、RESTful API 或其他 GraphQL 服務,作為服務的整合介面。Apollo GraphQL Server 提供了許多實用的功能和工具,使開發者能夠輕鬆建立和管理 GraphQL API,並與各種資料源連接。
Apollo GraphQL Server 的主要特點包括:
- 強型別語言:可以明確定義資料型態,型別錯誤會被直接阻擋,並且可以自動產生文件,使程式即文檔。
- 減少資訊短缺或過度獲取:類似 SQL 語言的方式,使用者可以精確自定查詢條件及回傳內容,避免過多或過少的數據傳輸。
- 高相容性:無論是前端使用、與 REST API 混搭開發,還是通過微服務架構統一資料交換接口,都能讓開發更具彈性。

※ 下載Apollo GraphQL Server
npm install apollo-server graphql
★ 新版Apollo GraphQL Server:


※ Apollo GraphQL Server依賴GraphQL的原因主要有以下幾點:
- 核心功能:Apollo Server是專門為GraphQL設計的伺服器,它依賴GraphQL來定義數據結構和查詢語言。
- 解析器函數:GraphQL伺服器使用解析器函數來處理查詢,Apollo Server利用這些解析器函數,從不同的數據源(如資料庫、REST API等)獲取數據。
- 高效查詢:GraphQL允許客戶端查詢所需的精確欄位,減少了不必要的數據傳輸,提升了性能。
- 自動文件化:GraphQL的模式(schema)自帶文件化功能,Apollo Server利用這一特性來提供自動生成的API文檔。
這些特性使得Apollo GraphQL Server能夠高效地處理和管理GraphQL查詢,並提供強大的數據訪問能力。
※ Apollo GraphQL Server透過Nodejs Express來建立自己的應用伺服器,原因有三個:
- 流行性和兼容性:Express是最受歡迎的Node.js網絡框架之一,跟很多其他流行的程式庫都能很好地搭配使用。
- 中介層支持:用Express可以很方便地使用Node.js的中介層來處理一些常見問題,比如速率限制、安全性和身份驗證。
- 靈活性:透過apollo-server-express,可以同時提供REST和GraphQL服務。
- 簡單設置:把Apollo Server當作中介層應用在Express HTTP實例中,設置起來相對簡單。

※ Apollo GraphQL Server 提供了一些工具和功能來輔助 GraphQL 的使用。以下是一些主要的輔助工具和功能:
- Apollo Studio:這是一個圖形化介面,可以更方便地管理和監控 GraphQL 服務。它提供查詢性能分析、錯誤追蹤和使用情況報告等功能。
- Apollo Client:這是一個用來與 GraphQL 服務互動的 JavaScript 客戶端。它簡化了查詢、變更和訂閱的過程,並提供快取機制來提升性能。
- Apollo Server:這是一個用來建構 GraphQL API 的伺服器框架。它與 Express、Koa 等 Node.js 框架相容,並提供許多中間件和外掛來擴展功能。
- Apollo Federation:這是一個用來建構分佈式 GraphQL 架構的工具。它允許你將多個 GraphQL 服務合併成一個統一的 GraphQL 閘道,達到簡化認證機制、提升查詢優化和快取性能。

※ Apollo Server 時所需的依賴和工具:主要包括:
- @apollo/server:這是 Apollo Server 本身的主要套件。它負責將 HTTP 請求和回應轉換為 GraphQL 操作,並在可擴展的環境中運行這些操作,支持外掛和其他功能。
- graphql:也稱為 graphql-js,是實現核心 GraphQL 解析和執行算法的套件。這個套件是 Apollo Server 的對等依賴項。

※ 在 Apollo Server 中,typeDefs 和 resolvers 是兩個非常重要的參數,它們共同定義了你的 GraphQL API 的結構和行為。
★ typeDefs :用來定義 GraphQL schema ,目的是描述數據結構和查詢操作。
例如:
這段代碼展示了如何使用 typeDefs 來定義 GraphQL schema,包括查詢、變更和輸入/輸出類型。
const typeDefs = gql`
type User {
id: ID!
email(text: String): String
name: String
}
type Query {
user: User
}
input UpdateUserInputType {
id: ID!
name: String
}
type UpdateUserPayload {
payload: User
status: String
message:String
errors: [String]
}
type Mutation {
updateUser(input: UpdateUserInputType): UpdateUserPayload
}
`
★ resolvers :是用來處理實際查詢的函數。每個 resolver 函數負責填充 schema 中對應字段的數據。它可以從數據庫、第三方 API 或其他來源獲取數據。
例如:
這段代碼展示了如何使用 resolvers 來處理查詢和變更操作。
const resolvers = {
Query: {
user: (root, args, context, info) => {
return context.database.user
},
},
Mutation: {
updateUser: (root, args, context, info) => {
const {auth} = context
if (!auth) {
return {
status: 401,
message: "unauthorization"
}
}
const {id, name} = args.input
if (parseInt(id, 10) === user.id) {
user['name'] = name
}
return {
payload: user,
status: 200,
message: "Success",
errors: null
}
}
},
User: {
email: (parent, args, context, info) => {
const {text} = args
if (user.email.indexOf(text) >= 0) {
return parent.email + text
}
return null
}
}
}

※ 在 Apollo Server 中,typeDefs 負責定義所有的 GraphQL 類型,包括查詢(Query)和變更(Mutation)。這些類型定義了你的 GraphQL API 的結構和操作。通常,typeDefs 會搭配 Apollo GraphQL Server 提供的 gql 標籤模板字面量來撰寫。
※ 使用 gql 標籤模板字面量(tagged template literal)來定義 typeDefs 是一種常見的做法。主要原因:
- 語法標示:使用
gql標籤模板字面量可以讓編輯器識別出這段代碼是 GraphQL schema。編輯器可對代碼進行不同顏色標示,以提高代碼的可讀性和編寫效率。 - 語法檢查:
gql標籤模板字面量會在編譯時對 GraphQL schema 進行語法檢查,及早發現並修正語法錯誤。 - 可讀性:使用
gql標籤模板字面量將 GraphQL schema 與其他 JavaScript 代碼區分開來,方便閱讀。 - 模板字面量:JavaScript 的模板字面量允許在字符串中嵌入變量和表達式,這對於動態生成 GraphQL schema 非常有用。

※ Apollo GraphQL Server完整的請求處理過程:
- 接收請求:Apollo Server 接收到這個查詢請求。
- 解析查詢:伺服器解析查詢,確定需要哪些數據和操作。
- 執行解析器函數:伺服器調用對應的解析器函數(resolvers)來處理查詢或變更。 解析器函數負責從數據庫或其他數據源獲取所需的數據,並將結果返回給伺服器。
- 生成回應:伺服器將獲取到的 數據組裝成 GraphQL 回應,並返回給客戶端。

※ 當你在 Apollo GraphQL Server 中加入 updateUser 這樣的變更操作時,必須在 typeDefs 中定義對應的 Mutation 類型,並在解析器中實現具體的變更邏輯。這樣,伺服器才能正確處理和響應這個變更請求。
以下這段程式碼展示了如何定義一個變更操作 updateUser,包括輸入類型 UpdateUserInputType 和返回類型 UpdateUserPayload。這樣的設計可以讓客戶端發送變更請求來更新使用者數據,並獲取操作的結果和相關訊息。

※ 在 Apollo GraphQL Server 中,添加 Mutation 和 updateUser 解析器函數的主要目的是處理客戶端發送的變更請求,並更新伺服器上的數據。
在這個範例中:
const resolvers = {
Mutation: {
updateUser: (root, args, context, info) => {
const { auth } = context;
if (!auth) {
return {
status: 401,
message: "unauthorization",
};
}
const { id, name } = args.input;
if (parseInt(id, 10) === user.id) {
user['name'] = name;
}
return {
payload: user,
status: 200,
message: "Success",
errors: null,
};
},
},
};
- 處理變更請求:當客戶端發送
updateUser變更請求時,伺服器會調用updateUser解析器。 - 驗證和授權:解析器檢查
context中的auth狀態,如果未授權則返回 401 狀態。 - 更新數據:解析器函數根據輸入的
id和name更新對應的使用者數據。 - 返回結果:解析器函數返回更新後的使用者數據、操作狀態和相關訊息。

※ 在 Apollo GraphQL Server 中,updateUser: (root, args, context, info) 是一個解析器函數,用於處理 updateUser 變更操作。
解析這個函數的參數:
- root:這個參數通常是上一層解析器函數的返回結果。如果沒有上一層解析器函數,這個參數通常是
null或undefined一個。例如,如果你查詢user,返回一個User對象,那麼在User類型的解析器函數中,root就是這個User對象。 - args(arguments參數):包含客戶端傳遞給這個變更操作的參數。在這個例子中,
args.input包含了UpdateUserInputType類型的數據。 - context:包含請求的上下文信息,例如認證狀態、數據庫連接等。在這個例子中,我們使用
context.auth來檢查用戶是否已經授權。 - info:包含查詢的執行信息和 schema 詳細信息,通常用於進階的查詢處理。

※ 在 Apollo GraphQL Server 中,應用程序的最後一步就是確保伺服器在特定的網絡端口上運行,啟動伺服器並讓它開始接收和處理來自客戶端的請求。

※ Apollo GraphQL Server 的基本架構可以分為以下幾個步驟:
- 引用
apollo-server:首先需要安裝並引用apollo-server套件。 - 定義 Schema:使用 GraphQL 定義 Schema,描述數據結構和查詢方式。
- 定義 Resolvers:定義 Resolvers 來處理查詢和變更,這些函數負責從數據源中獲取數據。
- 創建伺服器實例:使用
ApolloServer創建伺服器實例,並將 Schema 和 Resolvers 傳遞給伺服器。 - 指定端口並啟動伺服器:指定伺服器運行的端口,然後啟動伺服器。
※ 展示Apollo GraphQL Server 的基本架構(簡易版):
const { ApolloServer, gql } = require('apollo-server');
// 定義 schema
const typeDefs = gql`
type Query {
hello: String
}
`;
// 定義 resolvers
const resolvers = {
Query: {
hello: () => 'Hello world!',
},
};
// 創建伺服器實例
const server = new ApolloServer({ typeDefs, resolvers });
// 指定端口並啟動伺服器
server.listen({ port: 4000 }).then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
※ 展示Apollo GraphQL Server 的基本架構(複雜版):
const { ApolloServer, gql } = require('apollo-server');
// 模擬的資料庫
const database = {
user: {
id: 1,
name: 'Whien',
email: 'sal9561@gmail.com',
friends: ['Bob', 'Tom'],
work_state: 1,
created_at: new Date(),
updated_at: new Date()
}
};
// 模擬的使用者資料
const user = {
id: 1,
name: 'Whien',
email: 'sal9561@gmail.com',
friends: ['Bob', 'Tom'],
work_state: 1,
created_at: new Date(),
updated_at: new Date()
};
// 定義 GraphQL schema
const typeDefs = gql`
type User {
id: ID!
email(text: String): String
name: String
}
type Query {
user: User
}
input UpdateUserInputType {
id: ID!
name: String
}
type UpdateUserPayload {
payload: User
status: String
message: String
errors: [String]
}
type Mutation {
updateUser(input: UpdateUserInputType): UpdateUserPayload
}
`;
// 定義 resolvers
const resolvers = {
Query: {
user: (root, args, context, info) => {
return context.database.user;
},
},
Mutation: {
updateUser: (root, args, context, info) => {
const { auth } = context;
if (!auth) {
return {
status: 401,
message: "unauthorization"
};
}
const { id, name } = args.input;
if (parseInt(id, 10) === user.id) {
user['name'] = name;
}
return {
payload: user,
status: 200,
message: "Success",
errors: null
};
}
},
User: {
email: (parent, args, context, info) => {
const { text } = args;
if (user.email.indexOf(text) >= 0) {
return parent.email + text;
}
return null;
}
}
};
// 創建 Apollo 伺服器實例
const server = new ApolloServer({
typeDefs,
resolvers,
context: () => {
return {
auth: false,
database
};
}
});
// 指定端口並啟動伺服器
server.listen(8891, () => {
console.log('Apollo GraphQL Server run on port 8891');
});









