توسعه فرانت‌اند

API Design Patterns: REST، GraphQL و gRPC

مقایسه REST، GraphQL و gRPC بر اساس مدل داده، مصرف‌کننده، خطا، مجوز، Pagination و هزینه نگهداری؛ بدون رتبه‌بندی یا سرعت تضمینی.

1404/12/02 1 دقیقه مطالعه 1,457 بازدید

انتخاب REST، GraphQL یا gRPC از شکل داده و رابطه مصرف‌کنندگان شروع می‌شود. هر سه می‌توانند برای یک خدمت مفید باشند، اما هزینه قرارداد، ابزار و نگهداری یکسانی ندارند. فهرست «REST برای همه، GraphQL برای داده پیچیده و gRPC برای میکروسرویس» بیش از حد کلی است. مرورگر عمومی، برنامه موبایل و سرویس داخلی ممکن است از یک داده استفاده کنند و به قراردادهای متفاوت نیاز داشته باشند. ابتدا مسیرهای واقعی خواندن و تغییر داده را بنویسید.

این سه نام هم دقیقاً در یک سطح قرار ندارند: REST سبک معماری است، GraphQL زبان Query و مدل اجرای مبتنی بر Schema دارد، و gRPC چارچوب RPC با قرارداد سرویس است. نمایش هر API دارای JSON و HTTP به عنوان REST دقیق نیست. استفاده از Protobuf هم به تنهایی gRPC نمی‌سازد. این تمایز کمک می‌کند انتخاب را با ویژگی واقعی توضیح دهید، نه با نامی که در معرفی محصول یا کتابخانه نوشته شده است.

قرارداد مشترک را پیش از فناوری تعریف کنید

منبع داده، مالکیت، واحدها و حالت خطا را مشخص کنید. در سفارش، شناسه عمومی با کلید داخلی پایگاه داده ممکن است فرق داشته باشد؛ قیمت باید واحد پول معلوم داشته باشد و تاریخ به منطقه زمانی وابسته است. این ابهام‌ها با انتخاب GraphQL یا gRPC حذف نمی‌شوند. مدل API باید داده مورد نیاز مصرف‌کننده را ارائه کند و اطلاعات داخلی را بی‌دلیل بیرون ندهد. مقاله الگوهای TypeScript درباره تفکیک DTO و Domain ادامه مناسبی دارد.

برای هر عملیات، ورودی، نتیجه و خطاهای مورد انتظار را بنویسید. «ایجاد سفارش» ممکن است با نبود موجودی، مجوز ناکافی یا درخواست تکراری روبه‌رو شود. این حالت‌ها باید در قرارداد قابل تشخیص باشند. یک پیام کلی Server Error مصرف‌کننده را برای اقدام بعدی راهنمایی نمی‌کند. خطای قابل نمایش کاربر را از جزئیات داخلی جدا نگه دارید. اطلاعات لازم برای پشتیبانی مثل شناسه درخواست می‌تواند همراه پاسخ بیاید، بدون اینکه Query یا رمز زیرساخت آشکار شود.

REST را با Semantics HTTP هماهنگ کنید

در API مبتنی بر HTTP، Method و Status باید معنی روشن داشته باشند. RFC 9110 Semantics روش‌ها، Safety و Idempotency را تعریف می‌کند. GET نباید برای تغییری استفاده شود که مصرف‌کننده انتظار خواندن صرف دارد. Idempotent بودن درباره اثر مورد نظر تکرار درخواست است و لزوماً پاسخ یا وضعیت عددی یکسان در همه دفعات را تضمین نمی‌کند. این تفاوت برای Retry و ابزارهای واسط مهم است.

مسیر منبع باید با مدل محصول هماهنگ باشد. فهرست سفارش و یک سفارش مشخص دو استفاده متفاوت‌اند و Pagination، فیلتر و مجوزشان باید تعریف شود. افزودن پارامتر برای همه فیلدهای ممکن می‌تواند قرارداد را پیچیده کند؛ نیاز واقعی Clientها را بررسی کنید. استفاده از HTTP به شما امکان بهره‌برداری از سازوکارهای آن را می‌دهد، اما Cache یا ETag خودکار در همه APIها درست تنظیم نمی‌شوند. داده خصوصی و عمومی نیز سیاست Cache یکسانی ندارند.

