Interview Prep
PracticePortfolio

Categories

  • HTML & CSS41
  • JavaScript40
  • TypeScript42
  • ReactJS40
  • Next.js44
  • Node.js41
  • NestJS44
  • SQL & Database43
  • MongoDB40
  • Git40
  • Docker44
  • GraphQL47
Interview PrepGraphQL

GraphQL

47 questions

20 / 47 questions

1

GraphQL là một query language cho API và một runtime để thực thi các query đó dựa trên schema bạn định nghĩa. Client tự khai báo chính xác dữ liệu cần và nhận về đúng shape mong muốn. Nó không phụ thuộc database hay ngôn ngữ cụ thể — chỉ là một lớp hợp đồng (contract) giữa client và server.

2

REST có nhiều endpoint cố định, dễ over-fetch/under-fetch. GraphQL có một endpoint và client tự khai báo chính xác dữ liệu cần qua query, server trả đúng shape đó. Đổi lại GraphQL phức tạp hơn ở caching và cần chú ý bài toán N+1 ở resolver.

3

Over-fetching là khi endpoint trả nhiều field hơn client cần, lãng phí băng thông. Under-fetching là khi một endpoint không đủ dữ liệu nên client phải gọi thêm nhiều request (waterfall). GraphQL giải quyết cả hai vì client chỉ liệt kê đúng field cần trong một query duy nhất.

4

Schema là hợp đồng mô tả toàn bộ type và field mà API cung cấp; đây là single source of truth. SDL (Schema Definition Language) là cú pháp text để viết schema đó.

type User {
  id: ID!
  name: String!
  posts: [Post!]!
}
5

Các nhóm chính: Scalar (Int, Float, String, Boolean, ID), Object (tập hợp field), Enum (tập giá trị cố định), Interface và Union (đa hình), và Input (dùng làm argument). Ngoài ra có hai root type đặc biệt là Query và Mutation (và Subscription).

6

! đánh dấu field non-null (bắt buộc có giá trị). [] là list. Vị trí ! rất quan trọng:

posts: [Post!]!

[Post!]! nghĩa là list không được null và mỗi phần tử cũng không null. [Post] cho phép cả list null lẫn phần tử null.

7

Scalar là type lá (leaf), không có field con — nó biểu diễn một giá trị cụ thể. GraphQL định nghĩa sẵn 5 scalar: Int, Float, String, Boolean và ID. ID là chuỗi định danh duy nhất, thường được serialize như String nhưng mang ý nghĩa khác biệt về mặt ngữ nghĩa.

8

Enum giới hạn một field chỉ nhận một trong tập giá trị cố định, giúp validate ngay ở tầng schema.

enum Role {
  ADMIN
  EDITOR
  VIEWER
}

Client chỉ được truyền/nhận đúng các giá trị này, tránh chuỗi tự do sai chính tả.

9

Interface định nghĩa một tập field bắt buộc mà nhiều Object type khác nhau phải implement. Nó dùng khi các type chia sẻ field chung.

interface Node {
  id: ID!
}
type User implements Node { id: ID! name: String! }

Query trên interface có thể trả về bất kỳ type nào implement nó.

10

Union cho phép một field trả về một trong nhiều Object type không nhất thiết chia sẻ field chung.

union SearchResult = User | Post | Comment

Khác interface ở chỗ union không ép các member phải có field chung — client phải dùng inline fragment (... on User) để lấy field cụ thể.

11

Interface dùng khi các type có field chung và bạn muốn ép chúng cùng implement — có thể query field chung trực tiếp. Union dùng khi các type không liên quan về field nhưng có thể xuất hiện ở cùng một chỗ; bạn buộc phải dùng inline fragment ... on Type để đọc field. Nói ngắn gọn: interface = is-a có contract chung, union = one-of không contract.

12

Query để đọc dữ liệu (read-only, có thể chạy song song). Mutation để ghi/thay đổi dữ liệu (chạy tuần tự theo thứ tự khai báo). Subscription để nhận dữ liệu realtime khi server push event, thường qua WebSocket. Cả ba đều là root type nhưng có ngữ nghĩa thực thi khác nhau.

13

Resolver là hàm chịu trách nhiệm trả về giá trị cho một field trong schema. Nó nhận 4 tham số: (parent, args, context, info).

const resolvers = {
  Query: {
    user: (parent, args, ctx) => ctx.db.user.findById(args.id),
  },
};

Mỗi field có thể có resolver riêng; nếu không, GraphQL dùng resolver mặc định.

14

GraphQL resolve theo cây: kết quả của resolver cha được truyền làm parent cho resolver con — gọi là resolver chain. Nếu một field không có resolver riêng, default resolver sẽ đọc property cùng tên từ parent (hoặc gọi nó nếu là function). Nhờ vậy, phần lớn field đơn giản không cần viết resolver thủ công.

15

Arguments là tham số truyền vào field để lọc/định hình kết quả. Variables giúp truyền giá trị động, tránh nối chuỗi và cho phép cache query.

query GetUser($id: ID!) {
  user(id: $id) { name }
}

Giá trị của $id được gửi riêng trong phần variables của request.

16

Input type là type chuyên dùng làm argument (thường cho mutation), gom nhiều field vào một object.

input CreateUserInput {
  name: String!
  email: String!
}
type Mutation { createUser(input: CreateUserInput!): User! }

Khác Object type ở chỗ input chỉ chứa scalar/enum/input khác, không có resolver và không dùng làm output.

17

Fragment là một tập field tái sử dụng được, tránh lặp lại khi nhiều query cần cùng shape.

fragment UserFields on User {
  id
  name
  avatarUrl
}
query { me { ...UserFields } }

Còn có inline fragment (... on Type) để chọn field theo type cụ thể trên interface/union.

18

Alias cho phép đổi tên field trong kết quả, cần thiết khi gọi cùng một field nhiều lần với argument khác nhau.

query {
  hero: character(id: "1") { name }
  villain: character(id: "2") { name }
}

Nếu không có alias, hai field character sẽ trùng key và gây xung đột.

19

Directive thay đổi cách thực thi query/schema. Hai directive chuẩn: @include(if: Bool) chỉ lấy field khi điều kiện đúng, @skip(if: Bool) bỏ qua field khi điều kiện đúng — chúng ngược nhau.

query($withEmail: Boolean!) {
  user { name email @include(if: $withEmail) }
}
20

@deprecated là schema directive đánh dấu một field/enum value sắp bị loại bỏ, kèm lý do để client biết chuyển sang cái mới.

type User {
  fullName: String
  name: String @deprecated(reason: "Dùng fullName thay thế")
}

Công cụ như GraphiQL/Apollo sẽ hiển thị cảnh báo nhưng field vẫn hoạt động cho đến khi bị xoá.