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:
- Schema: Mô tả các loại dữ liệu (types) và các truy vấn (query/mutation).
- Resolvers: Logic xử lý truy vấn và trả về dữ liệu.
- 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
-
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.
-
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ũ.
-
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
- Node.js:
- Apollo Server: Framework phổ biến nhất cho GraphQL.
- Express-GraphQL: Dễ dàng tích hợp với Express.js.
- Ruby on Rails: GraphQL Ruby
- Python: Graphene
b. Ví dụ với Apollo Server
- Cài đặt:
npm install apollo-server graphql
- 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
-
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.
-
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 độ.
-
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
-
Cài đặt công cụ cần thiết:
- Node.js và npm: Tải từ Node.js Official Site.
- IDE: Sử dụng VSCode hoặc WebStorm.
-
Tạo dự án Node.js:
mkdir graphql-api cd graphql-api npm init -y
-
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
-
Tạo file chính (
index.js
):touch index.js
-
Đị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}`); });
-
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
-
Thêm SQLite hoặc PostgreSQL: Cài đặt thư viện ORM như Sequelize:
npm install sequelize sqlite3
-
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();
-
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
-
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), }, };
-
Pagination: Giới hạn số lượng dữ liệu trả về với
limit
vàoffset
:const resolvers = { Query: { getTodos: async (_, { limit = 10, offset = 0 }) => await Todo.findAll({ limit, offset }), }, };
-
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
-
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 }; }, });
-
Phân quyền (Authorization):
- Xác định quyền truy cập dựa trên vai trò (roles).
-
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
-
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"); });
- Sử dụng Jest:
-
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.
-
Monitoring:
- Tích hợp Apollo Studio để giám sát hiệu suất API.