+1

API GraphQL: Kiến thức đầy đủ và chi tiết

API GraphQL: Kiến thức đầy đủ và chi tiết

GraphQL là một ngôn ngữ truy vấn và runtime để thực hiện các truy vấn dữ liệu API, được phát triển bởi Facebook vào năm 2012 và phát hành mã nguồn mở vào năm 2015. Dưới đây là hướng dẫn chi tiết về GraphQL, từ cơ bản đến nâng cao.


1. Khái niệm cơ bản về GraphQL

a. GraphQL là gì?

GraphQL là một ngôn ngữ truy vấn cho API, cho phép khách hàng (client) yêu cầu chính xác dữ liệu mà họ cần. Nó thay thế cho kiến trúc REST truyền thống với những ưu điểm:

  • Chỉ trả về dữ liệu cần thiết, không thừa hoặc thiếu.
  • Cho phép truy vấn dữ liệu từ nhiều nguồn chỉ với một yêu cầu.
  • Hỗ trợ cấu trúc dữ liệu phân cấp (nested).

b. Kiến trúc GraphQL

GraphQL bao gồm ba phần chính:

  1. Schema: Mô tả các loại dữ liệu (types) và các truy vấn (query/mutation).
  2. Resolvers: Logic xử lý truy vấn và trả về dữ liệu.
  3. Runtime: Môi trường thực thi truy vấn GraphQL.

2. Các thành phần trong GraphQL

a. Schema

Schema là trung tâm của GraphQL API, định nghĩa:

  • Query: Lấy dữ liệu.
  • Mutation: Ghi dữ liệu hoặc thay đổi trạng thái.
  • Subscription: Nhận thông báo thời gian thực.

Ví dụ schema cơ bản:

type Todo {
  id: ID!
  title: String!
  completed: Boolean!
}

type Query {
  getTodos: [Todo]
}

type Mutation {
  addTodo(title: String!): Todo
}

b. Query

Query được sử dụng để lấy dữ liệu từ API:

query {
  getTodos {
    id
    title
    completed
  }
}

c. Mutation

Mutation thực hiện các thay đổi dữ liệu:

mutation {
  addTodo(title: "Learn GraphQL") {
    id
    title
    completed
  }
}

d. Subscription

Subscription cho phép nhận dữ liệu thời gian thực khi có thay đổi:

subscription {
  onTodoAdded {
    id
    title
  }
}

e. Resolvers

Resolvers chứa logic xử lý truy vấn:

const resolvers = {
  Query: {
    getTodos: () => [...], // Lấy danh sách todos
  },
  Mutation: {
    addTodo: (_, { title }) => {
      const newTodo = { id: "1", title, completed: false };
      return newTodo;
    },
  },
};

3. Lợi ích của GraphQL

  1. Hiệu quả hơn REST API:

    • Với REST, có thể cần nhiều endpoint để lấy dữ liệu khác nhau.
    • Với GraphQL, chỉ cần một endpoint và yêu cầu đúng dữ liệu cần thiết.
  2. Dễ mở rộng và duy trì:

    • Schema rõ ràng, dễ dàng thêm loại dữ liệu mới mà không ảnh hưởng tới client cũ.
  3. Phù hợp cho ứng dụng phức tạp:

    • Đặc biệt hiệu quả trong hệ thống phân tán hoặc microservices.

4. Những khái niệm nâng cao

a. Fragments

Tái sử dụng truy vấn trong GraphQL:

fragment TodoFields on Todo {
  id
  title
  completed
}

query {
  getTodos {
    ...TodoFields
  }
}

b. Variables

Sử dụng biến để truyền tham số động:

query GetTodoById($id: ID!) {
  getTodoById(id: $id) {
    id
    title
    completed
  }
}

Dữ liệu biến:

{
  "id": "1"
}

c. Directives

Thêm điều kiện vào truy vấn:

query ($includeCompleted: Boolean!) {
  getTodos {
    id
    title
    completed @include(if: $includeCompleted)
  }
}

d. Batching và Caching

  • Batching: Nhiều truy vấn nhỏ có thể được nhóm lại thành một yêu cầu duy nhất.
  • Caching: GraphQL không hỗ trợ caching sẵn, nhưng có thể tích hợp với các thư viện như Apollo Client.

5. Triển khai GraphQL API

a. Các thư viện phổ biến

