<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Web-Architecture on IT Comparison</title><link>https://comparison.metacog.co.kr/tags/web-architecture/</link><description>Recent content in Web-Architecture on IT Comparison</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Sun, 06 Sep 2026 10:03:08 +0900</lastBuildDate><atom:link href="https://comparison.metacog.co.kr/tags/web-architecture/index.xml" rel="self" type="application/rss+xml"/><item><title>REST vs GraphQL: Multiple Endpoints vs Single Query Language</title><link>https://comparison.metacog.co.kr/posts/2026-09-06-rest-vs-graphql-multiple-endpoints-vs-single-query-language/</link><pubDate>Sun, 06 Sep 2026 10:03:08 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-09-06-rest-vs-graphql-multiple-endpoints-vs-single-query-language/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;REST structures an API as a fixed set of &lt;strong class="kw"&gt;endpoints&lt;/strong&gt;, each returning a predetermined shape of data tied to a resource. GraphQL exposes a single endpoint driven by a client-specified &lt;strong class="kw"&gt;query&lt;/strong&gt;, letting callers request exactly the fields they need across related resources in one round trip.&lt;/p&gt;
&lt;h2 id="comparison-diagram"&gt;Comparison Diagram&lt;/h2&gt;
&lt;div class="compare-diagram"&gt;
&lt;svg viewBox="0 0 640 360" xmlns="http://www.w3.org/2000/svg"&gt;&lt;text x="160" y="30" text-anchor="middle" font-size="18" style="fill:var(--primary)"&gt;REST&lt;/text&gt;&lt;text x="480" y="30" text-anchor="middle" font-size="18" style="fill:var(--primary)"&gt;GraphQL&lt;/text&gt;&lt;circle cx="60" cy="90" r="18" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="60" y="95" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;Client&lt;/text&gt;&lt;rect x="140" y="55" width="100" height="32" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="190" y="75" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;/users/1&lt;/text&gt;&lt;rect x="140" y="100" width="100" height="32" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="190" y="120" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;/users/1/posts&lt;/text&gt;&lt;rect x="140" y="145" width="100" height="32" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="190" y="165" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;/posts/1/comments&lt;/text&gt;&lt;line x1="78" y1="90" x2="140" y2="71" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;line x1="78" y1="90" x2="140" y2="116" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;line x1="78" y1="90" x2="140" y2="161" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="190" y="200" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;3 requests, fixed shapes&lt;/text&gt;&lt;rect x="20" y="230" width="340" height="70" rx="4" style="fill:none;stroke:var(--border)" stroke-width="1" stroke-dasharray="4 3"/&gt;&lt;text x="190" y="255" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;Response 1: full user object&lt;/text&gt;&lt;text x="190" y="273" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;Response 2: full posts array&lt;/text&gt;&lt;text x="190" y="291" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;may over- or under-fetch fields&lt;/text&gt;&lt;circle cx="400" cy="90" r="18" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="400" y="95" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;Client&lt;/text&gt;&lt;rect x="480" y="75" width="120" height="32" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="540" y="95" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;/graphql&lt;/text&gt;&lt;line x1="418" y1="90" x2="480" y2="91" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;rect x="440" y="130" width="160" height="70" rx="4" style="fill:none;stroke:var(--border)" stroke-width="1" stroke-dasharray="4 3"/&gt;&lt;text x="520" y="150" text-anchor="middle" font-size="10" style="fill:var(--content)"&gt;{ user(id:1){&lt;/text&gt;&lt;text x="520" y="165" text-anchor="middle" font-size="10" style="fill:var(--content)"&gt;name posts{ title }&lt;/text&gt;&lt;text x="520" y="180" text-anchor="middle" font-size="10" style="fill:var(--content)"&gt;} }&lt;/text&gt;&lt;text x="520" y="225" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;1 request, client-shaped&lt;/text&gt;&lt;rect x="440" y="250" width="160" height="50" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="520" y="272" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;Single JSON response&lt;/text&gt;&lt;text x="520" y="288" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;matches requested fields&lt;/text&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;h2 id="comparison-table"&gt;Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;REST&lt;/th&gt;
&lt;th&gt;GraphQL&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Request entry point&lt;/td&gt;
&lt;td&gt;Multiple resource-based URLs (e.g. /users, /posts)&lt;/td&gt;
&lt;td&gt;Single endpoint (e.g. /graphql) for all operations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Query specification&lt;/td&gt;
&lt;td&gt;Server defines response shape per endpoint&lt;/td&gt;
&lt;td&gt;Client defines response shape via query document&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fetching related data&lt;/td&gt;
&lt;td&gt;Requires multiple round trips or ad-hoc nested routes&lt;/td&gt;
&lt;td&gt;Nested relations resolved in one request via resolvers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Over/under-fetching&lt;/td&gt;
&lt;td&gt;Common — fixed payloads return unused or missing fields&lt;/td&gt;
&lt;td&gt;Minimized — client requests exactly the fields it needs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Caching&lt;/td&gt;
&lt;td&gt;Leverages HTTP caching (ETags, CDNs, cache-control)&lt;/td&gt;
&lt;td&gt;Requires custom client-side or persisted-query caching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Versioning strategy&lt;/td&gt;
&lt;td&gt;New versions (/v2/) or new endpoints for breaking changes&lt;/td&gt;
&lt;td&gt;Schema evolves additively; fields deprecated in place&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Error handling&lt;/td&gt;
&lt;td&gt;HTTP status codes signal success/failure per request&lt;/td&gt;
&lt;td&gt;200 OK typical even on partial errors; errors array in body&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tooling and discovery&lt;/td&gt;
&lt;td&gt;Relies on external docs (OpenAPI/Swagger) for contracts&lt;/td&gt;
&lt;td&gt;Self-describing schema with built-in introspection&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="key-differences"&gt;Key Differences&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;REST models an API around &lt;strong class="kw"&gt;resources&lt;/strong&gt; and HTTP verbs; GraphQL models it around a typed &lt;strong class="kw"&gt;schema&lt;/strong&gt; and queries.&lt;/li&gt;
&lt;li&gt;REST responses have a shape fixed by the server; GraphQL responses are shaped by the &lt;strong class="kw"&gt;client query&lt;/strong&gt; itself.&lt;/li&gt;
&lt;li&gt;REST benefits from standard HTTP &lt;strong class="kw"&gt;caching&lt;/strong&gt; infrastructure; GraphQL typically needs bespoke caching layers.&lt;/li&gt;
&lt;li&gt;Fetching nested or related data usually takes REST multiple &lt;strong class="kw"&gt;round trips&lt;/strong&gt;, while GraphQL resolves it in a single request.&lt;/li&gt;
&lt;li&gt;REST signals failures through HTTP &lt;strong class="kw"&gt;status codes&lt;/strong&gt;; GraphQL usually returns 200 with errors embedded in the payload.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="when-to-use-each"&gt;When to Use Each&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;REST&lt;/strong&gt;&lt;/p&gt;</description></item></channel></rss>