更新於 2024/10/03閱讀時間約 21 分鐘

GraphQL(二):Apollo GraphQL Server應用

    ※ 什麼是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 的主要特點包括:

    1. 強型別語言:可以明確定義資料型態,型別錯誤會被直接阻擋,並且可以自動產生文件,使程式即文檔。
    2. 減少資訊短缺或過度獲取:類似 SQL 語言的方式,使用者可以精確自定查詢條件及回傳內容,避免過多或過少的數據傳輸。
    3. 高相容性:無論是前端使用、與 REST API 混搭開發,還是通過微服務架構統一資料交換接口,都能讓開發更具彈性。


    ※ 下載Apollo GraphQL Server

    npm install apollo-server graphql

    ★ 新版Apollo GraphQL Server:


    ※ Apollo GraphQL Server依賴GraphQL的原因主要有以下幾點:

    1. 核心功能:Apollo Server是專門為GraphQL設計的伺服器,它依賴GraphQL來定義數據結構和查詢語言。
    2. 解析器函數:GraphQL伺服器使用解析器函數來處理查詢,Apollo Server利用這些解析器函數,從不同的數據源(如資料庫、REST API等)獲取數據。
    3. 高效查詢:GraphQL允許客戶端查詢所需的精確欄位,減少了不必要的數據傳輸,提升了性能。
    4. 自動文件化:GraphQL的模式(schema)自帶文件化功能,Apollo Server利用這一特性來提供自動生成的API文檔。

    這些特性使得Apollo GraphQL Server能夠高效地處理和管理GraphQL查詢,並提供強大的數據訪問能力。

    ※ Apollo GraphQL Server透過Nodejs Express來建立自己的應用伺服器,原因有三個:

    1. 流行性和兼容性:Express是最受歡迎的Node.js網絡框架之一,跟很多其他流行的程式庫都能很好地搭配使用。
    2. 中介層支持:用Express可以很方便地使用Node.js的中介層來處理一些常見問題,比如速率限制、安全性和身份驗證。
    3. 靈活性:透過apollo-server-express,可以同時提供REST和GraphQL服務。
    4. 簡單設置:把Apollo Server當作中介層應用在Express HTTP實例中,設置起來相對簡單。


    ※ Apollo GraphQL Server 提供了一些工具和功能來輔助 GraphQL 的使用。以下是一些主要的輔助工具和功能:

    1. Apollo Studio:這是一個圖形化介面,可以更方便地管理和監控 GraphQL 服務。它提供查詢性能分析、錯誤追蹤和使用情況報告等功能。
    2. Apollo Client:這是一個用來與 GraphQL 服務互動的 JavaScript 客戶端。它簡化了查詢、變更和訂閱的過程,並提供快取機制來提升性能。
    3. Apollo Server:這是一個用來建構 GraphQL API 的伺服器框架。它與 Express、Koa 等 Node.js 框架相容,並提供許多中間件和外掛來擴展功能。
    4. 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 是一種常見的做法。主要原因:

    1. 語法標示:使用 gql 標籤模板字面量可以讓編輯器識別出這段代碼是 GraphQL schema。編輯器可對代碼進行不同顏色標示,以提高代碼的可讀性和編寫效率。
    2. 語法檢查:gql 標籤模板字面量會在編譯時對 GraphQL schema 進行語法檢查,及早發現並修正語法錯誤。
    3. 可讀性:使用 gql 標籤模板字面量將 GraphQL schema 與其他 JavaScript 代碼區分開來,方便閱讀。
    4. 模板字面量:JavaScript 的模板字面量允許在字符串中嵌入變量和表達式,這對於動態生成 GraphQL schema 非常有用。


    ※ Apollo GraphQL Server完整的請求處理過程:

    1. 接收請求Apollo Server 接收到這個查詢請求。
    2. 解析查詢伺服器解析查詢,確定需要哪些數據和操作。
    3. 執行解析器函數伺服器調用對應的解析器函數(resolvers)來處理查詢或變更。 解析器函數負責從數據庫或其他數據源獲取所需的數據,並將結果返回給伺服器。
    4. 生成回應伺服器將獲取到的 數據組裝成 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,
    };
    },
    },
    };
    1. 處理變更請求當客戶端發送 updateUser 變更請求時,伺服器會調用 updateUser 解析器。
    2. 驗證和授權:解析器檢查 context 中的 auth 狀態,如果未授權則返回 401 狀態。
    3. 更新數據:解析器函數根據輸入的 id 和 name 更新對應的使用者數據。
    4. 返回結果:解析器函數返回更新後的使用者數據、操作狀態和相關訊息。


    ※ 在 Apollo GraphQL Server 中,updateUser: (root, args, context, info) 是一個解析器函數,用於處理 updateUser 變更操作。

    解析這個函數的參數:

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


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


    ※ Apollo GraphQL Server 的基本架構可以分為以下幾個步驟:

    1. 引用 apollo-server:首先需要安裝並引用 apollo-server 套件。
    2. 定義 Schema:使用 GraphQL 定義 Schema,描述數據結構和查詢方式。
    3. 定義 Resolvers:定義 Resolvers 來處理查詢和變更,這些函數負責從數據源中獲取數據。
    4. 創建伺服器實例:使用 ApolloServer 創建伺服器實例,並將 Schema 和 Resolvers 傳遞給伺服器。
    5. 指定端口並啟動伺服器:指定伺服器運行的端口,然後啟動伺服器。

    ※ 展示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');
    });



    分享至
    成為作者繼續創作的動力吧!
    © 2024 vocus All rights reserved.