b. Ví dụ với Apollo Server

  1. Cài đặt:
    npm install apollo-server graphql
    
  2. Tạo server:
    const { ApolloServer, gql } = require("apollo-server");
    
    // Định nghĩa schema
    const typeDefs = gql`
      type Todo {
        id: ID!
        title: String!
        completed: Boolean!
      }
    
      type Query {
        getTodos: [Todo]
      }
    
      type Mutation {
        addTodo(title: String!): Todo
      }
    `;
    
    // Resolvers
    const resolvers = {
      Query: {
        getTodos: () => [{ id: "1", title: "Learn GraphQL", completed: false }],
      },
      Mutation: {
        addTodo: (_, { title }) => ({ id: "2", title, completed: false }),
      },
    };
    
    // Tạo server
    const server = new ApolloServer({ typeDefs, resolvers });
    
    // Khởi chạy server
    server.listen().then(({ url }) => {
      console.log(`🚀 Server ready at ${url}`);
    });
    

c. Triển khai GraphQL với AWS AppSync

  • Sử dụng AWS AppSync để tạo GraphQL API mà không cần quản lý backend.

6. Công cụ hỗ trợ GraphQL

a. GraphQL Playground/GraphiQL

  • Môi trường trực quan để thử nghiệm các truy vấn GraphQL.

b. Apollo Client

  • Thư viện frontend mạnh mẽ cho React, Angular, Vue.js.

c. Relay

  • Framework frontend của Facebook tối ưu cho GraphQL.

d. Postman

  • Hỗ trợ kiểm tra GraphQL API dễ dàng.

7. Lưu ý khi sử dụng GraphQL

  1. Bảo mật:

    • Hạn chế depth limit để tránh truy vấn phức tạp.
    • Kiểm tra xác thực và phân quyền trong resolvers.
  2. Hiệu năng:

    • Sử dụng DataLoader để giảm số lượng truy vấn database.
    • Tích hợp caching để cải thiện tốc độ.
  3. Kiểm thử API:

    • Sử dụng các công cụ như Jest, Mocha để kiểm thử resolvers.

8. So sánh GraphQL và REST API

Tiêu chí GraphQL REST API
Truy vấn dữ liệu Linh hoạt, chỉ yêu cầu dữ liệu cần Phải nhận toàn bộ dữ liệu
Endpoint Một endpoint duy nhất Nhiều endpoint
Thời gian thực Hỗ trợ Subscription Cần thiết lập riêng
Caching Không hỗ trợ sẵn, cần thêm thư viện Dễ dàng với HTTP headers

Hướng dẫn triển khai và tối ưu hóa GraphQL API chi tiết

Dưới đây là hướng dẫn cụ thể hơn để triển khai GraphQL API và tối ưu hóa hiệu năng, bảo mật.


1. Triển khai GraphQL API chi tiết

A. Môi trường phát triển

  1. Cài đặt công cụ cần thiết:

  2. Tạo dự án Node.js:

    mkdir graphql-api
    cd graphql-api
    npm init -y
    
  3. Cài đặt thư viện:

    • Apollo Server để triển khai GraphQL.
    • GraphQL để định nghĩa schema.
    npm install apollo-server graphql
    