REST برای تیمی که ابزارهای HTTP و قرارداد منبع‌محور روشن دارد می‌تواند شروع قابل نگهداری باشد. این توصیه حکم برتری همگانی نیست. اگر یک صفحه به داده چند منبع نیاز دارد، می‌توانید Endpoint متناسب با نیاز صفحه یا لایه ترکیب داشته باشید؛ مجبور نیستید هر رابطه را به تعداد زیادی درخواست کوچک تبدیل کنید. از طرف دیگر، پاسخ بسیار بزرگ برای همه Clientها هم هزینه انتقال و وابستگی ایجاد می‌کند. حد پاسخ و هدف هر مسیر باید معلوم باشد.

GraphQL انتخاب فیلد را به Client می‌دهد

راهنمای رسمی GraphQL Schema، Query، Mutation و اجرای Resolverها را معرفی می‌کند. Client می‌تواند فیلدهای مورد نیاز را در محدوده Schema درخواست کند. این ویژگی برای Clientهایی با نیازهای متفاوت قابل بررسی است، اما دسترسی به هر فیلد تعریف‌شده برای هر کاربر مجاز نیست. اعتبار Query از نظر نوع با مجوز مشاهده داده فرق دارد. منطق دسترسی باید در لایه‌ای اعمال شود که همه مسیرهای رسیدن به همان داده را پوشش دهد.

یک Query ظاهراً کوتاه می‌تواند کار زیادی در Server ایجاد کند. رابطه‌های تودرتو و دریافت داده جدا برای هر آیتم ممکن است مشکل N+1 بسازند. اندازه، عمق یا هزینه Query و روش Batch باید با نیاز سامانه تعریف شوند. یک Endpoint واحد، Rate Limit مبتنی بر تعداد درخواست را برای همه هزینه‌ها کافی نمی‌کند. Pagination هم باید برای رابطه‌های بزرگ وجود داشته باشد. انتخاب فیلد کمتر در پاسخ همیشه به معنی Query ارزان‌تر در پایگاه داده نیست.

Schema مشترک به هماهنگی تیم‌ها کمک می‌کند، به شرط اینکه مالکیت و تغییر آن معلوم باشد. حذف فیلد، تغییر Nullability یا اضافه شدن حالت Enum می‌تواند روی Clientهای قدیمی اثر بگذارد. Deprecation باید با زمان مهاجرت و مشاهده مصرف واقعی همراه باشد. معرفی GraphQL به عنوان «بی‌نیاز از نسخه و تغییر ناسازگار» برداشت درستی نیست. نوع‌ها قرارداد را روشن‌تر می‌کنند و مسئولیت تکامل آن قرارداد همچنان با تیم باقی می‌ماند.

gRPC قرارداد عملیات سرویس را صریح می‌کند

معرفی رسمی gRPC تعریف سرویس، Methodها و Stubهای Client را توضیح می‌دهد. Protobuf انتخاب رایج برای پیام و تعریف قرارداد است. تولید کد می‌تواند بخشی از هماهنگی زبان‌های متفاوت را ساده کند، اما داده تولیدشده به معنی معتبر بودن درخواست تجاری نیست. مقدار تعداد، حق دسترسی یا وجود محصول همچنان بررسی Runtime لازم دارد. API تایپ‌شده خطای شبکه و تغییر وضعیت سرویس را حذف نمی‌کند.

