Mục lục
Giới thiệu về thiết kế API
Hầu hết công việc của các kỹ sư phần mềm hiện đại đều xoay quanh API: giao diện công cộng để tương tác với một chương trình, giống như API của Twilio. Tôi đã dành rất nhiều thời gian làm việc với API, cả trong việc xây dựng và sử dụng chúng. Tôi đã viết API công khai cho nhà phát triển bên thứ ba, API riêng tư cho sử dụng nội bộ (hoặc tiêu thụ bởi một trang frontend duy nhất), API REST và GraphQL, thậm chí cả các giao diện không mạng như công cụ dòng lệnh.
Giống như việc thiết kế các hệ thống phần mềm tốt, tôi nghĩ rằng phần lớn lời khẳng định xung quanh về thiết kế API quá phức tạp. Mọi người bị cuốn vào việc “REST thực sự” là gì, hoặc liệu HATEOAS có ý tưởng tốt không, v.v. Bài viết này là nỗ lực của tôi trong việc ghi lại mọi thứ tôi biết về việc thiết kế các API tốt.
Thiết kế API là sự cân bằng giữa sự quen thuộc và linh hoạt
Nếu điều này đúng với các hệ thống – và nó đúng – thì nó còn đúng hơn với API: các API tốt thì nhàm chán. Một API thú vị là một API xấu (hoặc ít nhất nó sẽ tốt hơn nếu nó ít thú vị hơn). Đối với các nhà phát triển xây dựng chúng, API là các sản phẩm phức tạp mà họ dành thời gian thiết kế và trau chuốt. Nhưng đối với các nhà phát triển sử dụng chúng, API là công cụ họ sử dụng để đạt được một số mục tiêu khác. Bất kỳ thời gian họ dành để suy nghĩ về API thay vì về mục tiêu đó là thời gian lãng phí. Theo quan điểm của họ, một API lý tưởng nên quen thuộc đến mức họ sẽ biết cách sử dụng nó trước khi đọc bất kỳ tài liệu nào.
Tuy nhiên, một khác biệt lớn so với hầu hết các hệ thống phần mềm là API khó thay đổi. Một khi bạn xuất bản một API và mọi người bắt đầu sử dụng nó, bất kỳ thay đổi nào đối với giao diện sẽ phá vỡ phần mềm của người dùng. Tất nhiên, có thể thực hiện các thay đổi. Nhưng (như tôi sẽ nói dưới đây) mỗi thay đổi đều gây ra chi phí nghiêm trọng: mỗi khi bạn buộc người dùng cập nhật phần mềm của họ, họ sẽ xem xét nghiêm túc việc sử dụng một API khác ổn định hơn. Điều đó mang lại cho nhà xây dựng API động lực mạnh mẽ để thiết kế cẩn thận và làm đúng ngay từ lần đầu tiên.
Sự căng thẳng này dẫn đến một động lực thú vị đối với các kỹ sư xây dựng API. Mặt một, họ muốn xây dựng API đơn giản nhất có thể. Mặt khác, họ muốn làm những điều thông minh để duy trì sự linh hoạt lâu dài. Nhìn chung, thiết kế API là về việc tìm kiếm sự cân bằng giữa hai mục tiêu không tương thích đó.
CHÚNG TÔI KHÔNG PHÁ HỦNG KHÔNG GIAN NGƯỜI DÙNG
Điều gì xảy ra khi bạn cần thay đổi API của mình? Các thay đổi bổ sung – ví dụ, đặt một trường mới trong phản hồi – thường thì ổn. Có một số người tiêu dùng sẽ gặp sự cố nếu họ nhận được nhiều trường hơn họ mong đợi, nhưng theo quan điểm của tôi, điều này là thiếu trách nhiệm. Bạn nên mong đợi người tiêu dùng API bỏ qua các trường không mong đợi (các ngôn ngữ phân tích kiểu JSON hợp lý làm điều này theo mặc định).
Tuy nhiên, bạn không thể loại bỏ hoặc thay đổi loại của các trường. Bạn không thể thay đổi cấu trúc của các trường hiện có (ví dụ, di chuyển user.address thành user.details.address trong phản hồi JSON). Nếu bạn làm như vậy, từng đoạn mã dựa vào các trường đó sẽ ngay lập tức bị hỏng. Người tiêu dùng của mã đó sẽ báo cáo đó là lỗi, và người bảo trì mã (khi họ tìm ra) sẽ chính đáng tức giận vì bạn cố tình phá vỡ phần mềm của họ.
Nguyên tắc ở đây giống như khẩu hiệu nổi tiếng của Linus Torvalds: CHÚNG TÔI KHÔNG PHÁ HỦNG KHÔNG GIAN NGƯỜI DÙNG. Với tư cách là người bảo trì API, bạn có một nghĩa vụ thiêng liêng để tránh gây hại cho người tiêu dùng downstream. Quy chuẩn này mạnh mẽ đến vậy vì rất nhiều phần mềm phụ thuộc vào rất nhiều API (mà lại phụ thuộc vào các API upstream, v.v.). Một người bảo trì API thiếu cẩn thận đủ xa upstream có thể phá vỡ hàng trăm hoặc hàng nghìn phần mềm downstream.
Bạn không bao giờ nên thay đổi API chỉ vì nó sẽ gọn gàng hơn, hoặc vì nó hơi awkward. Tiêu đề “referer” trong đặc tả HTTP nổi tiếng là lỗi chính tả của từ “referrer”, nhưng họ chưa thay đổi nó, vì chúng tôi không phá hủy không gian người dùng.
Thay đổi API mà không phá hủy không gian người dùng
Thật khó để nghĩ về các trường hợp mà API thực sự cần thay đổi gây gián đoạn. Nhưng đôi khi giá trị kỹ thuật của một thay đổi đủ cao để bạn quyết tâm làm điều đó bất chấp. Trong những trường hợp đó, bạn có thể thay đổi API của mình một cách có trách nhiệm như thế nào? Câu trả lời là phiên bản hóa.
Phiên bản hóa API có nghĩa là “cung cấp cả phiên bản cũ và mới của API cùng một lúc”. Người tiêu dùng hiện có có thể tiếp tục sử dụng phiên bản cũ, trong khi người tiêu dùng mới có thể chọn tham gia phiên bản mới. Cách dễ nhất để làm điều này là bao gồm thứ gì đó như /v1/ trong URL API của bạn. API trò chuyện của OpenAI ở tại v1/chat/completions, vì vậy nếu họ muốn tái cấu trúc hoàn toàn, họ có thể làm điều đó trong v2/chat/completions và giữ cho người tiêu dùng hiện tại tiếp tục hoạt động.
Một khi bạn có cả phiên bản mới và cũ hoạt động đồng thời, bạn có thể bắt đầu yêu cầu người dùng nâng cấp lên phiên bản mới. Điều này mất rất nhiều thời gian: hàng tháng hoặc thậm chí hàng năm. Ngay cả với banner trên trang web, tài liệu, email tùy chỉnh và tiêu phản hồi API, khi bạn cuối cùng loại bỏ phiên bản cũ, bạn vẫn sẽ nhận được rất nhiều người dùng tức giận vì bạn đã phá vỡ phần mềm của họ. Nhưng ít nhất bạn đã làm những gì bạn có thể.
Có rất nhiều cách khác để thực hiện phiên bản hóa API. API Stripe thực hiện phiên bản hóa trong tiêu đề và để tài khoản đặt phiên bản mặc định trong UI. Nhưng nguyên tắc là như nhau – bất kỳ người tiêu dùng nào của API Stripe đều có thể tự tin rằng Stripe sẽ không quyết định phá vỡ ứng dụng của họ, và họ có thể nâng cấp phiên bản theo tốc độ của riêng mình.
Tôi không thích phiên bản hóa API. Tôi nghĩ rằng tốt nhất nó là một điều ác cần thiết, nhưng nó vẫn là điều ác. Nó gây nhầm lẫn cho người dùng, những người không thể dễ dàng tìm kiếm tài liệu API của bạn mà không đảm bảo rằng trình chọn phiên bản khớp với phiên bản họ đang sử dụng. Và đó là cơn ác mộng cho người bảo trì. Nếu bạn có ba mươi điểm cuối API, mỗi phiên bản mới bạn thêm vào sẽ giới thiệu ba mươi điểm cuối mới để bảo trì. Bạn sẽ nhanh chóng kết thúc với hàng trăm API tất cả đều cần kiểm tra, gỡ lỗi và hỗ trợ khách hàng.
Tất nhiên, thêm một phiên bản mới không làm gấp đôi kích cơ sở mã của bạn. Bất kỳ backend phiên bản hóa API hợp lý nào cũng sẽ có một lớp dịch thuật có thể chuyển đổi phản hồi thành bất kỳ phiên bản API công khai nào của bạn. Stripe có thứ gì đó như thế này: logic kinh doanh thực tế là giống nhau cho tất cả các phiên bản, vì chỉ cần tuần tự hóa và phân tích tham số mới nhận biết được về phiên bản hóa. Tuy nhiên, các trừu tượng như vậy luôn rò rỉ. Xem bình luận HN năm 2017 từ một nhân viên Stripe, chỉ ra rằng một số thay đổi phiên bản hóa cần logic có điều kiện trong “código cốt lõi”.
Tóm lại, bạn chỉ nên sử dụng phiên bản hóa API như một giải pháp cuối cùng.
Thành công của API phụ thuộc hoàn toàn vào sản phẩm
Một API theo chính nó không làm gì cả. Nó là một lớp giữa người dùng và thứ họ thực sự muốn. Đối với API OpenAI, đó là khả năng suy luận với mô hình ngôn ngữ. Đối với API Twilio, đó là việc gửi tin nhắn SMS. Không ai sử dụng API vì chính API được thiết kế tinh tế như vậy. Họ sử dụng nó để tương tác với sản phẩm của bạn. Nếu sản phẩm của bạn đủ giá trị, người dùng sẽ đổ xô đến ngay cả một API tồi.
Đây là lý do tại sao một số API phổ biến nhất là một cơn ác mộng khi sử dụng. Facebook và Jira nổi tiếng có những API kinh khủng, nhưng điều đó không quan trọng – nếu bạn muốn tích hợp với Facebook hoặc Jira, điều mà bạn muốn làm, bạn cần dành thời gian để tìm hiểu chúng. Chắc chắn, thật tuyệt nếu các công ty đó có API tốt hơn. Nhưng tại sao đầu tư thời gian và tiền bạc để cải thiện nó khi mọi người sẽ tích hợp với nó bất chấp? Việc viết API tốt thực sự rất khó.
Tôi sẽ đưa ra rất nhiều lời khuyên cụ thể trong phần còn lại của bài đăng này về cách viết API tốt. Nhưng đáng nhớ rằng hầu hết thời gian nó không quan trọng. Nếu sản phẩm của đủ hấp dẫn, bất kỳ API nào hoạt động cơ bản cũng đã đủ; nếu không, dù API của bạn tốt đến đâu cũng không quan trọng. Chất lượng API là một tính năng biên: nó chỉ quan trọng khi người tiêu dùng chọn giữa hai cơ bản sản phẩm tương đương.
Có một câu chuyện hoàn toàn khác về sự hiện diện của API. Nếu một sản phẩm hoàn toàn không có API, đó là một vấn đề lớn. Người dùng kỹ thuật sẽ yêu cầu một cách nào đó để tích hợp với phần mềm họ mua thông qua mã.
Sản phẩm được thiết kế kém thường có API tồi
Một API tuyệt về mặt kỹ thuật không thể cứu một sản phẩm mà không ai muốn sử dụng. Tuy nhiên, một sản phẩm kém về mặt kỹ thuật có thể khiến việc xây dựng API tinh tế gần như không thể. Đó là vì thiết kế API thường theo dõi “nguồn cơ bản” của một sản phẩm (ví dụ, các nguồn của Jira sẽ là vấn đề, dự án, người dùng, v.v.). Khi các nguồn đó được thiết lập awkwardly, điều đó làm cho API cũng awkward.
Lấy ví dụ, một hệ thống blog lưu trữ bình luận trong bộ nhớ dưới dạng danh sách liên kết (mỗi bình luận có một trường next trỏ đến bình luận tiếp theo trong chuỗi). Đây là một cách tồi để lưu trữ bình luận. Cách ngây thơ để gắn một API REST lên hệ thống này sẽ là có một giao diện trông như thế này:
GET /comments/1 -> { id: 1, body: "...", next_comment_id: 2 }
Hoặc thậm chí tệ hơn như thế này:
GET /comments -> {body: "...", next_comment: { body: "...", next_comment: {...}}}
Có vẻ như đây là một ví trí ngớ ngẩn, vì trên thực tế bạn chỉ cần lặp qua danh sách liên kết và trả về một mảng bình luận trong phản hồi API. Nhưng ngay cả khi bạn sẵn lòng làm công việc thêm đó, bạn lặp xuống sâu bao xa? Trong một chuỗi với hàng nghìn bình luận, có phải không thể lấy bất kỳ bình luận nào sau vài trăm bình luận đầu tiên không? API lấy bình luận của bạn có phải sử dụng một công việc nền, buộc giao diện phải trở thành thứ gì đó như:
POST /comments/fetch_job/1 -> { job_id: 589 }
GET /comments_job/589 -> { status: 'complete', comments: [...] }
Đây là cách một số API tồi nhất xảy ra. Các ràng buộc kỹ thuật có thể được ẩn một cách thông minh trong UI được bày ra trong API, buộc người tiêu dùng API phải hiểu nhiều hơn về thiết kế hệ thống so với những gì họ nên có.
Xác thực
Bạn nên cho mọi người sử dụng API của bạn với một khóa API có thời gian tồn tại dài. Vâng, khóa API không an toàn bằng các hình thức thông tin xác thực ngắn hạn, như OAuth (mà bạn cũng nên hỗ trợ). Điều đó không quan trọng. Mỗi tích hợp với API của bạn bắt đầu cuộc đời dưới dạng một kịch bản đơn giản, và sử dụng khóa API là cách dễ nhất để có một kịch bản đơn giản hoạt động. Bạn muốn làm cho nó dễ dàng có thể cho các kỹ sư để bắt đầu.
Mặc dù người tiêu dùng API (theo định nghĩa gần như) sẽ viết mã, nhưng nhiều người dùng của bạn sẽ không phải là kỹ sư chuyên nghiệp. Họ có thể là nhân viên bán hàng, quản lý sản phẩm, sinh viên, người đam mê, v.v. Khi bạn là một kỹ sư tại công ty công nghệ xây dựng API, điều dễ dàng để tưởng tượng rằng bạn đang xây dựng nó cho những người khác giống như bạn: kỹ sư phần mềm chuyên nghiệp, đủ năng lực. Nhưng bạn thì không. Bạn đang xây dựng nó cho một phần người rất rộng, nhiều người trong số đó không thoải mái khi viết hoặc đọc mã. Nếu API của bạn yêu cầu người dùng làm bất cứ điều gì khó khăn – như thực hiện một cuộc bắt tay OAuth – nhiều người dùng đó sẽ gặp khó khăn.
Đồng nhất và thử lại
Khi một yêu cầu API thành công, bạn biết nó đã làm những gì nó cố gắng làm. Còn khi nó thất bại thì sao? Một số loại thất bại cho bạn biết điều gì đã xảy ra: 422 thường có nghĩa là nó thất bại trong giai đoạn xác thực yêu cầu, trước khi bất kỳ hành động nào được thực hiện. Nhưng còn 500 thì sao? Còn hết giờ thì sao?
Điều này quan trọng đối với các thao tác API có hành động. Nếu bạn nhấn một số API Jira để tạo bình luận vấn đề, và yêu cầu 500s hoặc hết giờ, bạn có nên thử lại không? Bạn không chắc chắn liệu bình luận đã được tạo hay chưa, vì lỗi có thể xảy ra sau thao tác đó. Nếu bạn thử lại, bạn có thể kết up đăng hai bình luận. Điều này quan trọng hơn nữa khi có nhiều rủi ro hơn là một bình luận Jira. Điều gì nếu bạn đang chuyển một số tiền? Điều gì nếu bạn đang phân phát thuốc?
Giải pháp là đồng nhất, một từ hoa mỹ cho “yêu cầu có thể được thử lại an toàn mà không tạo ra bản sao”. Cách tiêu chuẩn để làm điều này là hỗ trợ một “khóa đồng nhất” trong yêu cầu (ví dụ, một chuỗi do người dùng định nghĩa trong tham số hoặc tiêu đề). Khi máy chủ API nhận được yêu cầu “tạo bình luận” với khóa đồng nhất, nó đầu tiên xem xem nó đã thấy khóa đồng nhất này trước đó chưa. Nếu có, nó không làm gì; nếu không, nó sẽ đi tạo bình luận, sau đó lưu khóa đồng nhất. Bằng cách đó bạn có thể gửi bao nhiêu lần thử lại tùy thích, miễn là chúng đều có cùng khóa đồng nhất – thao tác chỉ được thực hiện một lần.
Bạn nên lưu khóa đó như thế nào? Tôi đã thấy mọi người lưu nó theo cách bền vững, cụ thể cho tài nguyên (ví dụ, như một cột trong bảng bình luận), nhưng tôi nghĩ điều đó không hoàn toàn cần thiết. Cách dễ nhất là đặt chúng vào Redis hoặc một số cửa hàng khóa/giá trị tương tự (với khóa đồng nhất là khóa). UUID đủ duy nhất để bạn không cần phạm vi theo người dùng, nhưng bạn cũng có thể làm như vậy. Nếu bạn không xử lý thanh toán, bạn thậm chí có thể hết hạn chúng sau vài giờ, vì hầu hết các lần thử lại xảy ra ngay lập tức.
Bạn có cần khóa đồng nhất cho mọi yêu cầu không? Chà, bạn không cần chúng cho các yêu cầu đọc, vì việc đọc kép vô hại. Bạn cũng thường không cần chúng cho các yêu cầu xóa, vì nếu bạn xóa theo ID tài nguyên, ID đó đóng vai trò là khóa đồng nhất. Hãy nghĩ về nó – nếu bạn gửi ba yêu cầu DELETE comments/32 liên tiếp, nó sẽ không xóa ba bình luận. Yêu cầu thành công đầu tiên sẽ xóa bình luận với ID 32, và các yêu cầu còn lại sẽ 404 khi không tìm thấy bình luận đã bị xóa.
Trong大多数情况下,同质性应该是可选的。如上所述,您需要确保您的API对非工程师(这些人通常认为同质性是一个棘手的概念)是可访问的。从长远来看,让更多人使用您的API比那些没有阅读文档的用户偶尔重复评论更重要。
An toàn và giới hạn tốc độ
Người dùng tương tác với UI của bạn bị giới hạn bởi tốc độ của tay họ. Nếu có một luồng nào đó tốn kém cho backend của bạn để phục vụ, một người dùng ác ý hoặc thiếu cẩn thận chỉ có thể kích hoạt luồng đó nhanh như họ có thể nhấp qua nó. API thì khác. Bất kỳ thao tác nào bạn hiển thị qua API đều có thể được gọi với tốc độ của mã.
Cẩn thận với các API làm rất nhiều công việc trong một yêu cầu duy nhất. Khi tôi làm việc tại Zendesk, chúng tôi có một API cho phép bạn lan tỏ thông báo đến tất cả người dùng của một ứng dụng cụ thể. Một số nhà phát triển bên thứ ba đã sử dụng điều này để xây dựng một hệ thống trò chuyện trong ứng dụng, trong đó mỗi tin nhắn gửi thông báo đến mọi người dùng khác trên tài khoản. Đối với các tài khoản có hơn một vài người dùng hoạt động, điều này đáng tin cậy đã giết chết máy chủ backend của Apps.
Chúng tôi không lường trước mọi người sẽ xây dựng ứng dụng trò chuyện dựa trên API này. Nhưng một khi nó ra ngoài, mọi người đã làm những gì họ muốn với nó. Tôi đã tham gia rất nhiều cuộc gọi sự cố mà nguyên nhân gốc rễ là một số tích hợp khách hàng tự làm điều gì đó ngớ ngẩn, như:
- Tạo và xóa cùng một bản ghi hàng trăm lần mỗi phút, không có lý do chính đáng
- Đang quét một điểm cuối /index lớn mà không có độ trễ giữa các lần, mãi mãi
- Nhập hoặc xuất một lượng lớn dữ liệu mà không giảm tốc độ trong trường hợp có lỗi
Bạn nên đặt giới hạn tốc độ trên API của mình, với giới hạn chặt chẽ hơn cho các thao tác tốn kém. Cũng hợp lý khi dự trữ khả năng tạm thời vô hiệu hóa API cho các khách hàng cụ thể, để bạn có thể giảm bớt hệ thống backend nếu nó thực sự bị tấn công dữ dội.
Bao gồm siêu dữ liệu giới hạn tốc độ trong phản hồi API của bạn. Các tiêu đề X-Limit-Remaining và Retry-After cung cấp cho khách hàng thông tin họ cần để là người tiêu dùng tôn trọng của API của bạn, và cho phép bạn đặt giới hạn tốc độ nghiêm ngặt hơn so với những gì bạn có thể làm được khác đi.
Phân trang
Hầu như mọi API đều phải phục vụ một danh sách dài các bản ghi. Đôi khi là một danh sách rất dài (ví dụ, API /tickets của Zendesk có thể chứa hàng triệu vé). Làm thế nào để phục vụ các bản ghi đó?
Cách tiếp cận ngây thơ SELECT * FROM tickets WHERE… sẽ làm cạn kiệt bộ nhớ khả dụng (nếu không phải trong cơ sở dữ liệu, thì ở lớp ứng dụng nơi bạn đang cố gắng tuần tự hóa danh sách triệu mục này). Bạn đơn giản không thể phục vụ mọi vé trong một yêu cầu duy nhất. Thay vào đó, bạn phải phân trang.
Cách đơn giản nhất để phân trang là sử dụng trang (hoặc “độ lệch”, nói chung hơn). Khi bạn nhấn /tickets, bạn nhận được mười vé đầu tiên trên tài khoản. Để nhận thêm, bạn phải nhấn /tickets?page=2 hoặc /tickets?offset=20. Điều này dễ thực hiện, vì máy chủ chỉ cần thêm OFFSET 20 LIMIT 10 vào cuối truy vấn cơ sở dữ liệu. Nhưng nó không mở rộng ra cho các số lượng bản ghi thực sự lớn. Cơ sở dữ liệu quan hệ phải đếm qua độ lệch của bạn mỗi lần, vì vậy mỗi trang bạn phục vụ sẽ chậm hơn một chút so với trang trước. Đến khi độ lệch của bạn ở hàng trăm nghìn, đó là một vấn đề thực sự.
Giải pháp là “phân trang dựa trên con trỏ”. Thay vì chuyển offset=20 để nhận trang thứ hai, bạn lấy vé cuối cùng trên trang đầu tiên (ví dụ, với ID 32) và chuyển cursor=32. API sau đó sẽ trả về mười vé tiếp theo, bắt đầu với vé số 32. Thay vì sử dụng OFFSET, truy vấn trở thành WHERE id > cursor ORDER BY id LIMIT 10. Truy vấn đó nhanh như nhau bất kể bạn đang ở đầu bộ sưu tập hay hàng trăm nghìn vé, vì cơ sở dữ liệu có thể ngay lập tức tìm thấy vị trí (được chỉ mục) của vé con trỏ của bạn thay vì phải đếm qua toàn bộ độ lệch.
Bạn luôn nên sử dụng phân trang dựa trên con trỏ cho các bộ dữ liệu có thể kết thúc bằng rất lớn. Mặc dù nó khó hơn cho người tiêu dùng nắm bắt,但当您遇到扩展问题时,您可能仍然需要更改为基于游标的分页,并且进行此更改的成本通常非常高。然而,我认为在其他情况下使用页面或基于偏移量的分页是可行的。
Thường thì nên bao gồm một trường next_page trong phản hồi danh sách API của bạn. Điều đó tiết kiệm cho người tiêu dùng phải tự mình tìm ra số trang tiếp theo hoặc con trỏ.
Trường tùy chọn và GraphQL
Nếu một phần của phản hồi API của bạn tốn kém để phục vụ, hãy làm cho chúng tùy chọn. Ví dụ, nếu lấy trạng thái đăng ký của người dùng yêu cầu backend của bạn thực hiện một lệnh gọi API, hãy xem xét làm cho điểm cuối /users/:id của bạn không trả về subscription trừ khi yêu cầu chuyển qua một tham số include_subscription. Là một cách tiếp cận chung hơn, bạn có thể có một tham số mảng includes với tất cả các trường tùy chọn của bạn. Điều này thường được sử dụng cho các bản ghi có liên quan (ví dụ, bạn có thể chuyển includes: [posts] vào yêu cầu người dùng của bạn để nhận các bài đăng của người dùng trong phản hồi).
Đây là một phần ý tưởng đằng sau GraphQL, một kiểu API trong đó thay vì nhấn các điểm cuối khác nhau cho mỗi thao tác, bạn tạo một truy vấn duy nhất với tất cả dữ liệu bạn cần và backend sẽ tìm ra nó.
Tôi không thích GraphQL lắm, vì ba lý do. Thứ nhất, nó hoàn toàn khó hiểu đối với không phải là kỹ sư (và đối với nhiều kỹ sư). Một khi bạn học nó, đó là một công cụ như bất kỳ công cụ nào khác, nhưng rào cản gia nhập chỉ cao so với GET /users/1. Thứ hai, tôi không thích cho phép người dùng tự do tạo các truy vấn tùy ý. Nó làm cho việc lưu vào bộ nhớ cache phức tạp hơn và làm tăng số lượng trường hợp ngoại biên bạn phải suy nghĩ. Thứ ba, theo kinh nghiệm của tôi, việc triển khai backend phải tinh tế hơn rất nhiều so với API REST tiêu chuẩn của bạn.
Tôi không cảm thấy mạnh mẽ về sự không thích của mình đối với GraphQL. Tôi đã dành khoảng sáu tháng làm việc với nó trong nhiều bối cảnh và xa khỏi chuyên gia. Tôi chắc chắn có các trường hợp sử dụng nơi nó mang lại đủ sự linh hoạt để xứng đáng với chi phí. Nhưng bây giờ tôi chỉ sẽ sử dụng nó ở những nơi tôi tuyệt đối phải làm.
API nội bộ
Mọi thứ tôi đã nói cho đến nay là về API công khai. Còn API nội bộ thì sao: API chỉ được sử dụng bởi đồng nghiệp của bạn tại một công ty cụ thể? Một số giả định tôi đã đưa ra ở trên không áp dụng cho API nội bộ. Ví dụ, người tiêu dùng của bạn thường là các kỹ sư phần mềm chuyên nghiệp. Cũng có thể thực hiện các thay đổi gây gián đoạn một cách an toàn, vì (a) bạn thường có ít người dùng hơn một bậc độ lớn, và (b) bạn có khả năng đi vào và giao mã mới cho tất cả những người dùng đó. Bạn có thể yêu cầu một hình thức xác thực phức tạp như bạn muốn.
Tuy nhiên, API nội bộ vẫn có thể là nguồn gây ra sự cố, và vẫn cần phải đồng nhất đối với các hoạt động chính.
Tóm tắt
- API khó xây dựng vì chúng không linh hoạt nhưng phải dễ áp dụng
- Trách nhiệm chính của người bảo trì API là KHÔNG PHÁ HỦNG KHÔNG GIAN NGƯỜI DÙNG. Không bao giờ thực hiện các thay đổi gây gián đoạn cho API công khai
- Phiên bản hóa API của bạn cho phép bạn thay đổi, nhưng gây ra rào cản triển khai và áp dụng đáng kể
- Nếu sản phẩm của bạn đủ giá trị, thực sự không quan trọng API của bạn tốt như thế nào, mọi người vẫn sẽ sử dụng nó
- Nếu sản phẩm của bạn được thiết kế tồi đến mức nào đó, không quan trọng bạn thiết kế API cẩn thận đến đâu, nó có thể sẽ tồi
- Các yêu cầu có hành động (đặc biệt là hành động có rủi ro cao như thanh toán) nên bao gồm một loại khóa đồng nhất nào đó để làm cho việc thử lại an toàn
- API của bạn luôn là nguồn gây ra sự cố. Đảm bảo bạn có giới hạn tốc độ và công tắt trong vị trí
- Sử dụng phân trang dựa trên con trỏ cho các bộ dữ liệu có thể kết thúc bằng rất lớn
- Làm cho các trường tùy chọn và mặc định tắt, nhưng (theo quan điểm của tôi) GraphQL là quá mức cần thiết
- API nội bộ khác theo một số cách (vì người tiêu dùng của bạn rất khác nhau)
li>API của bạn nên hỗ trợ khóa API đơn giản cho xác thực, vì nhiều người dùng của bạn sẽ không phải là kỹ sư chuyên nghiệp
Điều gì tôi chưa viết về?
Tôi chưa viết nhiều về REST so với SOAP, hoặc JSON so với XML, vì tôi không nghĩ rằng những thứ đó đặc biệt quan trọng. Tôi thích REST và JSON, nhưng tôi không cảm thấy mạnh mẽ về nó. Tôi cũng chưa đề cập đến lược đồ OpenAPI – đó là một công cụ hữu ích, nhưng tôi nghĩ rằng cũng ổn nếu chỉ viết tài liệu API của bạn bằng Markdown nếu bạn muốn.