B. Xây dựng API

  1. Tạo file chính (index.js):

    touch index.js
    
  2. Định nghĩa schema:

    const { ApolloServer, gql } = require("apollo-server");
    
    // Schema định nghĩa
    const typeDefs = gql`
      type Todo {
        id: ID!
        title: String!
        completed: Boolean!
      }
    
      type Query {
        getTodos: [Todo]
        getTodoById(id: ID!): Todo
      }
    
      type Mutation {
        addTodo(title: String!, completed: Boolean!): Todo
        updateTodo(id: ID!, completed: Boolean!): Todo
        deleteTodo(id: ID!): String
      }
    `;
    
    // Dữ liệu mẫu
    const todos = [
      { id: "1", title: "Learn GraphQL", completed: false },
      { id: "2", title: "Build API", completed: false },
    ];
    
    // Resolvers
    const resolvers = {
      Query: {
        getTodos: () => todos,
        getTodoById: (_, { id }) => todos.find((todo) => todo.id === id),
      },
      Mutation: {
        addTodo: (_, { title, completed }) => {
          const newTodo = { id: String(todos.length + 1), title, completed };
          todos.push(newTodo);
          return newTodo;
        },
        updateTodo: (_, { id, completed }) => {
          const todo = todos.find((todo) => todo.id === id);
          if (todo) todo.completed = completed;
          return todo;
        },
        deleteTodo: (_, { id }) => {
          const index = todos.findIndex((todo) => todo.id === id);
          if (index > -1) {
            todos.splice(index, 1);
            return `Todo with id ${id} deleted`;
          }
          return `Todo not found`;
        },
      },
    };
    
    // Khởi tạo server
    const server = new ApolloServer({ typeDefs, resolvers });
    
    // Lắng nghe server
    server.listen().then(({ url }) => {
      console.log(`🚀 Server ready at ${url}`);
    });
    
  3. Chạy server:

    node index.js
    

    Truy cập vào URL (thường là http://localhost:4000) để thử nghiệm với GraphQL Playground.


C. Mở rộng API với database

  1. Thêm SQLite hoặc PostgreSQL: Cài đặt thư viện ORM như Sequelize:

    npm install sequelize sqlite3
    
  2. Tạo kết nối và bảng:

    const { Sequelize, DataTypes } = require("sequelize");
    
    // Kết nối database
    const sequelize = new Sequelize("sqlite:database.sqlite");
    
    // Định nghĩa bảng
    const Todo = sequelize.define("Todo", {
      title: { type: DataTypes.STRING, allowNull: false },
      completed: { type: DataTypes.BOOLEAN, defaultValue: false },
    });
    
    // Khởi tạo bảng
    sequelize.sync();
    
  3. Cập nhật Resolvers:

    const resolvers = {
      Query: {
        getTodos: async () => await Todo.findAll(),
        getTodoById: async (_, { id }) => await Todo.findByPk(id),
      },
      Mutation: {
        addTodo: async (_, { title, completed }) =>
          await Todo.create({ title, completed }),
        updateTodo: async (_, { id, completed }) => {
          const todo = await Todo.findByPk(id);
          if (todo) {
            todo.completed = completed;
            await todo.save();
          }
          return todo;
        },
        deleteTodo: async (_, { id }) => {
          const deleted = await Todo.destroy({ where: { id } });
          return deleted ? `Todo with id ${id} deleted` : `Todo not found`;
        },
      },
    };
    

2. Tối ưu hóa GraphQL API

A. Hiệu suất

  1. Batching với DataLoader: DataLoader giúp nhóm các truy vấn để giảm số lần truy cập database.

    npm install dataloader
    
    const DataLoader = require("dataloader");
    
    const todoLoader = new DataLoader(async (ids) => {
      const todos = await Todo.findAll({ where: { id: ids } });
      return ids.map((id) => todos.find((todo) => todo.id === id));
    });
    
    const resolvers = {
      Query: {
        getTodoById: async (_, { id }) => await todoLoader.load(id),
      },
    };
    
  2. Pagination: Giới hạn số lượng dữ liệu trả về với limitoffset:

    const resolvers = {
      Query: {
        getTodos: async (_, { limit = 10, offset = 0 }) =>
          await Todo.findAll({ limit, offset }),
      },
    };
    
  3. Caching:

    • Sử dụng Redis hoặc Memcached để lưu trữ kết quả truy vấn.
    • Kết hợp với Apollo Cache.

B. Bảo mật

  1. Xác thực (Authentication): Sử dụng JWT hoặc OAuth:

    const { AuthenticationError } = require("apollo-server");
    
    const resolvers = {
      Query: {
        getTodos: (_, __, { user }) => {
          if (!user) throw new AuthenticationError("Not authenticated");
          return Todo.findAll();
        },
      },
    };
    
    const server = new ApolloServer({
      typeDefs,
      resolvers,
      context: ({ req }) => {
        const token = req.headers.authorization || "";
        const user = verifyToken(token); // Hàm xác thực
        return { user };
      },
    });
    
  2. Phân quyền (Authorization):

    • Xác định quyền truy cập dựa trên vai trò (roles).
  3. Depth Limit: Giới hạn độ sâu của truy vấn để tránh query quá tải:

    npm install graphql-depth-limit
    
    const depthLimit = require("graphql-depth-limit");
    
    const server = new ApolloServer({
      typeDefs,
      resolvers,
      validationRules: [depthLimit(5)],
    });
    

3. Công cụ và quy trình bổ sung

  1. Kiểm thử API:

    • Sử dụng Jest:
      npm install jest
      
    • Viết kiểm thử:
      test("addTodo mutation", async () => {
        const ADD_TODO = `
          mutation {
            addTodo(title: "Test", completed: false) {
              id
              title
            }
          }
        `;
        const response = await server.executeOperation({ query: ADD_TODO });
        expect(response.data.addTodo.title).toBe("Test");
      });
      
  2. Triển khai lên đám mây:

    • Heroku: Dễ triển khai GraphQL server.
    • AWS AppSync: Tích hợp sẵn GraphQL và DynamoDB.
  3. Monitoring:

    • Tích hợp Apollo Studio để giám sát hiệu suất API.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí