From eecd79592ef4cb632e959fb723a1bacb247dbf9f Mon Sep 17 00:00:00 2001 From: Jeff Auriemma Date: Fri, 28 Jun 2024 12:41:07 -0400 Subject: [PATCH 1/4] Copy over original text --- .../blog/2024-06-28-why-i-like-graphql.mdx | 161 ++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 src/pages/blog/2024-06-28-why-i-like-graphql.mdx diff --git a/src/pages/blog/2024-06-28-why-i-like-graphql.mdx b/src/pages/blog/2024-06-28-why-i-like-graphql.mdx new file mode 100644 index 0000000000..5e6955e7bf --- /dev/null +++ b/src/pages/blog/2024-06-28-why-i-like-graphql.mdx @@ -0,0 +1,161 @@ +--- +title: "Why, after 8 years, I still like GraphQL" +tags: ["in-production"] +date: 2024-06-28 +byline: Marc-André Giroux, edited by Jeff Auriemma +--- + +A recent post, [Why, after 6 years, I’m over GraphQL](https://bessey.dev/blog/2024/05/24/why-im-over-graphql/), made the rounds in the tech circle. The author argues that they would not recommend GraphQL anymore due to concerns like security, performance, and maintainability. In this post, I want to go over some interesting points made, and some points I think don't hold up to scrutiny. + +Always be Persistin' +-------------------- + +Ok, first of all, let's start with something maybe a little bold: **Persisted Queries are basically essential for building a solid GraphQL API**. If you are not using them, you're doing GraphQL on hard mode. It's not impossible, but it leads to difficult problems, some of them discussed in the post. After 8 years of GraphQL, this has only gotten more and more important to me. Persist all queries, as soon as possible in your GraphQL journey. You'll thank yourself later. + +It is a little sad that this is basically glossed over and mentionned only in a small note at the bottom: + +> Persisted queries are also a mitigation for this and many attacks, but if you actually want to expose a customer facing GraphQL API, persisted queries are not an option. + +I assume the author is talking about public APIs here. While I don't think this is necessarily inherently true (One could ask customers to register queries first, figuring out the DX for this would be an interesting task), it's still a valid point. That's why [We Don’t See Many Public GraphQL APIs](https://productionreadygraphql.com/blog/2019-10-21-why-we-dont-see-many-public-graphql-apis) out there, and why I would not pick GraphQL if I were to expose a public API today. + +For a public API, a coarser-grained, resource-based API works great, and can be described through OpenAPI. SDKs can be generated through amazing tools like [Kiota](https://learn.microsoft.com/en-us/openapi/kiota/overview). It's hard to beat a well-made SDK for a public API, and in my experience, that's actually what customers expect and want to use. Moving on. + +Haxors +------ + +The author's first point is about GraphQL's allegedly bigger attack surface. Again this focuses more on completely public GraphQL APIs which are relatively rare: + +> exposing a query language to untrusted clients increases the attack surface of the application + +I think that's right, it's hard to argue about this, hence not exposing a query language to untrusted clients unless you're ready to handle the trade-offs. But let's see what they think is hard to get right here. + +Authz is really hard... +----------------------- + +Authorization is a challenge with GraphQL. The thing is it's almost always challenging no matter what API style you use. I'd go as far as saying it is a challenge with designing software in general. The example given in the post actually highlights this very well: + + query { + user(id: 321) { + handle # ✅ I am allowed to view Users public info + email # 🛑 I shouldn't be able to see their PII just because I can view the User + } + user(id: 123) { + blockedUsers { + # 🛑 And sometimes I shouldn't even be able to see their public info, + # because context matters! + handle + } + } + } + + +`handle` and `email` both have different authorization rules. This is actually quite tricky to handle with a `GET /user/:id` endpoint as well. There's really nothing that makes GraphQL harder here. Yes when fine-grained authorization is needed, you'll need fine-grained authorization checks at that level. + + user(id: 123) { + blockedUsers { + # 🛑 And sometimes I shouldn't even be able to see their public info, + # because context matters! + handle + } + } + + +This part is interesting as well and another challenge of authorizing code and models in general. The same "object" accessed through different contexts can actually have different authorization rules. Again, this is common in all API styles as well. That's why I actually usually advise designing this through a different model entirely, since it's likely they'll evolve differently. Here this is even possibly an API design mistake instead. If `handle` should never be seen for `blockedUsers`, then it shouldn't even be part of the schema here. This is a super common mistake where folks try to reuse common models/types instead of being specific. + +After 8 years of GraphQL, I realize more and more that authorization is much more than a GraphQL problem. The API layer part is easy, but most code-bases are much more complex and must guard against unauthorized access in much better ways. Companies like [oso](https://www.osohq.com/) and [authzed](https://authzed.com/) and two great examples of how to do this well but also how complex this thing can be in general. + +Demand Control +============== + +The section on rate limiting starts with this: + +> With GraphQL we cannot assume that all requests are equally hard on the server. + +Let me fix this to something I think is more accurate: + +> We cannot assume that all requests are equally hard on the server. + +There, much better. The truth is that no matter what API style you use, whether that's a binary protocol over UDP or GraphQL, it is extremelly rare, especially as the API surface grows, that all use-cases and "requests" will be equally expensive for a server to process. + +A very easy example to show this is simply a paginated endpoint: + + GET /users?first=100 + + + GET /users?first=1 + + +Or super expensive mutations: + + POST /expensive-creation + + +To be 100% fair, of course GraphQL exposes this problem a bit more, earlier on, especially when not using persisted queries (Which should not happen!!). And while folks building a small RPC API may not need to implement variable rate limiting or some sort of cost categories, they almost always end up having to. + +And again: this focuses on public, unauthenticated public APIs. I think we can agree this is not [GraphQL's Sweet Spot](https://productionreadygraphql.com/blog/2020-05-14-sweetspot). + +The rest of the section shows how simple it is to rate limit a simple rest API. Sure? I have never had the chance to work on an API that was that easy to implement demand control for. + +Performance +----------- + +The performance section focuses mainly on dataloader and n+1s. I think the author makes some good points here. It's true that a GraphQL API must be ready for many query shapes and use cases. It is wise to implement efficient data loader for most fields through dataloaders. In fact, that's why I don't recommend using datafetching techniques that are overly coupled to the query shape, things like AST analysis, lookaheads, and things using context from sibling or parent fields. + +The author acknowledges that this is a problem with REST as well, but still makes this statement: + +> Meanwhile, in REST, we can generally hoist nested N+1 queries up to the controller, which I think is a pattern much easier to wrap your head around. + +Again this is an extremelly simple example, which has a trivial solution. But it's true, an endpoint based API that is simple can usually be kept simple, rather than being part of multiple other use-cases that could affect its performance long term. + +Overall I think dataloader is a requirement for GraphQL, and I agree that it's part of the slight complexity add for a GraphQL API, even simple ones. Authorization n+1s are also an issue. + +> Again, this problem simply does not exist in the REST world. + +But that's simply not true. Authz n+1s exist everywhere including in REST given a sufficiently complex API. Performant authz is a problem of its own. + +Coupling +-------- + +> In my experience, in a mature GraphQL codebase, your business logic is forced into the transport layer. This happens through a number of mechanisms, some of which we’ve already talked about: + +That's of course an observation from the author's experience, but in general, this couldn't be further from the truth. Hell, this is even specifically stated on [GraphQL's website](https://graphql.org/learn/thinking-in-graphs/): + +![business logic](/business.png) + +If one ends up with coupling it's because there's a tendency to couple business logic with the transport layer in general. But it's not like GraphQL encourages you to do so. It actually does the opposite. + +> Solving data authorisation leads to peppering authorisation rules throughout your GraphQL types + +Again, even the website tells you **not to do this**: [Delegate authorization logic to the business logic layer](https://graphql.org/learn/authorization/). + +> Solving resolver data fetching N+1s leads to moving this logic into GraphQL specific dataloaders + +I actually agree with this one. Data-loading in a performant way can be in tension with keeping things in reusable "business logic" units. I think this is a challenge when it comes to implementing a GraphQL API. + +> Leveraging the (lovely) Relay Connection pattern leads to moving data fetching logic into GraphQL specific custom connection objects + +True, similar to the point above. + +Breaking Changes?? +------------------ + +Probably the strangest sentence in the post is this one: + +> GraphQL discourages breaking changes and provides no tools to deal with them. This adds needless complexity for those who control all their clients, who will have to find workarounds. + +What? What API style encourages breaking changes? That's probably not a good idea. I think there's some confusion here. GraphQL encourages continuous evolution of your API. This usually relates to versioning rather than avoiding breaking changes. Instead of breaking changes, with GraphQL you deprecate schema members, and only remove them once they are not used anymore (or are ok with breaking). + +Arguably one of GraphQL's most powerful tool is around continuous evolution. The fact the client declaratively selects the API surface it is interested in means we can track with very high precision how our API is used. This allows us to actually **avoid** breaking changes, and make removals safely on fine grained parts of the schema. + +Breaking changes and deprecations suck. We all try to avoid them, and yes it's annoying for clients. But if anything GraphQL makes this easier, not harder. + +Conclusion +---------- + +Overall, I can feel the pain of the author when it comes to building public GraphQL APIs. It's not easy. But the post in general never really addresses a very common use-case for GraphQL, which is an internal API for known multiple clients. In this context, using persisted queries is easy, and solves a lot of the problems the author encountered in their journey. There are also a lot of problems mentionned here that are hard problems in general, and I don't always buy the fact that GraphQL makes them any harder. + +After 8 years of GraphQL, I still enjoy the decoupling a GraphQL schema offers between server-side capabilities and client-side requirements, but am aware of the trade-offs when picking it as a technology. + +Anyway, **Persist your queries and probably don't build public GraphQL APIs unless you really know what you're doing.** + +* [xuorig](https://x.com/__xuorig__) From 4a690989c0c3b6979361c52cfe0aadf1ef6fa555 Mon Sep 17 00:00:00 2001 From: Jeff Auriemma Date: Fri, 28 Jun 2024 12:41:59 -0400 Subject: [PATCH 2/4] Formatting and initial editorial --- .../blog/2024-06-28-why-i-like-graphql.mdx | 165 +++++------------- 1 file changed, 48 insertions(+), 117 deletions(-) diff --git a/src/pages/blog/2024-06-28-why-i-like-graphql.mdx b/src/pages/blog/2024-06-28-why-i-like-graphql.mdx index 5e6955e7bf..7b4c764445 100644 --- a/src/pages/blog/2024-06-28-why-i-like-graphql.mdx +++ b/src/pages/blog/2024-06-28-why-i-like-graphql.mdx @@ -5,157 +5,88 @@ date: 2024-06-28 byline: Marc-André Giroux, edited by Jeff Auriemma --- -A recent post, [Why, after 6 years, I’m over GraphQL](https://bessey.dev/blog/2024/05/24/why-im-over-graphql/), made the rounds in the tech circle. The author argues that they would not recommend GraphQL anymore due to concerns like security, performance, and maintainability. In this post, I want to go over some interesting points made, and some points I think don't hold up to scrutiny. +This post is edited from its [original publication](https://www.magiroux.com/eight-years-of-graphql/) on Marc-André Giroux's blog. Though originally intended to respond to a specific article, we at the GraphQL Foundation felt that Marc-André's post would stand well on its own with a few light edits. What follows is a well-informed set of practices and principles that engineers should consider when using GraphQL in production. -Always be Persistin' --------------------- +## Use persisted queries -Ok, first of all, let's start with something maybe a little bold: **Persisted Queries are basically essential for building a solid GraphQL API**. If you are not using them, you're doing GraphQL on hard mode. It's not impossible, but it leads to difficult problems, some of them discussed in the post. After 8 years of GraphQL, this has only gotten more and more important to me. Persist all queries, as soon as possible in your GraphQL journey. You'll thank yourself later. +Ok, first of all, let's start with something maybe a little bold: **Persisted Queries are basically essential for building a solid GraphQL API**. If you are not using them, you're doing GraphQL on hard mode. It's not impossible, but it leads to difficult problems that show up in GraphQL debates from time to time. After 8 years of GraphQL, this has only gotten more and more important to me. Persist all queries, as soon as possible in your GraphQL journey. You'll thank yourself later. -It is a little sad that this is basically glossed over and mentionned only in a small note at the bottom: - -> Persisted queries are also a mitigation for this and many attacks, but if you actually want to expose a customer facing GraphQL API, persisted queries are not an option. - -I assume the author is talking about public APIs here. While I don't think this is necessarily inherently true (One could ask customers to register queries first, figuring out the DX for this would be an interesting task), it's still a valid point. That's why [We Don’t See Many Public GraphQL APIs](https://productionreadygraphql.com/blog/2019-10-21-why-we-dont-see-many-public-graphql-apis) out there, and why I would not pick GraphQL if I were to expose a public API today. +Public APIs are a bit different. To be clear, it's not necessarily true that you can't use Persisted Queries in public APIs; one could ask customers to register queries first. But figuring out the DX for this would be an interesting task. That's why [We Don’t See Many Public GraphQL APIs](https://productionreadygraphql.com/blog/2019-10-21-why-we-dont-see-many-public-graphql-apis) out there, and why I would not pick GraphQL if I were to expose a public API today. For a public API, a coarser-grained, resource-based API works great, and can be described through OpenAPI. SDKs can be generated through amazing tools like [Kiota](https://learn.microsoft.com/en-us/openapi/kiota/overview). It's hard to beat a well-made SDK for a public API, and in my experience, that's actually what customers expect and want to use. Moving on. -Haxors ------- - -The author's first point is about GraphQL's allegedly bigger attack surface. Again this focuses more on completely public GraphQL APIs which are relatively rare: - -> exposing a query language to untrusted clients increases the attack surface of the application - -I think that's right, it's hard to argue about this, hence not exposing a query language to untrusted clients unless you're ready to handle the trade-offs. But let's see what they think is hard to get right here. - -Authz is really hard... ------------------------ - -Authorization is a challenge with GraphQL. The thing is it's almost always challenging no matter what API style you use. I'd go as far as saying it is a challenge with designing software in general. The example given in the post actually highlights this very well: - - query { - user(id: 321) { - handle # ✅ I am allowed to view Users public info - email # 🛑 I shouldn't be able to see their PII just because I can view the User - } - user(id: 123) { - blockedUsers { - # 🛑 And sometimes I shouldn't even be able to see their public info, - # because context matters! - handle - } - } +## Authorization + +Authorization is a challenge with GraphQL. The thing is it's almost always challenging no matter what API style you use. I'd go as far as saying it is a challenge with designing software in general. Here's an example from a recent post that actually highlights this very well: + +```graphql +query { + user(id: 321) { + handle # ✅ I am allowed to view Users public info + email # 🛑 I shouldn't be able to see their PII just because I can view the User + } + user(id: 123) { + blockedUsers { + # 🛑 And sometimes I shouldn't even be able to see their public info, + # because context matters! + handle } - + } +} +``` `handle` and `email` both have different authorization rules. This is actually quite tricky to handle with a `GET /user/:id` endpoint as well. There's really nothing that makes GraphQL harder here. Yes when fine-grained authorization is needed, you'll need fine-grained authorization checks at that level. - user(id: 123) { - blockedUsers { - # 🛑 And sometimes I shouldn't even be able to see their public info, - # because context matters! - handle - } - } - +```graphql +user(id: 123) { + blockedUsers { + # 🛑 And sometimes I shouldn't even be able to see their public info, + # because context matters! + handle + } +} +``` This part is interesting as well and another challenge of authorizing code and models in general. The same "object" accessed through different contexts can actually have different authorization rules. Again, this is common in all API styles as well. That's why I actually usually advise designing this through a different model entirely, since it's likely they'll evolve differently. Here this is even possibly an API design mistake instead. If `handle` should never be seen for `blockedUsers`, then it shouldn't even be part of the schema here. This is a super common mistake where folks try to reuse common models/types instead of being specific. After 8 years of GraphQL, I realize more and more that authorization is much more than a GraphQL problem. The API layer part is easy, but most code-bases are much more complex and must guard against unauthorized access in much better ways. Companies like [oso](https://www.osohq.com/) and [authzed](https://authzed.com/) and two great examples of how to do this well but also how complex this thing can be in general. -Demand Control -============== - -The section on rate limiting starts with this: +## Demand Control -> With GraphQL we cannot assume that all requests are equally hard on the server. - -Let me fix this to something I think is more accurate: - -> We cannot assume that all requests are equally hard on the server. - -There, much better. The truth is that no matter what API style you use, whether that's a binary protocol over UDP or GraphQL, it is extremelly rare, especially as the API surface grows, that all use-cases and "requests" will be equally expensive for a server to process. +We cannot assume that all requests are equally hard on the server, whether we're using GraphQL or not. The truth is that no matter what API style you use, whether that's a binary protocol over UDP or GraphQL, it is extremely rare, especially as the API surface grows, that all use-cases and "requests" will be equally expensive for a server to process. A very easy example to show this is simply a paginated endpoint: - GET /users?first=100 - +``` +GET /users?first=100 - GET /users?first=1 - +GET /users?first=1 +``` Or super expensive mutations: - POST /expensive-creation - +``` +POST /expensive-creation +``` -To be 100% fair, of course GraphQL exposes this problem a bit more, earlier on, especially when not using persisted queries (Which should not happen!!). And while folks building a small RPC API may not need to implement variable rate limiting or some sort of cost categories, they almost always end up having to. +To be 100% fair, of course GraphQL exposes this problem a bit more, earlier on, especially when not using persisted queries (Which should not happen!). And while folks building a small RPC API may not need to implement variable rate limiting or some sort of cost categories, they almost always end up having to. And again: this focuses on public, unauthenticated public APIs. I think we can agree this is not [GraphQL's Sweet Spot](https://productionreadygraphql.com/blog/2020-05-14-sweetspot). -The rest of the section shows how simple it is to rate limit a simple rest API. Sure? I have never had the chance to work on an API that was that easy to implement demand control for. - -Performance ------------ - -The performance section focuses mainly on dataloader and n+1s. I think the author makes some good points here. It's true that a GraphQL API must be ready for many query shapes and use cases. It is wise to implement efficient data loader for most fields through dataloaders. In fact, that's why I don't recommend using datafetching techniques that are overly coupled to the query shape, things like AST analysis, lookaheads, and things using context from sibling or parent fields. - -The author acknowledges that this is a problem with REST as well, but still makes this statement: - -> Meanwhile, in REST, we can generally hoist nested N+1 queries up to the controller, which I think is a pattern much easier to wrap your head around. - -Again this is an extremelly simple example, which has a trivial solution. But it's true, an endpoint based API that is simple can usually be kept simple, rather than being part of multiple other use-cases that could affect its performance long term. +## Performance -Overall I think dataloader is a requirement for GraphQL, and I agree that it's part of the slight complexity add for a GraphQL API, even simple ones. Authorization n+1s are also an issue. +Many GraphQL critics talk about the N+1 problem and DataLoader. It's true that a GraphQL API must be ready for many query shapes and use cases. It is wise to implement efficient data loader for most fields through dataloaders. In fact, that's why I don't recommend using datafetching techniques that are overly coupled to the query shape, things like AST analysis, lookaheads, and things using context from sibling or parent fields. But an honest observer should acknowledge that this is a problem with REST as well. As the complexity of an endpoint or operation increases, an engineer should expect to have to make more trade-offs in the name of performance. -> Again, this problem simply does not exist in the REST world. +Overall I think dataloader is a requirement for GraphQL, and I agree with critics that it's part of the slight complexity add for a GraphQL API, even simple ones. Authorization N+1s are also an issue, though, again, REST has these issues as well. -But that's simply not true. Authz n+1s exist everywhere including in REST given a sufficiently complex API. Performant authz is a problem of its own. +## Breaking Changes vs. Continuous Evolution -Coupling --------- - -> In my experience, in a mature GraphQL codebase, your business logic is forced into the transport layer. This happens through a number of mechanisms, some of which we’ve already talked about: - -That's of course an observation from the author's experience, but in general, this couldn't be further from the truth. Hell, this is even specifically stated on [GraphQL's website](https://graphql.org/learn/thinking-in-graphs/): - -![business logic](/business.png) - -If one ends up with coupling it's because there's a tendency to couple business logic with the transport layer in general. But it's not like GraphQL encourages you to do so. It actually does the opposite. - -> Solving data authorisation leads to peppering authorisation rules throughout your GraphQL types - -Again, even the website tells you **not to do this**: [Delegate authorization logic to the business logic layer](https://graphql.org/learn/authorization/). - -> Solving resolver data fetching N+1s leads to moving this logic into GraphQL specific dataloaders - -I actually agree with this one. Data-loading in a performant way can be in tension with keeping things in reusable "business logic" units. I think this is a challenge when it comes to implementing a GraphQL API. - -> Leveraging the (lovely) Relay Connection pattern leads to moving data fetching logic into GraphQL specific custom connection objects - -True, similar to the point above. - -Breaking Changes?? ------------------- - -Probably the strangest sentence in the post is this one: - -> GraphQL discourages breaking changes and provides no tools to deal with them. This adds needless complexity for those who control all their clients, who will have to find workarounds. - -What? What API style encourages breaking changes? That's probably not a good idea. I think there's some confusion here. GraphQL encourages continuous evolution of your API. This usually relates to versioning rather than avoiding breaking changes. Instead of breaking changes, with GraphQL you deprecate schema members, and only remove them once they are not used anymore (or are ok with breaking). +One of the biggest "culture shocks" of adopting GraphQL is the concept of continuous evolution. Because GraphQL has primitives for deprecating and aliasing fields, the notion of backwards compatibility becomes gray instead of black-and-white. Want to add a field? Great, do it. Want to remove a field? Add `@deprecated` and gradually stop querying them, eventually removing the field altogether. Arguably one of GraphQL's most powerful tool is around continuous evolution. The fact the client declaratively selects the API surface it is interested in means we can track with very high precision how our API is used. This allows us to actually **avoid** breaking changes, and make removals safely on fine grained parts of the schema. Breaking changes and deprecations suck. We all try to avoid them, and yes it's annoying for clients. But if anything GraphQL makes this easier, not harder. -Conclusion ----------- - -Overall, I can feel the pain of the author when it comes to building public GraphQL APIs. It's not easy. But the post in general never really addresses a very common use-case for GraphQL, which is an internal API for known multiple clients. In this context, using persisted queries is easy, and solves a lot of the problems the author encountered in their journey. There are also a lot of problems mentionned here that are hard problems in general, and I don't always buy the fact that GraphQL makes them any harder. - -After 8 years of GraphQL, I still enjoy the decoupling a GraphQL schema offers between server-side capabilities and client-side requirements, but am aware of the trade-offs when picking it as a technology. - -Anyway, **Persist your queries and probably don't build public GraphQL APIs unless you really know what you're doing.** +## Conclusion -* [xuorig](https://x.com/__xuorig__) +After 8 years of GraphQL, I still enjoy the decoupling a GraphQL schema offers between server-side capabilities and client-side requirements, but am aware of the trade-offs when picking it as a technology. Definitely persist your queries. Definitely build a public GraphQL API only if you really know what you're doing. Many critiques of GraphQL apply to any sufficiently complex API, there are no silver bullets in software engineering. From 68fede6e5bc0e03e3d56483d9782f632c9a1dfcb Mon Sep 17 00:00:00 2001 From: Jeff Auriemma Date: Tue, 2 Jul 2024 07:09:44 -0400 Subject: [PATCH 3/4] Update src/pages/blog/2024-06-28-why-i-like-graphql.mdx Co-authored-by: Benjie --- src/pages/blog/2024-06-28-why-i-like-graphql.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/blog/2024-06-28-why-i-like-graphql.mdx b/src/pages/blog/2024-06-28-why-i-like-graphql.mdx index 7b4c764445..b630d55397 100644 --- a/src/pages/blog/2024-06-28-why-i-like-graphql.mdx +++ b/src/pages/blog/2024-06-28-why-i-like-graphql.mdx @@ -13,7 +13,7 @@ Ok, first of all, let's start with something maybe a little bold: **Persisted Qu Public APIs are a bit different. To be clear, it's not necessarily true that you can't use Persisted Queries in public APIs; one could ask customers to register queries first. But figuring out the DX for this would be an interesting task. That's why [We Don’t See Many Public GraphQL APIs](https://productionreadygraphql.com/blog/2019-10-21-why-we-dont-see-many-public-graphql-apis) out there, and why I would not pick GraphQL if I were to expose a public API today. -For a public API, a coarser-grained, resource-based API works great, and can be described through OpenAPI. SDKs can be generated through amazing tools like [Kiota](https://learn.microsoft.com/en-us/openapi/kiota/overview). It's hard to beat a well-made SDK for a public API, and in my experience, that's actually what customers expect and want to use. Moving on. +For a public API, a coarser-grained, resource-based API works great, and can be described through OpenAPI. SDKs can be generated through amazing tools like [Kiota](https://learn.microsoft.com/en-us/openapi/kiota/overview). It's hard to beat a well-made SDK for a public API, and in my experience, that's actually what customers expect and want to use. ## Authorization From da150ffec09fedc9bbf1694c9a3baf29f06c1cea Mon Sep 17 00:00:00 2001 From: Jeff Auriemma Date: Tue, 2 Jul 2024 07:10:25 -0400 Subject: [PATCH 4/4] Apply suggestions from code review Co-authored-by: Benjie --- src/pages/blog/2024-06-28-why-i-like-graphql.mdx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/blog/2024-06-28-why-i-like-graphql.mdx b/src/pages/blog/2024-06-28-why-i-like-graphql.mdx index b630d55397..801574623b 100644 --- a/src/pages/blog/2024-06-28-why-i-like-graphql.mdx +++ b/src/pages/blog/2024-06-28-why-i-like-graphql.mdx @@ -5,11 +5,11 @@ date: 2024-06-28 byline: Marc-André Giroux, edited by Jeff Auriemma --- -This post is edited from its [original publication](https://www.magiroux.com/eight-years-of-graphql/) on Marc-André Giroux's blog. Though originally intended to respond to a specific article, we at the GraphQL Foundation felt that Marc-André's post would stand well on its own with a few light edits. What follows is a well-informed set of practices and principles that engineers should consider when using GraphQL in production. +_This is a guest post outlining GraphQL TSC emeritus Marc-André Giroux's thoughts on practices and principles engineers should consider when using GraphQL in production. Though the [original publication](https://www.magiroux.com/eight-years-of-graphql/) was intended to respond to a specific article, we at the GraphQL Foundation felt that Marc-André's well-informed opinions would stand well on their own with a little light editing._ -## Use persisted queries +## Use Persisted Queries -Ok, first of all, let's start with something maybe a little bold: **Persisted Queries are basically essential for building a solid GraphQL API**. If you are not using them, you're doing GraphQL on hard mode. It's not impossible, but it leads to difficult problems that show up in GraphQL debates from time to time. After 8 years of GraphQL, this has only gotten more and more important to me. Persist all queries, as soon as possible in your GraphQL journey. You'll thank yourself later. +Let's start with something maybe a little bold: **Persisted Queries are basically essential for building a solid GraphQL API**. If you are not using them, you're doing GraphQL on hard mode. It's not impossible, but it leads to difficult problems that show up in GraphQL debates from time to time. After 8 years of GraphQL, this has only gotten more and more important to me. Persist all queries, as soon as possible in your GraphQL journey. You'll thank yourself later. Public APIs are a bit different. To be clear, it's not necessarily true that you can't use Persisted Queries in public APIs; one could ask customers to register queries first. But figuring out the DX for this would be an interesting task. That's why [We Don’t See Many Public GraphQL APIs](https://productionreadygraphql.com/blog/2019-10-21-why-we-dont-see-many-public-graphql-apis) out there, and why I would not pick GraphQL if I were to expose a public API today. @@ -17,7 +17,7 @@ For a public API, a coarser-grained, resource-based API works great, and can be ## Authorization -Authorization is a challenge with GraphQL. The thing is it's almost always challenging no matter what API style you use. I'd go as far as saying it is a challenge with designing software in general. Here's an example from a recent post that actually highlights this very well: +Authorization is a challenge with GraphQL. The thing is it's almost always challenging no matter what API style you use. I'd go as far as saying it is a challenge with designing software in general. Here's an example from a recent post that highlights this very well: ```graphql query {