Unary و Streaming نیازهای متفاوتی دارند. اگر جریان داده لازم نیست، صرف وجود قابلیت Streaming دلیل استفاده از آن نیست. Deadline، Cancellation و Statusها باید در قرارداد مصرف تعریف شوند. فراخوانی شبیه تابع محلی است، اما هزینه و شکست آن از شبکه می‌آید؛ نباید در حلقه‌ای طولانی بدون توجه به Deadline یا حجم، رفتار تابع محلی فرض شود. Retry عملیات تغییر داده نیز به Idempotency و تعریف نتیجه نیاز دارد تا خطای ارتباط به ثبت تکراری منجر نشود.

برای مرورگر، اتصال به gRPC معمولی به همان شکل Clientهای سرویس داخلی نیست. gRPC-web مسیر و محدودیت خودش را دارد و ممکن است به Proxy یا پشتیبانی اختصاصی Backend نیاز داشته باشد. مقاله Protobuf و gRPC-web این مرز را بررسی می‌کند. اندازه پیام باینری می‌تواند مزیت داشته باشد، اما ادعای سرعت ثابت برای همه داده‌ها درست نیست. زمان Serialization، شبکه، Query و ابزارهای زیرساخت را در همان سناریو بسنجید.

سه سناریو را با نیاز واقعی مقایسه کنید

برای یک فروشگاه کوچک با چند مسیر روشن و Client واحد، قرارداد HTTP ساده ممکن است نگهداری آسانی داشته باشد. اگر چند تیم Client با ترکیب داده متفاوت کار می‌کنند، GraphQL می‌تواند به انتخاب فیلد کمک کند؛ در مقابل، هزینه Schema و اجرای Query باید پذیرفته شود. در ارتباط سرویس‌های کنترل‌شده با چند زبان و قرارداد عملیات روشن، gRPC قابل بررسی است. این مثال‌ها فرضی‌اند و نتیجه اندازه‌گیری مشتری واقعی یا نسخه اجباری معماری نیستند.

یک محصول می‌تواند ترکیبی باشد: قرارداد عمومی برای مرورگر، لایه ترکیب برای صفحه و RPC داخلی میان سرویس‌ها. این انتخاب فقط وقتی مفید است که مرزها و مالکیت واضح باشند. چند فناوری برای یک نیاز کوچک ممکن است هزینه ابزار و خطایابی را بیشتر کند. مسیر تبدیل خطا و داده میان لایه‌ها را مشخص کنید تا مصرف‌کننده نتیجه مبهم نگیرد. مقاله React Server Components درباره محل دریافت داده زمینه دارد، اما معماری رندر جای انتخاب قرارداد API را نمی‌گیرد.

امنیت و مشاهده‌پذیری به همه الگوها تعلق دارند

Authentication هویت را بررسی می‌کند و Authorization حق همان عملیات را. انتخاب نام API این دو را اجرا نمی‌کند. داده بیرونی باید بررسی شود و حد درخواست و پاسخ مشخص باشد. برای عملیات مهم، شناسه درخواست و راه پیگیری کمک می‌کنند؛ Log نباید اطلاعات خصوصی را بی‌دلیل نگه دارد. در Timeout، کاربر باید بداند نتیجه نامعلوم است یا عملیات انجام نشده است. این دو حالت در Retry رفتار متفاوتی می‌خواهند و نباید یک پیام ظاهری مشترک آن‌ها را پنهان کند.

در تحویل، قرارداد خطا، Pagination، تکامل Schema و حد زمانی را کنار فناوری ثبت کنید. نمونه مصرف باید با داده کوچک و مسیر شکست هم قابل فهم باشد. مقاله WebSocket برای ارتباط پیوسته گزینه دیگری معرفی می‌کند؛ فهرست ابزارها به همین سه نام محدود نیست. تصمیم مناسب با این پرسش روشن می‌شود: این Client برای این کار چه داده و تضمینی می‌خواهد، و تیم چگونه همان قرارداد را در طول تغییر محصول نگه می‌دارد؟

واکنش به این مقاله

اشتراک‌گذاری این مقاله:
دیدگاه‌ها 0
۰ / ۲۰۰۰

هنوز دیدگاهی ثبت نشده

اولین نفری باشید که نظر می‌دهد!