<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jens Neuse</title>
    <description>The latest articles on DEV Community by Jens Neuse (@jensneuse).</description>
    <link>https://dev.to/jensneuse</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F461422%2F5d6b3a05-f55e-4965-b90c-00a311267529.jpeg</url>
      <title>DEV Community: Jens Neuse</title>
      <link>https://dev.to/jensneuse</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jensneuse"/>
    <language>en</language>
    <item>
      <title>Authentication for SPAs done right with NextJS &amp; Server Side Rendering</title>
      <dc:creator>Jens Neuse</dc:creator>
      <pubDate>Sat, 31 Jul 2021 11:15:25 +0000</pubDate>
      <link>https://dev.to/jensneuse/authentication-for-spas-done-right-with-nextjs-server-side-rendering-fp</link>
      <guid>https://dev.to/jensneuse/authentication-for-spas-done-right-with-nextjs-server-side-rendering-fp</guid>
      <description>&lt;h2&gt;
  
  
  Why I'm so frustrated with Authentication in Single Page Applications
&lt;/h2&gt;

&lt;p&gt;A while back, I've tried to build a Single Page Application (SPA) using Create React APP (CRA).&lt;br&gt;
I wanted to use TypeScript as I'm using it over pure JavaScript for quite some time already.&lt;br&gt;
Additionally, I didn't want to build or maintain my own Authentication Service, so I chose Auth0 as OpenID Connect Provider.&lt;br&gt;
On paper, it looked simple enough: Configure GitHub and Google as upstream Auth providers,&lt;br&gt;
create a client ID and use the Auth0 SDK to hook up everything in my React application.&lt;/p&gt;

&lt;p&gt;However, things didn't go that smooth.&lt;br&gt;
There were no official TypeScript bindings for the Auth0 SDK, so I had to search through GitHub repositories until I found a solution from Auth0 users.&lt;br&gt;
I had to copy &amp;amp; paste a lot of code and wasn't 100% sure if it's going to work.&lt;br&gt;
Finally, I've got it working, but I wasn't satisfied with the solution.&lt;/p&gt;

&lt;p&gt;If you're using a provider like Auth0, you'll always have to put a Login page on a different domain in front of your website.&lt;br&gt;
Custom domains might be possible, or you could even embed the Login into your website directly, but that would mean extra work.&lt;br&gt;
Aside from that, you have to add a lot of code to your repository for the authentication SDK.&lt;/p&gt;

&lt;p&gt;After all, I've got super frustrated with the developer experience as well as the unsatisfying result for the end-user.&lt;br&gt;
As an end-user, you'll get redirected to a domain of the auth provider, which does not just take a lot of time but could also result in trust issues for the user.&lt;br&gt;
Additionally, the login SDK adds a lot of extra code to your website, resulting in slower load times.&lt;/p&gt;

&lt;p&gt;I've come to the conclusion that we can do better than that.&lt;br&gt;
Authentication should be very easy to set up, low maintenance and not distracting the user.&lt;/p&gt;

&lt;p&gt;In the meantime, I've switched from using CRA to NextJS.&lt;br&gt;
I think it's a much more rounded developer experience including Server Side Rendering (SSR) etc...&lt;br&gt;
With CRA, I always had to figure out which dependencies to use in order to get started.&lt;br&gt;
NextJS might or might not be the best framework. For me, it makes enough decisions, so I can focus on what matters to me, my application and my users.&lt;/p&gt;
&lt;h2&gt;
  
  
  How I think, Authentication should work for Single Page Applications
&lt;/h2&gt;

&lt;p&gt;I'd like to outline how authentication should ideally be implemented.&lt;br&gt;
To make it easier to understand, I've divided the topic into two parts.&lt;br&gt;
One is looking at authentication from the end-user perspective.&lt;br&gt;
Equally important, the second part looks at the developer experience.&lt;/p&gt;
&lt;h3&gt;
  
  
  User Stories from the perspective of the end-user
&lt;/h3&gt;

&lt;p&gt;Let's start by defining a number of user-stories about how authentication should work for the end-user.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As a user, I don't want to be redirected on a different domain or website to be able to login to a web application.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The user experience is slow and confusing when you get redirected to a slightly different looking website or even a different domain.&lt;br&gt;
Multiple redirects take a lot of time.&lt;br&gt;
Users expect that high quality websites don't redirect them to different domains during the login flow.&lt;br&gt;
They might sound similar and also use the same colors, but the overall CSS is usually different.&lt;br&gt;
All this leads to confusion and might lead to users abandoning the login flow.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As a user, I don't want to see loading spinners on a website that requires authentication.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You've probably seen this funny dance many times before.&lt;br&gt;
You enter a website, no content, just redirects and loading spinners.&lt;br&gt;
Then after a few seconds the spinners disappear, and the login screen appears.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As a user, I want to immediately see the content if I'm already logged into a website.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Am I logged in or not? After the loading spinners, it shows the Login screen for a second or two.&lt;br&gt;
At first, you think you're logged out, but then out of a sudden, the login screen disappears, and the dashboard starts to show up.&lt;br&gt;
If I'm authenticated, why can't I immediately see the dashboard?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As a user, I want to do a single login for all subdomains of a website: app.example.com, blog.example.com, docs.example.com&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Modern apps let you log in once across all products of a company.&lt;br&gt;
Why does it matter?&lt;br&gt;
Imagine you log into the dashboard of Stripe, then you open the docs.&lt;br&gt;
Wouldn't it make sense to immediately be logged in and show personalized docs?&lt;br&gt;
With single sign on across all products, you're able to deliver a much better user experience.&lt;/p&gt;
&lt;h3&gt;
  
  
  User Stories from the perspective of the developer
&lt;/h3&gt;

&lt;p&gt;For me, it's not just about the end-user experience.&lt;br&gt;
I want things to be easy to implement and maintain from a developer perspective as well.&lt;br&gt;
Therefore, I'll continue with a few stories for the devs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As a developer, I want to add almost no code to my NextJS application for authentication to work.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I've outlined it above.&lt;br&gt;
I want to have almost nothing to do with authentication.&lt;br&gt;
It should just work.&lt;br&gt;
Every line of code added, every framework, library or npm package added is a liability.&lt;br&gt;
Someone has to maintain it, update it and fix security issues.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As a developer, I want to be able to log users into my application using one or more external authentication providers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This should be the standard today.&lt;br&gt;
You should not add a database or custom authentication logic into your application.&lt;br&gt;
Also, I don't want to tightly couple authentication to an app instance.&lt;/p&gt;

&lt;p&gt;NextAuthJS, while looking great, adds authentication directly to your application.&lt;br&gt;
We want authentication that works across multiple applications and subdomains,&lt;br&gt;
while adding minimal to no code to our codebase to not distract us.&lt;/p&gt;

&lt;p&gt;You might disagree with me on this one, but I believe that you should not embed authentication into your application directly.&lt;br&gt;
Instead, you should have some kind of Authentication Gateway to handle the complexity while keeping your application clean.&lt;/p&gt;

&lt;p&gt;If you want a centralised database to store information about your users,&lt;br&gt;
consider using an OpenID Connect SaaS or an On Premises OIDC implementation like Keycloak.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As a developer, I want authentication aware data fetching&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At the same time I'm creating a Query or Mutation, I'd also want to create a policy if this operation is publicly available or if the user needs to be authenticated.&lt;br&gt;
If the user needs to be authenticated (private) and actually is, I want the operation to kick off.&lt;br&gt;
If the operation doesn't require authentication (public), the operation should immediately run.&lt;br&gt;
I don't want to add any extra logic into the frontend to handle all these cases,&lt;br&gt;
it's code duplication because it was already defined in the backend.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As a developer, I want to be able to implement SSR (Server Side Rendering) for authenticated users without much effort.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I want to get rid of those loading spinners, and the weird redirect dance.&lt;br&gt;
If the user is logged in already, I want to show them their dashboard right away.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As a developer, I don't want to mess around with differences between browsers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Looking at the previous user stories, you might already anticipate that we need to work with cookies in order to achieve our goals.&lt;br&gt;
If you've ever worked with cookies and multiple domains, you should be aware that getting it to work on Chrome, Firefox and Safari at the same time is not easy.&lt;br&gt;
You have to deal with CORS and SameSite policies.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As a developer, I want an authentication solution that is secure out of the box but also easy to handle.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Using cookies means we'll get some convenience at the expense of a threat: CSRF (Cross Site Request Forgery).&lt;br&gt;
The solution must solve CSRF automatically.&lt;br&gt;
That is, it should prevent that authenticated users can be tricked into making and action which they didn't intend to do.&lt;/p&gt;
&lt;h2&gt;
  
  
  Implementing Authentication for Single Page Applications, the right way
&lt;/h2&gt;

&lt;p&gt;With the user stories set up, we're now able to define the architecture of our solution.&lt;br&gt;
We'll go through the stories one by one, discuss possible solutions as well as their pros and cons.&lt;/p&gt;
&lt;h3&gt;
  
  
  Single Page Applications should not redirect users to a different domain during the login flow
&lt;/h3&gt;

&lt;p&gt;If you try to log into a Google product, you'll get redirected to accounts.google.com.&lt;br&gt;
It's a subdomain but still belongs to google.com and therefore there are no trust issues with this approach.&lt;/p&gt;

&lt;p&gt;Imagine your company domain is &lt;code&gt;example.com&lt;/code&gt; and you redirect your users to &lt;code&gt;example.authprovider.com&lt;/code&gt;.&lt;br&gt;
Even though there's &lt;code&gt;example&lt;/code&gt; in the domain name, it's still confusing.&lt;/p&gt;

&lt;p&gt;Ideally, we could run a service on e.g. &lt;code&gt;accounts.example.com&lt;/code&gt; that handles the login for us.&lt;br&gt;
Running such a service would mean we have to run an extra service which needs to be maintained.&lt;br&gt;
At the same time, this service would give us full control over the look and feel which increases trust for the user.&lt;br&gt;
So, while this options sounds like a good approach for a company like Google, we might want to find something simpler.&lt;/p&gt;

&lt;p&gt;What about a headless API service that also comes with authentication embedded?&lt;br&gt;
Luckily, there are protocols like OpenID Connect (OIDC) that allow us to run OIDC clients within our headless API service.&lt;br&gt;
This service will run on the domain &lt;code&gt;api.example.com&lt;/code&gt;.&lt;br&gt;
The login flow will be initiated from &lt;code&gt;example.com&lt;/code&gt; which also hosts the user interface to allow our users to start the flow.&lt;br&gt;
If the user clicks e.g. "Login with Google", we'll redirect them to &lt;code&gt;api.example.com/login-with-google&lt;/code&gt; (not the real path)&lt;br&gt;
and handle the login flow from there.&lt;br&gt;
Once the flow is complete, we'll redirect the user back to &lt;code&gt;example.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;How does &lt;code&gt;example.com&lt;/code&gt; know if the authentication was successful?&lt;br&gt;
Browsers are actually really cool about cookies.&lt;br&gt;
You're allowed to set cookies to the domain &lt;code&gt;example.com&lt;/code&gt; while you're on the domain &lt;code&gt;api.example.com&lt;/code&gt;.&lt;br&gt;
That is, the headless API service is allowed to set cookies for the whole &lt;code&gt;example.com&lt;/code&gt; domain.&lt;br&gt;
Btw. this doesn't work the other way around. You're not able to set cookies for &lt;code&gt;foo.example.com&lt;/code&gt; while you're on &lt;code&gt;example.com&lt;/code&gt; or &lt;code&gt;bar.example.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;What does all this mean?&lt;br&gt;
We start the user login flow from &lt;code&gt;example.com&lt;/code&gt;.&lt;br&gt;
On this page, we can fully own our user interface.&lt;br&gt;
We will then redirect to &lt;code&gt;api.example.com&lt;/code&gt; and handle the login flow from there.&lt;br&gt;
Once complete, we can set a cookie to the whole &lt;code&gt;example.com&lt;/code&gt; domain, allowing our users to be logged in to all our products!&lt;br&gt;
If we run a blog on &lt;code&gt;blog.example.com&lt;/code&gt; or docs on &lt;code&gt;docs.example.com&lt;/code&gt; we'd still have access to the user's cookie.&lt;br&gt;
By doing so, we've automatically achieved single sign on (SSO) for all our web applications.&lt;/p&gt;

&lt;p&gt;Is this complicated to set up?&lt;br&gt;
I've outlined the steps involved, so there's definitely some work to be done.&lt;/p&gt;

&lt;p&gt;If you're using WunderGraph, we've implemented this already for you.&lt;br&gt;
We're the headless API service mentioned above.&lt;br&gt;
Just deploy your apps to all subdomains you want, we'll provide &lt;code&gt;api.example.dom&lt;/code&gt; for you.&lt;br&gt;
The setup takes just a few minutes.&lt;/p&gt;

&lt;p&gt;First user story implemented, well almost.&lt;br&gt;
What about the tradeoffs?&lt;br&gt;
We've introduced cookies which means, we've created new security problems to solve, namely cross site request forgery (CSRF).&lt;br&gt;
For now, we don't want to solve the problem yet, just keep in mind that we have to tackle it later.&lt;/p&gt;
&lt;h3&gt;
  
  
  Users don't want to see loading spinners while the web application is figuring out if the user is authenticated or not
&lt;/h3&gt;

&lt;p&gt;As we've already covered a lot of ground with the last user story, this is going to be a lot easier.&lt;br&gt;
We've learned in the last story that we're able to set a cookie for the user on the whole &lt;code&gt;example.com&lt;/code&gt; domain.&lt;br&gt;
This means, we're able to access this cookie via server-side rendering and don't have to rely on the client trying to figure out if the user is authenticated or not.&lt;br&gt;
The result is that there are no redirects at all and all the loading spinners are gone.&lt;br&gt;
The server-side rendering process knows if the client is authenticated and can inject some data to tell the JS client about it.&lt;/p&gt;

&lt;p&gt;This makes for a super smooth user experience.&lt;br&gt;
The "time to usable application" is reduced by multiple seconds (redirects take time) and you can easily "refresh" the page.&lt;/p&gt;
&lt;h3&gt;
  
  
  Users want to see content immediately if they're authenticated
&lt;/h3&gt;

&lt;p&gt;More often than not, when you refresh a single page application (SPA) you're automatically reset to the landing page or even worse,&lt;br&gt;
the website logs you out automatically.&lt;br&gt;
With the cookie approach, the server can pre-render the website for individual users because it knows if the user is logged in and what their userID is.&lt;/p&gt;

&lt;p&gt;Metrics like "time to interactive" are meaningless if you're not able to do anything with the application.&lt;br&gt;
What's important to the user is "time to usable application", the time until the authenticated user can see real content and can interact with it.&lt;br&gt;
With a cookie set across all subdomains, we're able to reduce this metric across all our applications to a minimum.&lt;/p&gt;
&lt;h3&gt;
  
  
  Users want to single sign on (SSO) into all our products
&lt;/h3&gt;

&lt;p&gt;We've already covered this one in the first user story.&lt;br&gt;
Due to the nature of browsers, we're able to set cookies across all subdomains.&lt;br&gt;
SSO is already solved, no expensive enterprise tooling required.&lt;br&gt;
All you have to do is run all your apps on subdomains of &lt;code&gt;example.com&lt;/code&gt; which should be feasible.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Developer Experience of implementing Authentication
&lt;/h2&gt;

&lt;p&gt;With that we've already covered all stories directly related to the end-users.&lt;br&gt;
Next, let's focus on the developer experience.&lt;/p&gt;
&lt;h3&gt;
  
  
  We don't want to too much extra code, frameworks, etc. to our NextJS application just to implement authentication
&lt;/h3&gt;

&lt;p&gt;As previously mentioned, the majority of the authentication code runs on &lt;code&gt;api.example.com&lt;/code&gt; which you don't have to maintain.&lt;br&gt;
Because our api service is headless, we'll still have to build a login screen but this shouldn't take too much effort.&lt;br&gt;
All the login flows are being handled by &lt;code&gt;api.example.com&lt;/code&gt; so all we have to do is build the login forms and delegate to the headless service.&lt;/p&gt;

&lt;p&gt;If the user is authenticated, we can always use their cookie and forward it to &lt;code&gt;api.example.com&lt;/code&gt;, even from server side rendering.&lt;br&gt;
This way, we don't even have to put any logic into our NextJS &lt;code&gt;/api&lt;/code&gt; routes because authentication will be handled by passing on the cookie to the headless API service.&lt;br&gt;
The API service also comes with a handy endpoint to check if the user is logged in: &lt;code&gt;.../user&lt;/code&gt;&lt;br&gt;
If you pass along the cookies from the user, this endpoint will return all claims for the current user,&lt;br&gt;
allowing you to server-render the website for them automatically or redirect them to the login page.&lt;/p&gt;

&lt;p&gt;So, no additional code on the backend side of NextJS.&lt;br&gt;
In the client, we add very little logic for our login form so that we can redirect the user to the headless API service if they start the login flow.&lt;/p&gt;

&lt;p&gt;Luckily, WunderGraph automatically generates this client for you.&lt;br&gt;
Assuming you've added GitHub as authentication service to your app,&lt;br&gt;
we'll generate a function on the client to initiate this login flow,&lt;br&gt;
making it as convenient as possible for you.&lt;/p&gt;
&lt;h3&gt;
  
  
  It should be easy to configure multiple authentication providers for our application
&lt;/h3&gt;

&lt;p&gt;I've seen it too many times that frameworks make authentication more complicated than it should be.&lt;br&gt;
Imagine you'd like to build an app that allows your users to log in with their GitHub account.&lt;br&gt;
There should be a way to securely store the client ID and secret for your GitHub application.&lt;br&gt;
Then, you should be able to call a &lt;code&gt;login.github()&lt;/code&gt; function in your frontend and call it a day.&lt;/p&gt;

&lt;p&gt;This is exactly how we've implemented the developer workflow.&lt;br&gt;
You can read more on the topic &lt;a href="https://wundergraph.com/docs/overview/features/authentication"&gt;in the docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here's an example config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;authentication&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;cookieBased&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="nx"&gt;authProviders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;github&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;github&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;clientId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GITHUB_CLIENT_ID&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;clientSecret&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GITHUB_CLIENT_SECRET&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The WunderGraph code generator creates a fully typesafe client for us.&lt;br&gt;
This client can read the current user and allows us to start the login flow or log out the user.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;IndexPage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;logout&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useWunderGraph&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user not logged in!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="s2"&gt;`name: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, email: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;github&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;                &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;logout&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;logout&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are some very powerful details hidden in the example.&lt;br&gt;
If you look closely to the config, you'll see the "id" of the GitHub auth provider is "github".&lt;br&gt;
This id is re-used to generate the client and gives you the &lt;code&gt;login.github()&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;As you can see, authentication can be simple and shouldn't take too much effort.&lt;/p&gt;
&lt;h3&gt;
  
  
  How to do authentication-aware data-fetching
&lt;/h3&gt;

&lt;p&gt;As outlined in the user story, the data fetching layer should be aware of authentication.&lt;br&gt;
As a backend developer, you always know if an operation requires authentication or not.&lt;br&gt;
Therefore, you could mark operations as public or private.&lt;/p&gt;

&lt;p&gt;If an operation is public, any (anonymous) user could run it.&lt;br&gt;
If an operation is private, the user must be authenticated.&lt;/p&gt;

&lt;p&gt;Most if not all API clients are not aware of this situation,&lt;br&gt;
therefore the frontend developer has to write custom code that checks if the user is authenticated if this is a requirement.&lt;br&gt;
They'll then manually inject the user credentials into the request.&lt;/p&gt;

&lt;p&gt;This is completely unnecessary.&lt;br&gt;
Once we define a GraphQL operation, it's already clear that this operation is public or private.&lt;br&gt;
Let's look at an example to showcase this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;mutation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;fromClaim&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EMAIL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;@fromClaim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NAME&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;createOnemessages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;connectOrCreate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;}}}}){&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a mutation from our &lt;a href="https://github.com/wundergraph/nextjs-typescript-postgresql-graphql-realtime-chat"&gt;realtime chat example&lt;/a&gt;.&lt;br&gt;
As you can see, we're injecting two claims into the mutation.&lt;br&gt;
This is a special feature of WunderGraph, it allows you to use information from the user's claims as variables for your Operations.&lt;br&gt;
As WunderGraph keeps GraphQL Operations entirely on the server, this is completely safe.&lt;/p&gt;

&lt;p&gt;If you're using the &lt;code&gt;@fromClaim&lt;/code&gt; directive, you're automatically marking the Operation as "private", meaning that authentication is required.&lt;/p&gt;

&lt;p&gt;At this point we know about the operation as well as that authentication is required for the user to execute it.&lt;br&gt;
So far, this is similar to most approaches, even if you don't use WunderGraph.&lt;/p&gt;

&lt;p&gt;The problem is that most frameworks drop this piece of information and ignore it.&lt;br&gt;
Code-Generators could integrate this piece if information to generate a smart client that is aware of the requirement for the user to be authenticated.&lt;/p&gt;

&lt;p&gt;To achieve our goal, the WunderGraph code generator takes all the information about the Operations and generates a very smart client.&lt;/p&gt;

&lt;p&gt;All you have to do is call the generated React Hook like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;mutate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;addMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;messageAdded&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useMutation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AddMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;refetchMountedQueriesOnSuccess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the user is authenticated, &lt;code&gt;addMessage()&lt;/code&gt; will immediately run.&lt;br&gt;
If the user is unauthenticated, &lt;code&gt;messageAdded&lt;/code&gt; will stay in the state of &lt;code&gt;requiresAuthentication&lt;/code&gt;.&lt;br&gt;
Additionally, once the operation is successful &lt;code&gt;refetchMountedQueriesOnSuccess&lt;/code&gt; will refetch all currently mounted queries.&lt;br&gt;
This is very handy as you don't have to manually update a client side cache.&lt;/p&gt;

&lt;p&gt;This is how a client should handle data fetching, right?&lt;/p&gt;
&lt;h3&gt;
  
  
  Implementing Server Side Rendering (SSR) for authenticated users
&lt;/h3&gt;

&lt;p&gt;Now, onto another important topic: Server Side Rendering!&lt;/p&gt;

&lt;p&gt;Ideally, we should be able to render the UI on the server, even for authenticated users.&lt;br&gt;
We've talked about the topic above already, so the goal is clear. However, it's not just about achieving the goal but also keeping things simple.&lt;/p&gt;

&lt;p&gt;As previously discussed, we're using a headless API service that handles authentication via cookies.&lt;br&gt;
We also mentioned that we can set cookies across all subdomains (e.g. docs.example.com) if we set the domain of the cookie to the apex domain (example.com).&lt;/p&gt;

&lt;p&gt;Alright, how does this look like from the developers perspective?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getServerSideProps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GetServerSideProps&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// for SSR, simply create a new client and pass on the cookie header from the client context&lt;/span&gt;
    &lt;span class="c1"&gt;// this way, we can make authenticated requests via SSR&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;extraHeaders&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="c1"&gt;// fetch the user so that we can render the UI based on the user name and email&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetchUser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// fetch the initial messages&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Messages&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// pass on the data to the page renderer&lt;/span&gt;
            &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ok&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;findManymessages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, it's quite simple.&lt;br&gt;
Create a client, pass on the cookie header from the user, fetch some data and pass it on to the page render function.&lt;/p&gt;

&lt;h3&gt;
  
  
  Authentication that actually works in all major browsers
&lt;/h3&gt;

&lt;p&gt;Getting cookie-based authentication to work might sound easy.&lt;br&gt;
However, it's actually non-trivial to make it both functional and secure across all major browsers can definitely be a pain.&lt;br&gt;
We've made sure that cookies are encrypted as well as http only.&lt;br&gt;
The SameSite config is as restrictive as possible.&lt;/p&gt;

&lt;p&gt;Don't worry if you're not familiar with all these terms.&lt;br&gt;
We've built our solution based on OWASP best practices so that you don't have to.&lt;/p&gt;

&lt;h3&gt;
  
  
  CSRF protection out of the box
&lt;/h3&gt;

&lt;p&gt;If you're familiar with OWASP, you've probably heard of Cross Site Request Forgery (CSRF).&lt;br&gt;
Cookie-based authentication can be very powerful but also dangerous if you don't handle CSRF properly.&lt;br&gt;
If the browser always sends the user's auth information with every request,&lt;br&gt;
how does the server know if it was the intention of the user to make that request?&lt;/p&gt;

&lt;p&gt;An attacker could trick the user into clicking a link which triggers an action, e.g. sending money to a bitcoin wallet,&lt;br&gt;
even if the user didn't want to send any money at all.&lt;/p&gt;

&lt;p&gt;What can we do about this?&lt;/p&gt;

&lt;p&gt;There are plenty of resources on the topic, so I'd try to keep it brief.&lt;br&gt;
Instead of a GET request, we always make a POST for mutations.&lt;br&gt;
Additionally, we'll transfer a csrf token from server to client and set a special csrf cookie.&lt;br&gt;
Once the csrf token is transferred to the client, the client will use it for all POST requests with a different header.&lt;br&gt;
This means, an attacker can't just create a URL and have the user click on it.&lt;/p&gt;

&lt;p&gt;If you don't want to implement this yourself, just use WunderGraph.&lt;br&gt;
We automatically generate a client that takes care of CSRF protection out of the box.&lt;br&gt;
Nothing needs to be done on your side.&lt;/p&gt;

&lt;p&gt;You can actually see all this in action in &lt;a href="https://github.com/wundergraph/nextjs-typescript-postgresql-graphql-realtime-chat"&gt;one of our demos&lt;/a&gt;.&lt;br&gt;
This is a Chat Application example.&lt;br&gt;
If you Log in and try to send your very first message, you'll see a &lt;code&gt;/csrf&lt;/code&gt; call in the network tab, that's the exchange of the csrf token.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary &amp;amp; Demo
&lt;/h2&gt;

&lt;p&gt;I know it was a lot of content to digest.&lt;br&gt;
At the same time, isn't the end result what both end-users and developers are asking for?&lt;br&gt;
Applications that require authentication should be easy to use, fast and secure while being easy to implement and maintain for the developers.&lt;/p&gt;

&lt;p&gt;I really hope we're able to contribute to this goal, making applications better while saving developers a lot of time.&lt;/p&gt;

&lt;p&gt;If you want to see all the practices described in action, clone &lt;a href="https://github.com/wundergraph/nextjs-typescript-postgresql-graphql-realtime-chat"&gt;the demo&lt;/a&gt;&lt;br&gt;
and try it out yourself!&lt;/p&gt;

&lt;p&gt;I'd be more than happy to hear your feedback!&lt;/p&gt;

</description>
      <category>authentication</category>
      <category>nextjs</category>
      <category>ssr</category>
      <category>spa</category>
    </item>
    <item>
      <title>Why not use GraphQL? Misconceptions, why REST is superior but still draws the short straw</title>
      <dc:creator>Jens Neuse</dc:creator>
      <pubDate>Sat, 07 Nov 2020 11:54:26 +0000</pubDate>
      <link>https://dev.to/jensneuse/why-not-use-graphql-misconceptions-why-rest-is-superior-but-still-draws-the-short-straw-2fal</link>
      <guid>https://dev.to/jensneuse/why-not-use-graphql-misconceptions-why-rest-is-superior-but-still-draws-the-short-straw-2fal</guid>
      <description>&lt;p&gt;I think GraphQL will change the world. There will be a future where you can query any system in the world using GraphQL. I'm building this future. So why would I argue against using GraphQL? My personal pet peeve is when the community keeps advertising benefits of GraphQL that are very generic and really have nothing to do with GraphQL. If we want to drive adoption, we should be honest and take off the rose-tinted glasses. This post is a response to "Why use GraphQL" by Kyle Schrade (&lt;a href="https://www.apollographql.com/blog/why-use-graphql/"&gt;https://www.apollographql.com/blog/why-use-graphql/&lt;/a&gt;). It’s not meant to be direct criticism. The article is just an excellent base to work with as it represents opinions I keep hearing a lot in the community. If you read the whole article, it’ll take some time, you’ll fully understand why I think Kyle’s article should be named “Why use Apollo”.&lt;/p&gt;

&lt;p&gt;If you haven't read Kyles's article already, I think it makes most sense if you read it first: &lt;a href="https://www.apollographql.com/blog/why-use-graphql/"&gt;https://www.apollographql.com/blog/why-use-graphql/&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Downside of REST
&lt;/h1&gt;

&lt;p&gt;The author states that REST APIs come with a set of downsides and how GraphQL solves all of them:&lt;br&gt;
Over-fetching&lt;br&gt;
Multiple requests for multiple resources&lt;br&gt;
Waterfall network requests on nested data&lt;br&gt;
Each client need to know the location of each service&lt;/p&gt;

&lt;p&gt;The first three issues could be solved by writing another REST API as a facade for a specific user interface. Take Next.JS as an example. Next lets you define APIs with a very lightweight syntax. Instead of making multiple requests from the client, you can wrap those calls into an API and make them server-side. Over and underfetching can be solved with this approach too, as you can manipulate the data before sending it back to the client. The pattern described is named "backend for frontend" (BFF). It's not limited to full stack frameworks like Next.JS. You can build a BFF for your mobile apps as well.&lt;/p&gt;

&lt;p&gt;With the BFF pattern, the client itself doesn't have to know the location of each service. However, the developer who implements the BFF needs to understand the service landscape. Hopefully you have Open API Specifications for all your services, nicely presented in a developer portal. If that's the case, it should be easy to write a BFF.&lt;/p&gt;

&lt;p&gt;With GraphQL, there still needs to be a developer who implements the resolvers. Implementing the resolvers is more or less the same task as building a BFF, the logic is very similar. So, what's the real difference?&lt;/p&gt;

&lt;p&gt;The BFF is easier to implement as there's a lot more tooling available. E.g. if you use a framework like Next.JS in combination with swr hooks (stale while revalidate) you get automatic caching with Etags and cache invalidation out of the box. This reduces the amount of data sent between server and client. It's even less data than GraphQL, because you're not sending query payloads and the server responds with 304 (Not Modified) if the response is still valid. Additionally, you don't have to use a heavyweight client like Apollo. The library swr by Vercel is small and very easy to use. It comes with support for pagination, hooks, and helps to navigate back and forth very efficiently.&lt;/p&gt;

&lt;p&gt;GraphQL has persisted queries but it comes with additional overhead to implement this. If you don't use a client like Relay, which persists Queries by default, you have to do it on your own or use some third party library to implement it. Compared to the BFF approach using e.g. Next.JS there's a lot more complexity involved in getting to the same results on the frontend. How would you implement Etags with GraphQL? How do you make your GraphQL server return 304 status codes if nothing changed? Don't you first have to turn all Queries into GET requests? If so, does your GraphQL client and server easily support this?&lt;/p&gt;

&lt;p&gt;When it comes to user experience and ease of development, the BFF is the clear winner. Less data transfer between client and server. Easier to implement. Smaller client, less moving parts.&lt;/p&gt;

&lt;p&gt;But there's a catch. You have to build a BFF for each individual frontend. If you have many of them this can be a lot of work. You have to maintain all the BFFs. You have to operate them. You have to secure them.&lt;/p&gt;

&lt;p&gt;Wouldn't it be nice if you could have the benefits of both without making tradeoffs? This is exactly what WunderGraph is. A framework to build BFFs using GraphQL.&lt;/p&gt;
&lt;h1&gt;
  
  
  No more versioned APIs
&lt;/h1&gt;

&lt;p&gt;In the next paragraph, Kyle goes on with the problems involved with versioned APIs. He's absolutely right that having too many versions of an API makes it very hard to keep track of. He then concludes that in GraphQL, there's only one version of the graph and changes can be tracked in a schema registry, a paid feature of Apollo. For that reason you won’t have any problems with versioning, he says.&lt;/p&gt;

&lt;p&gt;I have problems coming to the same conclusion. Just because GraphQL schemas don’t support versioning natively doesn’t mean the problem goes away. You get the same effect if you just don’t version your REST APIs. In fact, many experts say that you should always try to not introduce versions of an API if you don’t have to. That being said, what holds you off running two versions of your GraphQL schema? Not that I think this is a good idea but it's technically possible.&lt;/p&gt;

&lt;p&gt;If having too many versions of your REST APIs is a problem in your organization, before throwing a new tool like GraphQL at the problem, maybe you should have a look at the organization first. What are the reasons for having so many versions? Maybe the change of a process or new team structures can help? GraphQL does absolutely nothing to solve your versioning problems. Instead I think it actually makes the situation worse.&lt;/p&gt;

&lt;p&gt;Do you have to support mobile applications? You should be aware that shipping native apps takes time. You have to wait for app store approval and you can expect many of your users to never (or slowly) install the new version. What if you want to introduce a breaking change in this scenario without breaking a client? It's impossible. You have to introduce this change in a non-breaking way. It would be interesting to hear from Facebook how they avoided breaking clients.&lt;/p&gt;

&lt;p&gt;Evolving your schema in the case of GraphQL would mean, you deprecate the old field and add a new one. New clients use the new field while you hope that the number of clients using the old field will get less and less. Hopefully, you have a system in place that forces your users to download a new version at some point in time. Otherwise, you might be forced to support the deprecated field indefinitely. If that's the case, the deprecation model of GraphQL doesn't help you at all.&lt;/p&gt;

&lt;p&gt;With REST you could create a new endpoint or another version of an existing one. The problem is the same, the solution just looks a bit different.&lt;/p&gt;

&lt;p&gt;To make it clear, if you cannot control your clients you really want some kind of versioning. If all you have is a single web application you won’t need this feature. But then again GraphQL might be overkill as well.&lt;/p&gt;
&lt;h1&gt;
  
  
  Smaller payloads
&lt;/h1&gt;

&lt;p&gt;In this paragraph, the author states that RESTful APIs don't allow partial responses.&lt;/p&gt;

&lt;p&gt;This is just wrong. Here's an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /users?fields=results(gender,name)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What does the author actually mean? I'm pretty sure he's aware of partial responses. I guess what he's trying to say is that someone needs to implement partial responses. Actually, it looks very familiar to GraphQL as you're selecting subfields from a resource. With GraphQL we have this feature out of the box.&lt;/p&gt;

&lt;p&gt;On the other hand, with the BFF approach, you don't need this. Just return exactly the data you need. Again, a full-stack framework like Next.JS makes it simpler to implement this, makes caching easier and gives you Etag based cache invalidation for free.&lt;/p&gt;

&lt;p&gt;To sum this section up, GraphQL gives you exactly the data you need. Partial responses can achieve the same result. BFFs come with the additional cost of implementation and maintenance but have a better UX &amp;amp; DX.&lt;/p&gt;

&lt;h1&gt;
  
  
  Strictly-typed interfaces
&lt;/h1&gt;

&lt;p&gt;In this paragraph, Kyle addresses the issues of REST APIs not being strictly typed. He talks about the problems with APIs where it's not clear if you get an array of posts or something different and how query parameters complicate the situation. He also states that GraphQL, because of its strict type system, doesn't have this problem.&lt;/p&gt;

&lt;p&gt;I think what Kyle is talking about is an organizational problem for which you need an organizational solution.&lt;/p&gt;

&lt;p&gt;You have the kind of problems he describes, when you allow developers to deploy REST APIs without publishing Open API Specifications (OAS) or similar. With OAS all resources can be described very easily. OAS also allows you to describe OAuth2 flows and required scopes per endpoint. Additionally, you can describe the exact types and validation rules for query parameters, a feature that GraphQL is lacking.&lt;/p&gt;

&lt;p&gt;Looking at GraphQL, there's no way to describe Authentication, Authorization and input validation. GraphQL is lacking these features because the inventors at Facebook solved this problem at a different layer. There was no need for them to add these features to GraphQL. You can add custom directives to your schema to achieve similar results like OAS but this would be a custom implementation which you have to maintain yourself.&lt;/p&gt;

&lt;p&gt;You might be thinking that OAS doesn't guarantee the response of an API to be compliant with the specification. You would be right. But how does a GraphQL schema guarantee anything?&lt;/p&gt;

&lt;p&gt;GraphQL introspection is the act of sending a specific GraphQL query to the server to get information about the GraphQL schema. The GraphQL server is free to answer with whatever types it wants to. If you send a Query, the server can answer with a response that doesn't adhere to the GraphQL schema from the introspection response. Take Apollo Federation as an example. You upload your schema into a schema registry and then, by error, deploy the wrong version of your GraphQL server. If you change the type of a field, the client might be confused.&lt;/p&gt;

&lt;p&gt;When we talk about type safety in GraphQL, what we actually mean is that we trust in a GraphQL server to behave exactly as advertised by the introspection Query response. Why can't we trust an Open API Specification in the same way? I think we can. If we don't, we have a people problem, not a technical one.&lt;/p&gt;

&lt;h1&gt;
  
  
  Better client performance
&lt;/h1&gt;

&lt;p&gt;The next paragraph, a short one, is about how GraphQL improves client performance and reduces network round trips.&lt;/p&gt;

&lt;p&gt;I think I've explained more than enough how much more powerful a BFF is and how much you gain from the ‘stale while revalidate pattern’ compared to a heavyweight GraphQL client.&lt;/p&gt;

&lt;p&gt;That said, GraphQL does indeed reduce the number of requests and reduces the overall data transfer. However, you should always consider the cost of adding a GraphQL client to your frontend.&lt;/p&gt;

&lt;h1&gt;
  
  
  Less time spent documenting and navigating APIs
&lt;/h1&gt;

&lt;p&gt;The next section is about how tools like OAS are being used for RESTful API development and the challenges of maintaining multiple OAS in microservice environments. Kyle compares a single GraphQL schema with Swagger files spread out across multiple git repositories.&lt;/p&gt;

&lt;p&gt;I think it's clear that navigating a single GraphQL schema is a lot simpler than looking at multiple OAS files, sitting in git repositories. However, to be fair, we have to compare apples to apples. If you want to make your developers productive, you wouldn't stick OAS files into a git repository and call it a day. You would run a developer portal where you can search for APIs and navigate between them.&lt;/p&gt;

&lt;p&gt;OAS relies on JSON-Schema which comes with an amazing feature: You can reference object types from another document. You can divide your OAS into multiple files which reference each other if required. There's also tooling to combine multiple OAS files into a single OAS document. You could then use this document and feed it into a developer portal which lets you explore all APIs as a whole. Keep in mind that there's the additional cost of setting up all this. You need to run a dev portal or buy one. You have to describe all your APIs, which, at least at the beginning, can be a burden.&lt;/p&gt;

&lt;p&gt;One thing to add, there are a lot of frameworks that let you describe a schema in your favourite programming language, e.g. through defining Objects or Classes. You'll then get an auto-generated Open API specification served at a well-known endpoint.&lt;/p&gt;

&lt;p&gt;Let's compare that to GraphQL. There are basically two approaches, SDL first vs. code first. Whatever way you go, you end up with a GraphQL schema which describes all your types as well as fields and allows you to comment on them.&lt;/p&gt;

&lt;p&gt;So, what's the difference then? OAS comes with more overhead to set things up. On the other hand, OAS has built-in support documenting example use cases, authentication &amp;amp; authorization as well as input validation rules.&lt;/p&gt;

&lt;p&gt;Keep in mind that GraphiQL itself has no concept of multiple GraphQL schemas. If you have multiple GraphQL (micro-)services you have to run or buy a dedicated component, e.g. a schema registry which is similar to a developer portal for REST APIs.&lt;/p&gt;

&lt;p&gt;One thing I wanted to dedicate an extra paragraph is API use cases. Just because you have an OAS or a GraphQL schema doesn't mean your API is well documented. What can an API user do with the API? How can they use it? What are good use cases? What are the bad ones? Where to ask for help? How do I authenticate a user? Do I need an API key? Documenting your API in a way that helps API consumers use it is much more work than adding descriptions to types and fields. OAS allows you to add example payloads and describe them. GraphQL is lacking this feature. Look at Stripe for a positive example of this. They went way beyond what a Swagger or GraphQL Playground could ever achieve.&lt;/p&gt;

&lt;p&gt;If, on the other hand, you look at the public GraphQL API of GitHub you will find not a single example Query. Let's say you want to get information about a repository and all its issues. You'd have to open GraphiQL and start searching. However, the search functionality in GraphiQL doesn't really help you much. Someone needs to sit down and write example Queries and use cases on how to use the API. Otherwise, it's really hard to get started.&lt;/p&gt;

&lt;p&gt;So, while the community keeps saying "GraphQL is self-documenting" this feature alone doesn't make a useful API. OAS gives you a tool to add use cases but you still have to write them. It takes effort to make an API useful to others, no matter what tools or languages you chose.&lt;/p&gt;

&lt;h1&gt;
  
  
  Legacy app support
&lt;/h1&gt;

&lt;p&gt;In this paragraph, the author says that keeping around old versions of REST APIs for mobile apps is a pain. He concludes that because we're only using a single GraphQL server, we don't have this problem.&lt;/p&gt;

&lt;p&gt;I'm sorry, but again, I get to a completely different conclusion. If you set the rule to disallow versioning, you can add new endpoints or swap the implementation of existing ones. There is no difference between GraphQL and REST in this case. Supporting legacy apps is a challenge with both REST and GraphQL APIs. You need to find a way to not break the contract between client and server. It doesn't matter if your server exposes REST or GraphQL, the problem is the same.&lt;/p&gt;

&lt;h1&gt;
  
  
  Better error handling
&lt;/h1&gt;

&lt;p&gt;On error handling, the author describes a scenario where a client would have to make 3 subsequent REST API calls compared to a single GraphQL query that would respond with partial data.&lt;/p&gt;

&lt;p&gt;With GraphQL, the logic of resolving partial data sits in the server. The client needs to have additional logic to react to a partial response appropriately.&lt;/p&gt;

&lt;p&gt;With REST the logic of fetching partial data could sit in the client or in a BFF. Either way, the logic is more or less the same as with GraphQL, it just sits somewhere else. Obviously, the REST API use case also needs logic in the client to handle a partial response. This logic will be almost identical to the one in the GraphQL use case.&lt;/p&gt;

&lt;p&gt;Nothing holds you off from returning specific information in a REST response on why something failed. OAS allows union types so you're free to give rich information to the client about a partial response. This is similar to the concept of response unions described by Sascha Solomon (&lt;a href="https://sachee.medium.com/200-ok-error-handling-in-graphql-7ec869aec9bc"&gt;https://sachee.medium.com/200-ok-error-handling-in-graphql-7ec869aec9bc&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Does GraphQL really have better error handling? I think both OAS and GraphQL give you good tooling to handle errors in a very user-friendly way. It's up to the developer to make good use of these tools. There's no free lunch.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Kyle concludes the whole article by saying that GraphQL is the future of APIs because it's superior in terms of performance, payload size, developer time and built-in documentation.&lt;/p&gt;

&lt;p&gt;I agree that GraphQL is the future of APIs, but for different reasons.&lt;/p&gt;

&lt;p&gt;Better performance and smaller payload size are no unique features to GraphQL. You can achieve better results with other tools or have to extend GraphQL, e.g. using Relay to get persisted Queries. To get real benefits from GraphQL documentation you definitely have to do more than just adding descriptions to your schema.&lt;/p&gt;

&lt;p&gt;Once the dust is settled and the hype is gone, we have to look at the facts. We should not try and convince the world that GraphQL is something which it isn't. There are a lot of advantages of using GraphQL but depending on your use case you might not really benefit from them. Instead of advertising GraphQL as the holy grail, we should give a more nuanced response.&lt;/p&gt;

&lt;p&gt;The best way to tackle this is to look at the problem first and then make a distinctive comparison of possible tools to solve a problem. If your organization fails at implementing REST APIs, how does GraphQL solve this problem? Maybe something within your organization has to change? If, on the other hand, it's not an organizational problem, and you're absolutely sure REST is no good alternative for your use case, I bet you will love the developer experience of GraphQL.&lt;/p&gt;

&lt;h1&gt;
  
  
  My version of "Why GraphQL"
&lt;/h1&gt;

&lt;p&gt;GraphQL in itself is barely useful. It’s the tools that make GraphQL so powerful. It’s the community, it’s us! It’s companies like Apollo, Hasura, The Guild, FaunaDB, Dgraph, GraphCMS and I hope WunderGraph too, who make GraphQL so powerful. It’s GraphiQL, various GraphQL clients, Schema Stitching, Federation. This whole ecosystem of tools is the reason why GraphQL is the next big thing.&lt;/p&gt;

&lt;p&gt;More tools and services will strengthen the ecosystem. A stronger ecosystem will lead to more adoption which again will lure in more companies to add services and tools to the GraphQL ecosystem, a very positive loop.&lt;/p&gt;

&lt;p&gt;GraphQL in that regard is very similar to Kubernetes. Docker, the container runtime, wasn’t enough. Wrapping complex Syscalls into a simple to use API was the enabler but in order to create a rich ecosystem a scheduler was needed that was expressive enough and allowed very easy extensibility.&lt;/p&gt;

&lt;p&gt;GraphQL gives us a language to define and consume APIs with the same simplicity as Docker. If you have seen a few lines of Javascript and JSON before, GraphQL immediately feels familiar. But similar to Docker, the language itself is not that powerful. It’s the extensibility and the tools around it.&lt;/p&gt;

&lt;p&gt;The language open-sourced by Facebook is not what made it successful. It’s tools like Relay that did. Unfortunately, many of the tools used internally never made it to the public. The community had to catch up with this which I think we’ve got pretty far already.&lt;/p&gt;

&lt;h1&gt;
  
  
  My personal Conclusion
&lt;/h1&gt;

&lt;p&gt;When Kyle asks “Why GraphQL” I think what he actually means is “Why Apollo”. And the answer is simple. No one cared to build a rich ecosystem around REST APIs. Try to generate a React.JS client from an Open API Specification. The experience sucks, compared to what GraphQL clients give you. No one figured out a good business model to solve the problem. Enter GraphQL and you get a massive amount of tools to abstract away problems you don’t want to deal with.&lt;/p&gt;

&lt;p&gt;REST APIs will become less relevant for the described use cases not because GraphQL is superior. It’s the tools and ecosystem that will make GraphQL continue to gain market share. The pace at which the GraphQL ecosystem is expanding is massive compared to REST.&lt;/p&gt;

&lt;p&gt;Hypermedia APIs played and still play a big role for server rendered web applications. However, the web is moving forward. Users expect an experience as native as it could be from websites. The Jamstack is taking over on the frontend. Hybrid models with server side rendering and dynamic Javascript clients are the enablers of these applications. RESTful APIs excel at a different set of problems. They will not go away, quite the opposite! They’re just not the right tool for this style of applications the industry is currently shifting towards. I think REST APIs are a perfect tool for internal APIs, partner APIs and server to server communication. This is an area where GraphQL doesn’t really bring any benefits over REST. Alongside RPC it will have a great future in this area. GraphQL on the other hand is more than happy to wrap resource- and RPC-based APIs.&lt;/p&gt;

&lt;p&gt;Do you think GraphQL would have become what it is without all the tooling by Apollo? What about the conferences? GraphQL Summit? Hasura online conferences? GraphQL in Space by Dgraph? What about the massive open source contributions by The Guild? GraphQL Galaxy?&lt;/p&gt;

&lt;p&gt;I hope this gave you a more nuanced view of why you should use GraphQL. A less hype-loaded view which should prepare you well to convince your manager.&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>rest</category>
      <category>apollo</category>
    </item>
    <item>
      <title>GraphQL is not meant to be exposed over the internet</title>
      <dc:creator>Jens Neuse</dc:creator>
      <pubDate>Tue, 13 Oct 2020 12:01:29 +0000</pubDate>
      <link>https://dev.to/jensneuse/graphql-is-not-meant-to-be-exposed-over-the-internet-2c53</link>
      <guid>https://dev.to/jensneuse/graphql-is-not-meant-to-be-exposed-over-the-internet-2c53</guid>
      <description>&lt;p&gt;GraphQL is currently one of the most frequently mentioned technologies when it comes to innovation in the API economy. Adopters enjoy the ease of use and tooling like for example GraphiQL, the browser-based user interface to try out any GraphQL API. The whole experience of GraphQL is exactly what frontend-developers need to build amazing interactive web applications.&lt;/p&gt;

&lt;p&gt;However, with the rise of adoption, I'm starting to get more and more concerned about the way people understand GraphQL and use it. In this post, I'd like to share my unpopular opinion on what GraphQL really is meant to be and why you should be concerned if you're using it the popular-but-risky way.&lt;/p&gt;

&lt;h2&gt;
  
  
  API styles
&lt;/h2&gt;

&lt;p&gt;Let's take a step back and discuss APIs and API styles in general before answering the main question of why you're probably using GraphQL the wrong way.&lt;/p&gt;

&lt;p&gt;APIs offer a way to hide the complexity of the implementation behind a user-friendly interface. For example, a shopping basket can have methods to add and delete items or to move forward to the checkout. As a user of this shopping cart API, you don't have to think about how the data gets stored or what exactly happens when you add or remove an item.&lt;/p&gt;

&lt;p&gt;Over the last few decades various styles of APIs have emerged, all with different implementations, depending on the use cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  You probably don't need GraphQL
&lt;/h2&gt;

&lt;p&gt;If you'd like to choose the right API style for a problem, you also have to consider how the API gets published and used. Do you know all your users and use cases? Are these users part of your own organization? Are they partners? The answers will most probably influence your choice of the API style and implementation, doesn't it?&lt;/p&gt;

&lt;p&gt;The last sentence is where I think we get it wrong a lot of the time. I see people all over the place choose the API style and implementation long before the important questions were answered.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do you have Facebook scale Problems?
&lt;/h2&gt;

&lt;p&gt;The current most popular example of this behaviour is GraphQL. Are you building a modern single page application with React? Awesome, use GraphQL! Facebook, Airbnb, Paypal, Netflix, they all do it so it must be a good fit.&lt;/p&gt;

&lt;p&gt;Why don't we see more discussions around choosing the right technology for a given problem? I assume it's a lack of education, but I'm not sure on this one. If you have any relevant degree, you might respond to this with your experience of education on APIs.&lt;/p&gt;

&lt;p&gt;Always keep in mind, if you use Facebook-scale tooling without having a Facebook-scale organization and Facebook-scale problems, you might realize painfully that you're using a sledgehammer to crack a nut. It's the same reason why chaos monkey makes sense for Netflix while it doesn't for your two docker containers running on a 5$ machine on digital ocean.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is GraphQL getting so popular?
&lt;/h2&gt;

&lt;p&gt;GraphQL simplifies the communication between API developer and API consumer. API consumers, often frontend developers, get lots of change requests from product owners which lead to changing requirements on the API. With GraphQL, you have a good chance of not being forced to talk to the developer of the API. You change the Query and can get back to CSS and Javascript.&lt;/p&gt;

&lt;p&gt;I assume this was one of the main drivers at GitHub to choose GraphQL as an implementation of the query-based API style for their new API. Their API is publicly available. They have big numbers of API consumers, all with different requirements. They can't build resource-based APIs that satisfy all of their users. In this particular use-case, GraphQL might actually be a good choice. Instead of trying to solve each problem, they rather offer a generic GraphQL API.&lt;/p&gt;

&lt;h2&gt;
  
  
  You're probably not GitHub, are you?
&lt;/h2&gt;

&lt;p&gt;What are the trade-offs that GitHub is willing to accept when publicly exposing a GraphQL API? They have a whole team behind their GraphQL API, making sure you, the user, does not accidentally or intentionally break their systems. You can watch videos of them talking at conferences about the complex systems they built to secure their API and keep it stable. They've built tooling for GraphQL specific analytics to get better insights into API usage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do you fully understand the risks?
&lt;/h2&gt;

&lt;p&gt;I assume that many developers with a focus outside of security have little experience on what it takes to secure a REST API exposed on the internet. Most of us have little experience implementing authentication, authorization, rate limiting etc. . However, I think securing a RESTful API is rather simple, compared to a GraphQL API. Any HTTP-based API framework lets you define your routes and attach standardized middlewares to solve the problems listed above. A single HTTP call always corresponds to a single call on the controller of an API. With GraphQL on the other hand, a single Query might result in thousands of calls on the controllers (resolvers) of the API. There is no simple way to solve this problem.&lt;/p&gt;

&lt;p&gt;Depending on the language you use, various libraries are trying to help you with the issue. How trustful are these libraries? Do you fully understand how they work? Are there edge cases we're not yet fully aware of?&lt;/p&gt;

&lt;h2&gt;
  
  
  Will you benefit as much as GitHub does?
&lt;/h2&gt;

&lt;p&gt;Are you a single developer working on a side project? Do you benefit as much as you're expecting from using GraphQL? Are you using many different clients with different data needs? Do you really need a query-based API? What's your strategy to combat the problems listed above?&lt;/p&gt;

&lt;h2&gt;
  
  
  But I'm not exposing my GraphQL API
&lt;/h2&gt;

&lt;p&gt;You might be thinking that your GraphQL API is not really exposed. It's used on your website, but you don't show the playground anywhere. If you're using a GraphQL client in the frontend that directly talks to your GraphQL API, this API is exposed, even if not visually exposed with a GraphQL playground.&lt;/p&gt;

&lt;h2&gt;
  
  
  Am I leaking sensitive information?
&lt;/h2&gt;

&lt;p&gt;Do you allow any client to invoke the introspection Query? Are you leaking sensitive information through the introspection Query? Are you planning a new feature on the UI which will be made public in a few weeks or months? Is this feature already visible to your competition if they look at your schema? What if someone scrapes your schema every day to track changes and try attacks whenever you update your schema?&lt;/p&gt;

&lt;h2&gt;
  
  
  Schema traversal attacks
&lt;/h2&gt;

&lt;p&gt;Are you aware of schema traversal attacks? A user might be allowed to see his own account balance, but how about his/her friends? Is it possible to traverse the schema in a way you didn't anticipate which leaks data? How do you test for this kind of behaviour and ensure it's not possible for your own schema?&lt;/p&gt;

&lt;h2&gt;
  
  
  Bug bounties everywhere
&lt;/h2&gt;

&lt;p&gt;Is there a reason why companies like Shopify participate in bug bounty programs? They seem to be aware of the complexity of securing a GraphQL API. They invite security experts to help them make their publicly available GraphQL API more secure. Do you realize that your GraphQL API is as vulnerable as Shopify's?&lt;/p&gt;

&lt;h2&gt;
  
  
  The most secure GraphQL server
&lt;/h2&gt;

&lt;p&gt;How to make a system 100% secure to any kind of remote attack? If you want to be 100% safe, you should consider unplugging the network cable. However, this comes with some inconvenient drawbacks. You probably don't want to store your GraphQL query on a USB dongle, walk to the remote computer and execute it manually, then copy the response back on the dongle and walk back to your own computer.&lt;/p&gt;

&lt;p&gt;What's in between an unplugged network cable and exposing GraphQL? How about reducing the complexity to the level of a REST or RPC-based API while keeping the advantages of a query-based API?&lt;/p&gt;

&lt;h2&gt;
  
  
  GraphQL as a server-side language
&lt;/h2&gt;

&lt;p&gt;If we primarily use GraphQL on the server to define JSON-RPC APIs, we get the best of both worlds. The flexibility of GraphQL combined with the security and predictable performance of an RPC-based API.&lt;/p&gt;

&lt;h2&gt;
  
  
  The GraphQL specification is designed for this
&lt;/h2&gt;

&lt;p&gt;The GraphQL spec allows us to define multiple Operations (Queries, Mutations, Subscriptions) in a single GraphQL document. In addition to this, the validation rules of the spec require all Operations in a GraphQL document to be named. There's just one exception which allows a single anonymous Query. But in case the number of operations in a document are above 1 we're already forced to name our Operations. Another important requirement is that all Operation names must be unique. That is, there shall be no two Operations with the same name.&lt;/p&gt;

&lt;h2&gt;
  
  
  A set of GraphQL Operations is a JSON-RPC API
&lt;/h2&gt;

&lt;p&gt;The design of the GraphQL specification alongside with the validation rules builds a perfect foundation for what we're trying to achieve here.&lt;/p&gt;

&lt;p&gt;If we want to define a new JSON-RPC API, all we have to do is create a new file containing a set of GraphQL Operations. Each Operation has a unique name. This name becomes the function name of the JSON-RPC. The Operation variables become the input of the RPC call.&lt;/p&gt;

&lt;p&gt;Next, we can "deploy" all Operations on our API backend and prepare the RPC Endpoints. Finally, based on the Operations and the known RPC-Endpoints we're able to generate a client that knows about the schema as well as all RPC endpoints.&lt;/p&gt;

&lt;h2&gt;
  
  
  JSON-RPC-GraphQL compared to exposed GraphQL
&lt;/h2&gt;

&lt;p&gt;Pros:&lt;br&gt;
input and outputs are typesafe&lt;br&gt;
the attack surface is reduced&lt;br&gt;
you know all the Operations a client is using&lt;br&gt;
the generated client is very small, compared to a thick GraphQL client, which leads to smaller JS bundle size&lt;br&gt;
less bandwidth usage because we're not sending Operations but just making RPC calls&lt;br&gt;
Query Parsing, Normalization &amp;amp; Validation happens at compile-time, not at runtime, making it more secure and performant&lt;br&gt;
no exposed GraphQL endpoint and therefore no exposed introspection either&lt;br&gt;
graph traversal attacks are impossible as the graph is not exposed anymore&lt;br&gt;
you know in advance when a change to the schema or one of the Operations would break a client and can mitigate this&lt;br&gt;
JSON-RPC turns any GraphQL Query into a GET request and therefore makes them easily cacheable at the transport layer&lt;br&gt;
because Operations are stored on the backend and never exposed to the client, you're able to put authorization logic into the Operations&lt;br&gt;
Cons:&lt;br&gt;
you can no longer use your favourite GraphQL client&lt;br&gt;
you have to run a code generator whenever you update the Schema or any of the Operations&lt;br&gt;
you need to compile and store all Operations on the API backend&lt;br&gt;
this approach does only work when the API user is allowed to prepare and store Operations on the API backend&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Using GraphQL as a framework for building JSON-RPC APIs is not a solution to every problem. There are situations where it's not feasible or simply technically impossible. However, a lot of GraphQL users can benefit from this approach, as it increases security and performance at marginal costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is implementing this approach your business?
&lt;/h2&gt;

&lt;p&gt;For most of you, the answer to this question will probably be "no". At WunderGraph, our goal is to make APIs easy to use, secure and performant. We've already implemented the approach outlined above. If you don't want to re-invent the wheel, we'd be more than happy to work with you together. We focus on the plumbing so you can solve problems of your own business domain and not waste your time.&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>api</category>
      <category>security</category>
    </item>
    <item>
      <title>@deprecate WebSockets: GraphQL Subscriptions using SSE</title>
      <dc:creator>Jens Neuse</dc:creator>
      <pubDate>Sat, 26 Sep 2020 14:42:21 +0000</pubDate>
      <link>https://dev.to/jensneuse/deprecate-websockets-graphql-subscriptions-using-sse-2657</link>
      <guid>https://dev.to/jensneuse/deprecate-websockets-graphql-subscriptions-using-sse-2657</guid>
      <description>&lt;p&gt;WebSockets are an outdated technology deemed dead thanks to the ietf not adding HTTP/2 support. Using Server Sent Events, we could simplify the implementation of GraphQL Subscriptions, reduce frontend code and increase performance as well as flexibility when writing modern frontend applications.&lt;/p&gt;

&lt;h1&gt;
  
  
  Subscriptions
&lt;/h1&gt;

&lt;p&gt;GraphQL subscriptions are a mechanism to stream updates from the GraphQL server to the GraphQL client. You can use subscriptions e.g to automatically update the UI once there's a new message available in a chat room. Here's an example Operation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;subscription&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Chat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;room&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  WebSocket transport
&lt;/h1&gt;

&lt;p&gt;In order to stream messages from the server to the client, there needs to be a persisted connection between the two. The most used transport for GraphQL Subscriptions is using WebSockets based on the &lt;a href="https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md"&gt;GraphQL over WebSockets Protocol&lt;/a&gt; by Apollo.&lt;/p&gt;

&lt;p&gt;I'd like to discuss various aspects of this approach and why I think we should deprecate it in favour of technology that is a much better fit, Server-Sent Events (SSE).&lt;/p&gt;

&lt;h1&gt;
  
  
  The GraphQL over WebSockets Protocol
&lt;/h1&gt;

&lt;p&gt;To initiate the protocol it's not enough to just open a WebSocket connection. The client needs to send a "connection init" message to which the server needs to respond with "ack". WebSockets operate over TCP so it's not obvious to me why you would need to ack receiving a message.&lt;/p&gt;

&lt;p&gt;Once the initial message is acknowledged the client needs to send a GraphQL operation alongside the ID of the operation. Both the client and the server have to remember the ID of each individual Operation because all Operations/Subscriptions get multiplexed over a single WebSocket connection. This makes the implementation of both server and client overly complex. We will see how SSE will improve this because it takes care of the multiplexing for us.&lt;/p&gt;

&lt;p&gt;If the client decides to unsubscribe a subscription it needs to send a "stop" message over the WebSocket connection. This gets once again acknowledged by the server sending a "done" message. Again, as this is still using TCP I'm not sure why this is required.&lt;/p&gt;

&lt;h1&gt;
  
  
  Multiplexing over WebSockets
&lt;/h1&gt;

&lt;p&gt;Multiplexing in terms of GraphQL means that multiple Subscriptions use the same connection. In case of the implementation over WebSockets multiplexing is implemented using Javascript. This code needs to be written and maintained. It needs to be transpiled and then must run in each and every browser. Ideally, we can remove as much code as possible to simplify development and keep the Javascript running in the browser as little as possible.&lt;/p&gt;

&lt;h1&gt;
  
  
  Multiplexing over EventSource/SSE
&lt;/h1&gt;

&lt;p&gt;The Browser API to use Server-Sent Events is named EventSource. While WebSockets allow bidirectional communication, Server-Sent Events, as the name indicates only allow the server to stream events to the client and not in both directions. Luckily, we don't have to stream messages from the client to the server to implement GraphQL subscriptions. We simply open one EventSource for each Subscription. This simplifies both the client as well as the server implementation. There's no more custom code required to remember operations and their ID's to associate a message with the right Subscription. Multiplexing is done by the client &amp;amp; server automatically.&lt;/p&gt;

&lt;h1&gt;
  
  
  HTTP/2
&lt;/h1&gt;

&lt;p&gt;The ietf decided to not add WebSocket support over HTTP/2. This means that for each WebSocket connection, the browser has to open a new TCP connection to the server because the initial Handshake is based on HTTP/1.1. Depending on the browser your users are using the number of allowed TCP connections per host varies between 2 and 8 averaging at around 5. Keep in mind that a user might open multiple tabs with the same website so you should try keeping open connections at a minimum.&lt;/p&gt;

&lt;p&gt;With Server-Sent Events this is a different story. Server-Sent Events, when used with HTTP/2 multiplex automatically over a single TCP Connection. This means you can open more than 100 EventSources to the same host across multiple tabs and would still only use one single TCP connection.&lt;/p&gt;

&lt;h1&gt;
  
  
  Component-Based User Interfaces
&lt;/h1&gt;

&lt;p&gt;These days, component-based single page applications get more and more popular. Frameworks like React, Vue, Flutter etc. all have some concept of Components that take data and render it.&lt;/p&gt;

&lt;p&gt;Using a persisted WebSocket connection means we have to use dependency injection to make this connection available to all components.&lt;/p&gt;

&lt;p&gt;With the EventSource API on the other hand we can simplify the code required to implement Subscriptions. Here's an example using React's Hooks API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SubscriptionComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// get's called only once when SubscriptionComponent is mounted&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;EventSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://example.com/persisted/FooSubscription&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// get's called when SubscriptionComponent is unmounted&lt;/span&gt;
            &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},[])&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="c1"&gt;// re-renders once data updates&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The implementation is rather simple. Once the component get's mounted we start the EventSource. New messages get pushed into setState() to trigger re-rendering the component. The &lt;code&gt;return () =&amp;gt;&lt;/code&gt; function at the end of useEffect can be used to clean up the EventSource to avoid memory leaks. No additional library is required to implement multiplexing.&lt;/p&gt;

&lt;h1&gt;
  
  
  Server-Side Implementation
&lt;/h1&gt;

&lt;p&gt;What looks simple on the client is even simpler on the server. In comparison to WebSockets, Server-Sent Events can be implemented without any additional dependencies. Have a look at this &lt;a href="https://gist.github.com/ismasan/3fb75381cd2deb6bfa9c"&gt;Example&lt;/a&gt; written in Golang to see how simple the implementation is.&lt;/p&gt;

&lt;h1&gt;
  
  
  Caveats
&lt;/h1&gt;

&lt;p&gt;With all the positive aspects what might hold you back from adopting this?&lt;/p&gt;

&lt;h2&gt;
  
  
  Internet Explorer
&lt;/h2&gt;

&lt;p&gt;First of all, the EventSource API is supported by 93.8% by all Browsers. The WebSocket API, on the other hand, is supported by 97.41%. That is because IE does not support the SSE API. Is this a blocker? No, but it contradicts with the idea of reducing complexity. If you want to support IE you have to add a polyfill for the missing API.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTTP/2
&lt;/h2&gt;

&lt;p&gt;The EventSource API makes a lot of sense when used together with HTTP/2 because only then you can multiplex all Subscriptions over a single TCP connection. So, if your server or clients don't support HTTP/2 you're in trouble. If you're not sure if your clients can use HTTP/2 you might want to stick with WebSockets.&lt;/p&gt;

&lt;h2&gt;
  
  
  URL length limitations
&lt;/h2&gt;

&lt;p&gt;The non-polyfill Browser API to initiate an EventSource doesn't allow you to send a payload. That is, you have to send the Operation as well as the variables in the query string of the URL. Due to URL length limitations, you might not be able to send the Operation in its original format. This problem is best addressed using persisted queries. I've written another article on persisted queries if you want to get more details about the concept.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Server-Sent Events and the EventSource Browser API simplify both the client as well as the server implementation a lot. We can reduce the amount of code and are able to improve performance with HTTP/2 while reducing resource consumption because we're running less Javascript code.&lt;/p&gt;

&lt;p&gt;At the same time, we're forced to use Persisted Queries and need to migrate from our WebSocket implementation to SSE, don't we? Not really, this is where WunderGraph comes into the picture.&lt;/p&gt;

&lt;h1&gt;
  
  
  WunderGraph
&lt;/h1&gt;

&lt;p&gt;We have done the migration already. WunderGraph takes your GraphQL Server with the WebSocket implementation and turns all Subscriptions into Server-Sent Events on the fly.&lt;/p&gt;

&lt;p&gt;We also take care of persisting all GraphQL Operations for you and generate a client for any frontend framework you'd like to use.&lt;/p&gt;

&lt;p&gt;This means you get all the benefits of SSE without any additional work to be done.&lt;/p&gt;

&lt;p&gt;As a side effect your GraphQL server becomes a lot more secure because it's not directly exposed anymore to the public. It's hidden behind WunderGraph which only allows the persisted you previously registered.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://wundergraph.com"&gt;Try us out!&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  There's one more thing..
&lt;/h2&gt;

&lt;p&gt;Did you know that the Query Planner of WunderGraph allows you to join Subscriptions with other data sources?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;subscription&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RocketStatus&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="c"&gt;# rocketStatus is a subscription based on polling a REST API&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;rocketStatus&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;speed&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;altitude&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;aboveCountryCode&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="c"&gt;# country gets joined to the Response by querying a GraphQL API using the value aboveCountryCode&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;capital&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;currency&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This feature allows you to join any type of different data sources together with very little configuration.&lt;/p&gt;

&lt;p&gt;For the future we're planning to add support for GraphQL Federation. This means you will be able to have Subscriptions and many more features for your federated GraphQL implementations thanks to the WunderGraph Query Planner and Execution Engine.&lt;/p&gt;

&lt;p&gt;If you're interested in this functionality, leave your email in the chat so we can inform you once we have implemented it.&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>websockets</category>
      <category>sse</category>
      <category>subscriptions</category>
    </item>
    <item>
      <title>The case against normalized caching in GraphQL</title>
      <dc:creator>Jens Neuse</dc:creator>
      <pubDate>Fri, 11 Sep 2020 12:59:17 +0000</pubDate>
      <link>https://dev.to/jensneuse/the-case-against-normalized-caching-in-graphql-2jep</link>
      <guid>https://dev.to/jensneuse/the-case-against-normalized-caching-in-graphql-2jep</guid>
      <description>&lt;p&gt;In this post we'll compare rich GraphQL clients that come with a normalized cache implementation and the generated WunderGraph clients that rely on HTTP caching.&lt;/p&gt;

&lt;p&gt;As you might have already found out, WunderGraph uses persisted queries by default.&lt;br&gt;
With the WunderGraph code generator WunderGen(&lt;a href="https://github.com/wundergraph/wundergen"&gt;https://github.com/wundergraph/wundergen&lt;/a&gt;) you can generate a client that knows exactly how to invoke your previously registered operations.&lt;br&gt;
With the &lt;code&gt;@cache&lt;/code&gt; directive you're able to configure that the response of an operation should be cached by the server &amp;amp; client.&lt;br&gt;
Cache Control headers will be set accordingly, including etags. This mechanism is fully compatible with all major browsers and CDN's who implement caching according to the &lt;a href="https://tools.ietf.org/html/rfc7234"&gt;HTTP Caching RFC&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To illustrate this a bit better I'd like to introduce two example queries.&lt;br&gt;
The first one fetches a list of friends.&lt;br&gt;
The second one fetches some details about those friends.&lt;/p&gt;

&lt;p&gt;Let's consider we want to show a list of friends:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Friends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;friends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;avatarURL&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;For each friend we'd like to be able to click on the friend in the list and open up a detail page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FriendByID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;friend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;avatarURL&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You will recognize that we already have all the data for the detail page.&lt;br&gt;
So in an ideal scenario the client won't have to make another request.&lt;br&gt;
This is possible thanks to cache normalization.&lt;/p&gt;

&lt;p&gt;A smart normalized cache will identify the Friend entity and will recognize that the "FriendByID" query can be fulfilled using the data from the "Friends" query which we already ran.&lt;/p&gt;

&lt;p&gt;What are the pros of this concept?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Navigating to a friend detail page will be instant because there is no network request required&lt;/li&gt;
&lt;li&gt;The client will save bandwidth, and the user experience will be more fluent&lt;/li&gt;
&lt;li&gt;If we navigate back we can also immediately pull out the list of friends from the normalized cache&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How can this situation become hairy?&lt;br&gt;
Let's add a third operation. While on a user detail page we'd like to unfriend one of our peers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;mutation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Unfriend&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;unfriend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;How does the &lt;code&gt;unfriend&lt;/code&gt; mutation make the situation complex?&lt;br&gt;
In your normalized cache you have to invalidate or update the "Friends" and "Friend" entities.&lt;br&gt;
In your friends list you have to remove the user with id 123.&lt;br&gt;
For the &lt;code&gt;Friends&lt;/code&gt; you have to make sure no friend is returned for id 123 anymore.&lt;/p&gt;

&lt;p&gt;How does your normalized cache draw the lines between the &lt;code&gt;unfriend&lt;/code&gt; mutation and the &lt;code&gt;friend&lt;/code&gt; and &lt;code&gt;friends&lt;/code&gt; query?&lt;br&gt;
You as the frontend developer have to program the cache to do so.&lt;br&gt;
After the mutation you must inform the cache about these changes.&lt;/p&gt;

&lt;p&gt;With that let's talk about the cons of a normalized cache:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a rich GraphQL client with a normalized cache is complex to build and maintain&lt;/li&gt;
&lt;li&gt;the cache is running in the javascript vm of your browser and therefore a lot less efficient than the browser cache&lt;/li&gt;
&lt;li&gt;the logic to keep the cache state correct can become quite hairy&lt;/li&gt;
&lt;li&gt;the frontend developer must understand the domain and program the cache correctly to avoid unwanted behaviour&lt;/li&gt;
&lt;li&gt;the frontend developer must implement custom rules for cache eviction&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One thing that I want to explicitly mention outside of the list:&lt;/p&gt;
&lt;h2&gt;
  
  
  There's no single source of truth for the business object in this scenario.
&lt;/h2&gt;

&lt;p&gt;The frontend developer might accidentally allow the UI to show a friend in the friends list even if you have previously unfriended said person.&lt;br&gt;
Errors like these are very hard to spot. I think we're giving the frontend developer a lot of responsibility in this case to get caching right.&lt;/p&gt;

&lt;p&gt;Should it really be a concern of a frontend developer if data is stale? Shouldn't a frontend developer focus on the UI and trust the data layer? Does it really have to be that complicated to build rich apps with good performance?&lt;/p&gt;

&lt;p&gt;I believe there are applications where it's definitely worth having such a complexity.&lt;br&gt;
On the other hand I see many use cases, eg a news website, where relying on the HTTP Caching RFC is a lot simpler and more efficient.&lt;/p&gt;
&lt;h2&gt;
  
  
  Enter WunderGraph caching:
&lt;/h2&gt;

&lt;p&gt;With WunderGraph every registered Query becomes an endpoint to which you can apply caching rules individually.&lt;/p&gt;

&lt;p&gt;Let's revisit the example from above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FriendByID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;@cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;maxAge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;friend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;avatarURL&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This Query becomes the following endpoint on your WunderGraph Node:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;FriendByID&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="nx"&gt;variables&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In this scenario we decided to cache a friend object for 5 seconds using the &lt;code&gt;@cache&lt;/code&gt; directive.&lt;br&gt;
After 5 seconds the client will re-request the user and send an &lt;code&gt;If-None-Match&lt;/code&gt; header with the request.&lt;br&gt;
If the previous response is still valid the server will respond with a &lt;code&gt;304 (Not Modified)&lt;/code&gt; http status code.&lt;br&gt;
The same logic can be applied to the &lt;code&gt;Friends&lt;/code&gt; Query.&lt;br&gt;
All you have to do is define the desired behaviour using directives on the Operations.&lt;/p&gt;

&lt;p&gt;What are the pros of this approach?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;there's a single source of truth - the Operation Definition&lt;/li&gt;
&lt;li&gt;caching is handled automatically by the browser which is easier to use and understand&lt;/li&gt;
&lt;li&gt;no complex tooling is required to understand why a request is cached, browsers have excellent debuggers for this&lt;/li&gt;
&lt;li&gt;no javascript code has to be written to keep the cache state in sync&lt;/li&gt;
&lt;li&gt;with a service worker you can easily build offline apps using standard caching techniques&lt;/li&gt;
&lt;li&gt;less javascript code to be run by the browser&lt;/li&gt;
&lt;li&gt;the frontend developer gets to focus on the UI and has to worry less about data fetching and caching logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What are the cons of HTTP caching for GraphQL queries?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the client has to make more requests than with a normalized cache&lt;/li&gt;
&lt;li&gt;more requests lead to more bandwidth usage&lt;/li&gt;
&lt;li&gt;there's no easy way to invalidate the cache immediately&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Does a normalized cache prevent us from doing more requests?
&lt;/h2&gt;

&lt;p&gt;Let's make this scenario a bit more realistic. On the friends detail page we'd like to see the bio of the friend too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FriendByID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;friend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;avatarURL&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;bio&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;With this one field added even with normalized caching we have to refetch each individual friend even though we already have most of the data.&lt;br&gt;
At the end of the day a normalized cache might introduce a lot of complexity to your app while the benefits are not as huge as you expect.&lt;br&gt;
In this last example you could have saved to transfer less fields for each user detail page at the expense of a complex GraphQL client that understands which fields are missing for an entity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cache invalidation
&lt;/h2&gt;

&lt;p&gt;As mentioned previously, a normalized cache can easily be invalidated.&lt;br&gt;
This comes at the cost of implementing and maintaining the cache code plus defining the logic when to invalidate which objects.&lt;/p&gt;

&lt;p&gt;With HTTP caching it's not that easy.&lt;br&gt;
You could add a dynamic parameter, e.g. a timestamp, to the Query.&lt;br&gt;
This would allow for easy cache invalidation but also reduces possible cache hits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Users can have multiple clients
&lt;/h2&gt;

&lt;p&gt;Is it possible for your users to open your application in multiple tabs, native applications, etc.?&lt;br&gt;
If that's the case what happens if you unfriend a user in one tab while you have another tab open?&lt;br&gt;
At that point your normalized cache has no way of figuring out if data is stale, it needs to make a network call if you switch tabs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Should you cache at all?
&lt;/h2&gt;

&lt;p&gt;Are we actually solving the problem at the right layer or creating a new, even more complex, problem?&lt;/p&gt;

&lt;p&gt;If data like in this example could change any time at any click (add friend/unfriend) should we really cache this at the client or transport level at all?&lt;/p&gt;

&lt;p&gt;Why not use an application cache, e.g. Redis or Memcached, in the Backend if hitting the database directly is a performance bottleneck?&lt;br&gt;
In this scenario, neither transport level caching, nor a normalized client cache is the proper solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to cache
&lt;/h2&gt;

&lt;p&gt;Caching makes sense for publicly available data that doesn't change frequently.&lt;/p&gt;

&lt;p&gt;E.g. on a news website it's totally fine to cache the content for each article for a few seconds (e.g. 5).&lt;br&gt;
This would reduce the amount of requests from thousands to one per resource per 5 seconds.&lt;/p&gt;

&lt;p&gt;In case data can change at high frequencies, especially after user interactions with the application, caching should happen at the application layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;When you think you have to use a normalized cache in the client you should consider an application level cache first.&lt;/p&gt;

&lt;p&gt;A normalized cache introduces a second source of truth in the frontend which needs to be maintained.&lt;br&gt;
Optimistic can get that last bit of performance out of an app to get the user experience from 95% to 98% at the cost of extra complexity.&lt;/p&gt;

&lt;p&gt;Most of the time you don't need this complexity and should avoid it.&lt;br&gt;
Keep it simple, solve a business problem, don't introduce technical debt.&lt;/p&gt;

&lt;p&gt;WunderGraph gives you a simple and powerful way to use transport based caching.&lt;br&gt;
For 99% of the other use cases you should consider adding an application level cache if performance is an issue.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Scaffolding a complete React app with GraphQL</title>
      <dc:creator>Jens Neuse</dc:creator>
      <pubDate>Mon, 07 Sep 2020 07:37:46 +0000</pubDate>
      <link>https://dev.to/jensneuse/scaffolding-a-complete-react-app-with-graphql-mag</link>
      <guid>https://dev.to/jensneuse/scaffolding-a-complete-react-app-with-graphql-mag</guid>
      <description>&lt;p&gt;Let us imagine that by writing a handful of GraphQL queries, it would be possible to build a fully functional React application. For a TODO app, we could allow users to login, post, retrieve, update and complete tasks. In this post, I'll explain how WunderGraph could be leveraged to achieve such a thing, and what the implications are.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is WunderGraph?
&lt;/h2&gt;

&lt;p&gt;Before we dig in, let's have a quick overview of what WunderGraph is in order to fully understand the scenario.&lt;/p&gt;

&lt;p&gt;WunderGraph is a GraphQL Engine that compiles Queries on the server, exposes them as persisted operations and then generates smart clients to help you to consume them.&lt;/p&gt;

&lt;p&gt;While GraphQL doesn't lose its dynamic character, this approach makes using GraphQL a lot safer and more performant.&lt;br&gt;
Additionally, because Queries &amp;amp; Mutations are persisted on the server by default, you are now uniquely able to use directives in Operations.&lt;/p&gt;

&lt;p&gt;A few example queries to illustrate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AllTasks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;fromClaim&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;queryTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;}}){&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;completed&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;mutation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AddTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;fromClaim&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;addTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;completed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]){&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;completed&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;mutation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SetTaskCompleted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$completed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;fromClaim&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;updateTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;completed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$completed&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}){&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;completed&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Above, we have 3 operations:&lt;/p&gt;

&lt;p&gt;AllTasks - Lists all tasks for a user by their email address&lt;br&gt;
AddTask - To add a task for a user&lt;br&gt;
SetTaskCompleted - To mark a task as complete&lt;/p&gt;

&lt;p&gt;The above three Operations are quite standard with one slight exception, They contain a custom directive: &lt;br&gt;
 &lt;code&gt;@fromClaim(name: "email")&lt;/code&gt;. This directive ensures that the variable &lt;code&gt;$email&lt;/code&gt;, is always obtained from a JWT claim&lt;br&gt;
 &lt;code&gt;email&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you're not yet familiar with the concepts of WunderGraph, this is absolutely safe because only the server (WunderGraph console) can define and persist an Operation.&lt;/p&gt;

&lt;p&gt;This means that a user needs to be logged-in to interact with these queries &amp;amp; only the logged-in user can create, list or update their own tasks.&lt;/p&gt;

&lt;p&gt;Next, we use wundergen (our code generator) to generate an OpenID client that is preconfigured with our backend, all&lt;br&gt;
type definitions for the GraphQL schema as well as React hooks for each Operation.&lt;/p&gt;

&lt;p&gt;This process is pretty much straight forward as we just take all the configuration and run it through a template. We can now just consume the TODO API as if it were just another function call.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why can't we scaffold the entire site?
&lt;/h2&gt;

&lt;p&gt;Well, at the moment this is just a concept, so I want to hear your feedback first before implementing it. Let's have a&lt;br&gt;
 look at some ideas on how we might implement this. As I said before we will only be writing GraphQL Operations so&lt;br&gt;
 let's revisit them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AllTasks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;fromClaim&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;@ui&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/tasks"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TABLE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;queryTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;}}){&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;@ui&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;linkTo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/tasks/:id"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;@ui&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columnName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ID"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;@ui&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columnName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Title"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;completed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;@ui&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columnName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Completed"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;AllTasks&lt;/code&gt; Query will generate a table component on the &lt;code&gt;/tasks&lt;/code&gt; route.&lt;br&gt;
Columns will have headers and each column will link to a task detail &lt;code&gt;/tasks/:id&lt;/code&gt; page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;mutation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AddTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="n"&gt;formType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;formName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;@fromClaim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;@ui&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/tasks/new"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FORM&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;addTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;completed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]){&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;completed&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;AddTask&lt;/code&gt; Mutation will generate a form on the &lt;code&gt;/tasks/new&lt;/code&gt; route.&lt;br&gt;
The variable &lt;code&gt;$title&lt;/code&gt; will be filled from the form.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;mutation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SetTaskCompleted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$completed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="n"&gt;formType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SWITCH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;formName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;Completed&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;@fromClaim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;@ui&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/tasks/:id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FLAT_VIEW&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;updateTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;completed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$completed&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}){&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;completed&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;SetTaskCompleted&lt;/code&gt; Mutation will render a switch on the &lt;code&gt;/tasks/:id&lt;/code&gt; page to update the task state.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let's discuss the pros and cons
&lt;/h3&gt;

&lt;p&gt;First, it's valid GraphQL syntax. Operations might get complex quite fast though.&lt;/p&gt;

&lt;p&gt;We're definitely limited in what we could achieve with this. However, there will be use cases where customization is not as important as the cost of development.&lt;/p&gt;

&lt;p&gt;WunderGraph allows you to connect any possible datasource (at least that's the vision) so if you wanted to build Dashboards or Admin panels, or even a simple contact page on top of your existing APIs this could be a very convenient way to achieve it without code.&lt;/p&gt;

&lt;p&gt;There might be quite a compelling use-case for rapid prototyping. Consider the flow: Deploy a CRUD GraphQL backend,  write a bunch of Operations and your app is ready to deploy. How much faster could it be? You could then customize the scaffolded views &amp;amp; forms as you saw fit.&lt;/p&gt;

&lt;p&gt;Maybe the idea to directly introduce navigation between pages is a step too far. Maybe we could just generate Components based on the Operations and let the user define Navigation manually?&lt;/p&gt;

&lt;p&gt;But then, how would you go about customizing the pre-generated components without breaking when re-generating the code after a change to the Operations?&lt;/p&gt;

&lt;p&gt;Would multiple templates be useful so that users could choose between Material-UI or Bootstrap etc... Would that make sense?&lt;/p&gt;

&lt;h3&gt;
  
  
  What do you think?
&lt;/h3&gt;

&lt;p&gt;Finally, I think this is not yet ready for implementation. I need your feedback to see if this approach is useful for you. Do you have ideas on how you could use this? Is there anything missing to make it work for you?&lt;/p&gt;

&lt;p&gt;Please meet us on &lt;a href="https://discord.gg/Jjmc8TC"&gt;Discord&lt;/a&gt;, &lt;a href="https://github.com/wundergraph/community/issues/1"&gt;GitHub&lt;/a&gt; &lt;br&gt;
 or in the comments and share your opinion so we can make the right decisions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Want to checkout WunderGraph?
&lt;/h3&gt;

&lt;p&gt;We're in public beta currently. Go to &lt;a href="https://wundergraph.com"&gt;https://wundergraph.com&lt;/a&gt; and give it a try.&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>react</category>
      <category>nocode</category>
      <category>lowcode</category>
    </item>
  </channel>
</rss>
