Hướng dẫn bắt đầu GraphQL với Node.js (Phần 1)

11866

Hầu hết chúng ta có thể rất quen thuộc với việc tạo REST API. GraphQL là một query language được tạo ra bởi Facebook với mục đích xây dựng các ứng dụng cho khách hàng dựa trên cú pháp trực quan và linh hoạt để mô tả các yêu cầu và tương tác dữ liệu. GraphQL được thiết kế để giải quyết một trong những nhược điểm lớn nhất của các API giống như REST. Một dịch vụ GraphQL được tạo ra bằng cách định nghĩa các loại và các trường, sau đó cung cấp các chức năng cho mỗi trường trên mỗi loại.

Một khi dịch vụ GraphQL đang chạy (thường là tại một URL trên web), nó có thể được gửi các truy vấn GraphQL để xác nhận và thực hiện. Truy vấn đã nhận được kiểm tra lần đầu để đảm bảo nó chỉ đề cập đến các loại và trường được xác định, sau đó chạy các hàm để tạo ra kết quả.

#Tính năng của GraphQL 

  • Hierarchical – các truy vấn chính xác như dữ liệu mà chúng trả về.
  • Các truy vấn do khách hàng chỉ định – khách hàng có quyền tự do quyết định lấy gì từ máy chủ,
  • Strongly typed – bạn có thể xác thực một truy vấn theo cú pháp và trong hệ thống kiểu GraphQL trước khi thi hành. Điều này cũng giúp thúc đẩy các công cụ mạnh mẽ để cải thiện trải nghiệm phát triển, như GraphiQL.
  • Introspective – bạn có thể truy vấn hệ thống sử dụng cú pháp GraphQL. Điều này rất tốt cho việc phân tích cú pháp dữ liệu đến vào các giao diện strongly-typed và không phải  phân tích cú pháp và chuyển đổi bằng tay JSON thành các đối tượng.

#Tại sao phải dùng GraphQL

Một trong những thách thức chính đối với các REST call truyền thống là không có khả năng khách hàng yêu cầu một bộ dữ liệu tùy chỉnh (giới hạn hoặc mở rộng). Trong hầu hết các trường hợp, một khi khách hàng yêu cầu thông tin từ máy chủ, nó sẽ nhận tất cả hoặc không có bất kì trường nào.

Một khó khăn khác là làm việc và duy trì nhiều điểm cuối. Khi nền tảng phát triển, do đó số lượng sẽ tăng lên. Vì vậy, khách hàng thường cần phải yêu cầu dữ liệu từ các endpoint khác nhau. GraphQL API được tổ chức theo các loại và trường, chứ không phải endpoint. Bạn có thể truy cập đầy đủ các tính năng của dữ liệu từ một điểm cuối.

Khi xây dựng một máy chủ GraphQL, chỉ cần có một URL cho tất cả dữ liệu tìm nạp và biến đổi. Do đó, khách hàng có thể yêu cầu một bộ dữ liệu bằng cách gửi một chuỗi truy vấn, mô tả những gì họ muốn, đến một máy chủ.

#Cài đặt

Chúng ta sẽ bắt đầu với một cấu trúc tệp đơn giản và một đoạn code mẫu đơn giản. Để làm theo hướng dẫn này, bạn tạo một thư mục GraphQLvà bên trong thư mục khởi tạo một dự án npm sử dụng npm init -y. Sau đó, tạo tệp server.js mà sẽ là tệp chính. Đối với code hoàn chỉnh, sao chép bằng cách sử dụng các hướng dẫn trong kho github đính kèm.

Chúng tôi sẽ cài đặt các package cần thiết.

npm i graphql express express-graphql -S   #Install required packages

Chúng tôi sẽ thiết lập một máy chủ đơn giản dùng Express và Express-Graphql, một máy chủ trung gian HTTP. Đừng lo lắng về ý nghĩa của code bởi vì chúng ta sẽ khám phá từng bước một trong các bước tiếp theo khi thêm nội dung khác.

var express = require('express');
var graphqlHTTP = require('express-graphql');
var { buildSchema } = require('graphql');

// Initialize a GraphQL schema
var schema = buildSchema(`
  type Query {
    hello: String
  }
`);

// Root resolver
var root = { 
  hello: () => 'Hello world!'
};

// Create an express server and a GraphQL endpoint
var app = express();
app.use('/graphql', graphqlHTTP({
  schema: schema,  // Must be provided
  rootValue: root,
  graphiql: true,  // Enable GraphiQL when server endpoint is accessed in browser
}));
app.listen(4000, () => console.log('Now browse to localhost:4000/graphql'));

#Concepts

Cho đến nay chúng ta đã khám phá một số tính năng và lợi thế của GraphQL. Chúng ta sẽ nghiên cứu các thuật ngữ khác nhau và triển khai một số tính năng kỹ thuật trong GraphQL. Chúng tôi sẽ sử dụng một máy chủ Express đơn giản để phân tích các feature.

  Hướng dẫn bắt đầu GraphQL với Node.js (Phần 1)
Xây dựng forum bằng GraphQL, React, Apollo và Prisma – Part 1 :Application Introduction and Technologies Overview”]

Schema

Trong GraphQL, Schema quản lý các truy vấn và đột biến, xác định những gì được phép thực hiện trong máy chủ GraphQL. Một giản đồ định nghĩa một hệ thống kiểu của GraphQL API. Nó mô tả tập hợp các dữ liệu có thể (object, field, relationship, mọi thứ) mà khách hàng có thể truy cập. Cuộc gọi từ máy khách được xác nhận và thực hiện đối với lược đồ. Một khách hàng có thể tìm thông tin về schema thông qua quá trình phân tích. Một schema nằm trên máy chủ GraphQL API.

Ngôn ngữ GraphQL Interface Definition (IDL) hoặc Ngôn ngữ Schema Definition (SDL) là cách ngắn gọn nhất để chỉ định GraphQL Schema. Các component cơ bản nhất của GraphQL schema là các loại đối tượng, chỉ đại diện cho một loại đối tượng mà bạn có thể lấy ra từ dịch vụ của mình và những trường nào nó có. Trong ngôn ngữ GraphQL schema, chúng ta có thể đại diện cho nó như sau:

type User {
  id: ID!
  name: String!
  age: Int
}

Trong đoạn trên, chúng ta đang sử dụng hàm buildSchema để xây dựng một đối tượng Schema từ ngôn ngữ GraphQL schema.

var schema = buildSchema(`
  type Query {
    hello: String
  }
`);

Xây dựng các Type

Bên trong buildSchema, chúng ta có thể định nghĩa các kiểu khác nhau. Bạn có thể nhận thấy trong hầu hết các trường hợp kiểu Query {...} và type Mutation {...}type Query {...} là một đối tượng đang nắm giữ các chức năng sẽ được ánh xạ tới các truy vấn GraphQL; được sử dụng để lấy dữ liệu (tương đương với GET trong REST) trong khi type Mutation {...} chứa các chức năng sẽ được ánh xạ tới mutaions; được sử dụng để tạo, cập nhật hoặc xóa dữ liệu (tương đương với POST, UPDATE và DELETE trong REST).

Chúng tôi sẽ làm cho chema phức tạp hơn bằng cách thêm một số loại hợp lý. Ví dụ: chúng tôi muốn trả lại user và một mảng users có các thuộc tính   idname ,age và gender .

var schema = buildSchema(`
  type Query {
    user(id: Int!): Person
    users(gender: String): [Person]
  },
  type Person {
    id: Int
    name: String
    age: Int
    gender: String  
  }
`);

Bạn có thể thấy một số cú pháp thú vị ở trên, [Person] có nghĩa là trả về một mảng kiểu Person trong khi dấu chấm than trong user(id: Int!) Có nghĩa là id  phải được cung cấp. truy vấn users lấy một biến giới tính tùy chọn.

Resolver

Resolver có trách nhiệm lập bản đồ hoạt động để thực hiện function. Bên trong type Query, chúng tôi có một hoạt động được gọi là users. Chúng tôi xác định hoạt động này đến một function có cùng tên tại root. Chúng tôi cũng sẽ tạo ra một số người dùng giả cho function này.

...
var users = [  // Dummy data
  {
    id: 1,
    name: 'Brian',
    age: '21',
    gender: 'M'
  },
  {
    id:2,
    name: 'Kim',
    age: '22',
    gender: 'M'
  },
  {
    id:3,
    name: 'Joseph',
    age: '23',
    gender: 'M'
  },
  {
    id:3,
    name: 'Faith',
    age: '23',
    gender: 'F'
  },
  {
    id:5,
    name: 'Joy',
    age: '25',
    gender: 'F'
  }
];

var getUser = function(args) { ... }  // Return a single user
var retrieveUsers = function(args) { ... } // Return  a list of users
...
var root = { 
  user: getUser,   // Resolver function to return user with specific id
  users: retrieveUsers
};

Để làm cho code trở nên dễ đọc hơn, chúng ta sẽ tạo các function riêng biệt thay vì xếp tất cả mọi thứ vào bộ root resolver. Cả hai function này lấy một tham số tùy chọn args mang các biến từ truy vấn của khách hàng. Hãy cung cấp một thực hiện cho các resolver và thử nghiệm các function của chúng.

...
var getUser = function(args) { // return a single user based on id
  var userID = args.id;
  return users.filter(user => {
    return user.id == userID;
  })[0];
}

var retrieveUsers = function(args) { // Return a list of users. Takes an optional gender parameter
  if(args.gender) {
    var gender = args.gender;
    return users.filter(user => user.gender === gender);
  } else {
    return users;
  }
}
...

Query

query getSingleUser {
    user {
        name
        age
        gender
    }
}

Output

Trong biểu đồ ở trên, chúng tôi đang sử dụng một hoạt động getSingleUser để có được một người dùng duy nhất với tên, giới tính và độ tuổi. Chúng tôi có thể tùy ý chỉ định rằng chúng tôi cần tên của họ chỉ khi chúng tôi không cần tuổi và giới tính.

Khi có trục trặc trong nhật ký mạng hoặc máy chủ GraphQL, sẽ dễ dàng xác định truy vấn trong codebase bằng tên thay vì cố gắng giải mã nội dung.

Truy vấn này không cung cấp id đã được yêu cầu và GraphQL cung cấp cho chúng tôi một thông báo lỗi mô tả rất. Bây giờ chúng ta sẽ thực hiện một truy vấn chính xác, chú ý việc sử dụng các biến / đối số.

Query

query getSingleUser($userID: Int!) {
    user(id: $userID) {
        name
        age
        gender
    }
}

Variables

{ 
    "userID":1
}

Output

 Còn tiếp…

TopDev via Scotch

  Tại sao bây giờ là thời điểm thích hợp để học GraphQL?