<?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>Forem: Marcell Toth</title>
    <description>The latest articles on Forem by Marcell Toth (@marcelltoth).</description>
    <link>https://forem.com/marcelltoth</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%2F447223%2F765dcab3-2f23-4586-9a0f-846bf290d15f.jpeg</url>
      <title>Forem: Marcell Toth</title>
      <link>https://forem.com/marcelltoth</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/marcelltoth"/>
    <language>en</language>
    <item>
      <title>Mapping between identical TypeScript enums without the boilerplate</title>
      <dc:creator>Marcell Toth</dc:creator>
      <pubDate>Thu, 14 Apr 2022 13:18:43 +0000</pubDate>
      <link>https://forem.com/marcelltoth/mapping-between-identical-typescript-enums-without-the-boilerplate-5b28</link>
      <guid>https://forem.com/marcelltoth/mapping-between-identical-typescript-enums-without-the-boilerplate-5b28</guid>
      <description>&lt;p&gt;TypeScript's enums are weird. They are the only piece in TS (as far as I'm aware) that doesn't follow the structural / duck typing pattern. If you prefer enums in place of string unions, I'm sure you've encountered the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu1l5i0gdmqwco9awuy9c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu1l5i0gdmqwco9awuy9c.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You have two enums which contain the same members, yet TS doesn't realize they are compatible. Now I see three main patterns people utilize to resolve the problem:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The IDC approcach&lt;/strong&gt;: You cast everywhere. This is the easiest, but it is dangerous, as enum casts let you do pretty much anything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The purist&lt;/strong&gt;: The types are different because "they represent different things", so you ignore the similarity and create a mapper function (or object) and use it where needed: &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbdyj1d1h3d2lak0e3sdq.png" alt="Image description"&gt;
This is very safe for sure, but adds a lot of maintenance overhead, and code (bundle) size for nooooot a lot of good reasons.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The middle ground&lt;/strong&gt;: You create a function but cast inside. &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvaiyixeqyumg5q4wzeva.png" alt="Image description"&gt;
This looks better than approach no 1 on paper, as you only cast once, but you really are still prone to the same danger: If the two enums diverge at any point, you won't get a single warning from TS.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Type safety without the boilerplate
&lt;/h2&gt;

&lt;p&gt;I was thinking, can we somehow get the type-safety of approach #2 while also retaining the purity and bundle size of #3? It turns out, we can.&lt;/p&gt;

&lt;p&gt;On the JS level this 3rd approach is trivial, your mapper functions are essentially &lt;code&gt;x =&amp;gt; x&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;So now it's "just" the matter of adding TS constraints to make sure we are only allowed to map between identical enums.&lt;/p&gt;

&lt;h3&gt;
  
  
  The implementation
&lt;/h3&gt;

&lt;h4&gt;
  
  
  The core
&lt;/h4&gt;

&lt;p&gt;My current solution only works with symmetrical (same key and values) enums for simplicity, although it could very well be extended. So let's define what is a symmetrical enum first:&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;type&lt;/span&gt; &lt;span class="nx"&gt;SymmetricalEnum&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TEnum&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&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="nx"&gt;key&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;TEnum&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;key&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;Then - here is the trick - we can define what the result value of a mapping will be, this is essentially doing the mapping on the TS metalanguage level, with all of its benefits:&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;type&lt;/span&gt; &lt;span class="nx"&gt;MapperResult&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;
  &lt;span class="nx"&gt;TSourceEnumObj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;TDestEnumObj&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;SymmetricalEnum&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TSourceEnumObj&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;TSourceValue&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;TSourceEnumObj&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TDestEnumObj&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;TSourceValue&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;TResult&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;TResult&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We have 3 generic args:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The source enum type, which can be anything (you could add a constraint here if you wanted to). &lt;/li&gt;
&lt;li&gt;The destination enum type, which &lt;strong&gt;needs to extend the source enum&lt;/strong&gt;. This is the core of the type-safety logic. If you try to map to an enum which is not a superset of the source this line will yell here. &lt;/li&gt;
&lt;li&gt;The type of actual input value given to the mapper function. This is then used in a conditional statement to essentially "pull" the corresponding value from the destination type. If you need to read up on Conditional Types I suggest starting with &lt;a href="https://www.typescriptlang.org/docs/handbook/2/conditional-types.html" rel="noopener noreferrer"&gt;the documentation&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can use the above on it's own if you need a mapping on the types-level, see this magic for example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frb0q5v8xwlutjinlt6rx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frb0q5v8xwlutjinlt6rx.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Incompatible enums are rejected - with a pretty clear error message even:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzt00ue8yyz1z2havlx1s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzt00ue8yyz1z2havlx1s.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Creating mapper functions
&lt;/h4&gt;

&lt;p&gt;Now we are ready to create a higher order function that auto-generates mapper functions, utilizing this magic.&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;createEnumMapperFunction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TSourceEnumObj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TDestEnumObj&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;SymmetricalEnum&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TSourceEnumObj&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TSourceEnumObj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TDestEnumObj&lt;/span&gt;&lt;span class="p"&gt;)&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;TInput&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;TSourceEnumObj&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;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TInput&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;value&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;MapperResult&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TSourceEnumObj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TDestEnumObj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TInput&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You can see it in action here, including one error it caught:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F153a0cy09wz3d6z4dld7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F153a0cy09wz3d6z4dld7.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And this is it!&lt;/strong&gt; Now you can replace all your switch statements with a call to &lt;code&gt;createEnumMapperFunction&lt;/code&gt;, or replace your casts with a type safe version like this above. Feel free to tweak it to your needs, like adding support for non-symmetrical enums or other cases. &lt;/p&gt;

&lt;h3&gt;
  
  
  Final code
&lt;/h3&gt;

&lt;p&gt;For those that have the StackOverflow keyboard &lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdvke5scasjdg9ywgf7oz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdvke5scasjdg9ywgf7oz.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;here is the full code, and a &lt;a href="https://tinyurl.com/yt69pk9h" rel="noopener noreferrer"&gt;TS Playground with examples&lt;/a&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;type&lt;/span&gt; &lt;span class="nx"&gt;SymmetricalEnum&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TEnum&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&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="nx"&gt;key&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;TEnum&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;MapperResult&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;
  &lt;span class="nx"&gt;TSourceEnumObj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;TDestEnumObj&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;SymmetricalEnum&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TSourceEnumObj&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;TSourceValue&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;TSourceEnumObj&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TDestEnumObj&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;TSourceValue&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;TResult&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;TResult&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createEnumMapperFunction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TSourceEnumObj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TDestEnumObj&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;SymmetricalEnum&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TSourceEnumObj&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TSourceEnumObj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TDestEnumObj&lt;/span&gt;&lt;span class="p"&gt;)&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;TInput&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;TSourceEnumObj&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;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TInput&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;value&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;MapperResult&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TSourceEnumObj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TDestEnumObj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TInput&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;I hope you find this useful some day. Let me know in the comments if you did, and if you have built an improved version I'd also love to know.&lt;/p&gt;

</description>
      <category>typescript</category>
    </item>
    <item>
      <title>Using accessibility guidelines to improve test quality and robustness</title>
      <dc:creator>Marcell Toth</dc:creator>
      <pubDate>Sat, 28 Nov 2020 15:15:13 +0000</pubDate>
      <link>https://forem.com/marcelltoth/using-accessibility-guidelines-to-improve-test-quality-and-robustness-408j</link>
      <guid>https://forem.com/marcelltoth/using-accessibility-guidelines-to-improve-test-quality-and-robustness-408j</guid>
      <description>&lt;p&gt;There's not a lot of talk about &lt;em&gt;Web Accessibility&lt;/em&gt; around here. It is viewed as an uninteresting topic, dealt with on an as-needed basis. But it shouldn't be this way!&lt;/p&gt;

&lt;p&gt;Let me give you an intro to &lt;em&gt;WAI-ARIA&lt;/em&gt; from an angle you probably have never seen before: &lt;strong&gt;How to utilize accessibility guidelines to drastically improve your unit tests?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What do Unit Testing and Accessibility have in common? Much more than you would think!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fda090x5lqs0qdu9ufy28.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fda090x5lqs0qdu9ufy28.png" alt="Unit testing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Unit Testing
&lt;/h2&gt;

&lt;p&gt;Let's begin with some (opinionated) &lt;strong&gt;Do-s &amp;amp; Don't-s on Unit Testing&lt;/strong&gt;. I am assuming you are already familiar, this list is not meant to be complete or perfect.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Test the behavior of components. Don't test implementation details&lt;/li&gt;
&lt;li&gt;Be resistant to non-user-visible changes in HTML / React structure. Assert from the users' perspective.&lt;/li&gt;
&lt;li&gt;Focus on the semantics, don't break if visuals or the copy changes.&lt;/li&gt;
&lt;li&gt;Use the app like a regular user would. Don't query artificial constructs (like CSS selectors or &lt;code&gt;id&lt;/code&gt;s). Trigger events a user would trigger themselves.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fi3xfiaxexismcf3e78u5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fi3xfiaxexismcf3e78u5.png" alt="Web accessibility"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessibility
&lt;/h2&gt;

&lt;p&gt;Now what about some &lt;strong&gt;guidelines for accessibility&lt;/strong&gt;? &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write semantic HTML that screen-readers can understand. Don't rely on CSS to convey structure.&lt;/li&gt;
&lt;li&gt;Non-user-visible changes in HTML structure should not change screen-reader output either.&lt;/li&gt;
&lt;li&gt;Focus first on the core value/functionality provided for your user, styling &amp;amp; fluff comes second.&lt;/li&gt;
&lt;li&gt;Your site by should be discoverable by screen-reader the same way it is for regular users. Don't treat headless user-agents as second-class citizens.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Unit Testing + Accessibility = 💞
&lt;/h2&gt;

&lt;p&gt;Now back to the question I asked before:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What do Unit Testing and Accessibility have in common?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's put the two lists &lt;strong&gt;side by side&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Testing&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Accessibility&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Test the behavior of components. Don't test implementation details&lt;/td&gt;
&lt;td&gt;Write semantic HTML that screen-readers can understand. Don't rely on CSS to convey structure.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Be resistant to non-user-visible changes in HTML / React structure.&lt;/td&gt;
&lt;td&gt;Assert from the users' perspective. Non-user-visible changes in HTML structure should not change screen-reader output either.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Focus on the semantics, don't break if visuals or the copy changes.&lt;/td&gt;
&lt;td&gt;Focus first on the core value/functionality provided for your user, styling &amp;amp; fluff comes second.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use the app like a regular user would. Don't query artificial constructs (like CSS selectors or &lt;code&gt;id&lt;/code&gt;s). Trigger events a user would trigger themselves.&lt;/td&gt;
&lt;td&gt;Your site by should be discoverable by screen-reader the same way it is for regular users. Don't treat headless user-agents as second-class citizens.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;At this point, looking back at both lists one can start to see the common pattern:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Describe your semantics well&lt;/strong&gt; in HTML.&lt;/li&gt;
&lt;li&gt;Treat &lt;strong&gt;headless agents as first-class citizens&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To put it in another way, &lt;strong&gt;screen-readers and test environments are very much alike&lt;/strong&gt;: They are headless user-agents interpreting your DOM structure without putting much if any effort into calculating layout, styling and visuals in general.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enough talk, let's see some &lt;strong&gt;code&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Let's start with a simple example, non-semantic vs semantic HTML. I'm sure most of you are familiar with this one already, but let's see the testing implications.&lt;/p&gt;

&lt;p&gt;Let's start with this HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"nav"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"nav-item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;strong&amp;gt;&lt;/span&gt;First Item&lt;span class="nt"&gt;&amp;lt;/strong&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"nav-item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;strong&amp;gt;&lt;/span&gt;Second Item&lt;span class="nt"&gt;&amp;lt;/strong&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's say in a test we would like to retrieve the first navigation element. We can do that with a simple CSS query like &lt;code&gt;querySelector('.nav &amp;gt; :first-child')&lt;/code&gt;, or a text-based selector (most testing frameworks should include one) &lt;code&gt;getByText('First Item')&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;While both of them work at the moment, both are fragile as they break the constraints we set out earlier:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The first one depends on exact CSS classes, if someone renames &lt;code&gt;.nav&lt;/code&gt; to &lt;code&gt;.navigation&lt;/code&gt;, the test breaks.&lt;/li&gt;
&lt;li&gt;It also depends on the exact CSS structure, adding another &lt;code&gt;div&lt;/code&gt; in-between - like grouping the items - or adding a logo before the items will break it as well.&lt;/li&gt;
&lt;li&gt;The second one is somewhat better, but depends on the exact words of the copy, which you don't usually want to test.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's rewrite this to be more textbook-like semantic HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;nav&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;First Item&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;Second Item&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point your CSS query becomes &lt;code&gt;querySelector('nav li:fist-of-type')&lt;/code&gt; which is much more robust, it is ignorant of grouping the items or adding a logo. (Unless of course you add the logo as another &lt;code&gt;li&lt;/code&gt; which is the &lt;em&gt;wrong thing to do&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;Even better would be to query by WAI-ARIA role, which we'll briefly discuss below: &lt;code&gt;getAllByRole(getByRole('navigation'), 'listitem')[0]&lt;/code&gt;. This does almost exactly what our plain English requirements said: Retrieves the navigation menu, and within that menu, the first list item.&lt;/p&gt;

&lt;h3&gt;
  
  
  Roles
&lt;/h3&gt;

&lt;p&gt;In WAI-ARIA, every HTML element can have something called a &lt;code&gt;role&lt;/code&gt;. This is either set implicitly, e.g. &lt;code&gt;nav&lt;/code&gt; tags have the &lt;code&gt;navigation&lt;/code&gt; role by default, or explicitly by applying the &lt;code&gt;role="some-role"&lt;/code&gt; attribute.&lt;/p&gt;

&lt;p&gt;Let's imagine you are testing some JavaScript that shows this tooltip on some condition. Your goal is to assert that the tooltip is visible.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-center w-96"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Tooltip content
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The simple solution would be to append a &lt;code&gt;data-testid="some-tooltip"&lt;/code&gt; and the &lt;em&gt;div&lt;/em&gt; then write the assertion as &lt;code&gt;expect(screen.getByTestId('some-tooltip')).toBeVisible()&lt;/code&gt;, or similar in another testing framework. But now you are querying an artificial construct - a test-id -, and you've given yourself another string to maintain, which doesn't even add any value from the perspective of someone writing (or reading) the HTML.&lt;/p&gt;

&lt;p&gt;What should we do instead? Think about what our goal is. This is a &lt;em&gt;tooltip&lt;/em&gt;. We need to assert &lt;em&gt;a tooltip is visible.&lt;/em&gt; Then let's do just that: WAI-ARIA has a role called &lt;em&gt;tooltip&lt;/em&gt; that is defined as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A contextual popup that displays a description for an element.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Instead of the test-id, you can add &lt;code&gt;role="tooltip"&lt;/code&gt; to the &lt;em&gt;div&lt;/em&gt;, then your assertion becomes &lt;code&gt;expect(screen.getByRole('tooltip')).toBeVisible()&lt;/code&gt;, which is &lt;strong&gt;almost exactly the business-requirement&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I bet if you show this to a business person who has zero JavaScript knowledge they would still understand it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And &lt;strong&gt;this is the power of using roles&lt;/strong&gt; and possibly other ARIA attributes &lt;strong&gt;for testing!&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Of course the full WAI-ARIA spec is much more in depth, with many other aspects (attributes, states, etc.) being just as useful for testing. Describing those is beyond the scope of this introductory article, but I encourage you to at least go through the MDN intro on WAI-ARIA to get an overview of the tools available to you.&lt;/p&gt;

&lt;p&gt;Thank you for reading, happy testing!&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Learn/Accessibility" rel="noopener noreferrer"&gt;MDN - Accessibility overview&lt;/a&gt;&lt;br&gt;
&lt;a href="https://developer.mozilla.org/en-US/docs/Learn/Accessibility/HTML" rel="noopener noreferrer"&gt;MDN - HTML accessibility&lt;/a&gt;&lt;br&gt;
&lt;a href="https://developer.mozilla.org/en-US/docs/Learn/Accessibility/WAI-ARIA_basics" rel="noopener noreferrer"&gt;MDN - Intro to WAI-ARIA&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.w3.org/TR/wai-aria-1.1/#role_definitions" rel="noopener noreferrer"&gt;W3 - Definition of Roles&lt;/a&gt;&lt;/p&gt;

</description>
      <category>testing</category>
      <category>webdev</category>
      <category>html</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to build GraphQL APIs with ASP.NET Core &amp; HotChocolate - Part 1 - Setup</title>
      <dc:creator>Marcell Toth</dc:creator>
      <pubDate>Sat, 08 Aug 2020 17:55:52 +0000</pubDate>
      <link>https://forem.com/marcelltoth/how-to-build-graphql-apis-with-asp-net-core-hotchocolate-part-1-setup-3jco</link>
      <guid>https://forem.com/marcelltoth/how-to-build-graphql-apis-with-asp-net-core-hotchocolate-part-1-setup-3jco</guid>
      <description>&lt;p&gt;Getting started with GraphQL as a .NET developer is &lt;strong&gt;much simpler than you would think&lt;/strong&gt;. Let me show you why.&lt;/p&gt;

&lt;p&gt;Coming from a REST or MVC background GraphQL looks scary. &lt;/p&gt;

&lt;p&gt;I know.&lt;/p&gt;

&lt;p&gt;It is very different from what we are used to, especially in the .NET (Core) ecosystem. &lt;/p&gt;

&lt;p&gt;There's no (built-in) template. With traditional RESTful APIs (or MVC apps for that matter) you select your template and you get a ready-to-run solution right away. Not with GraphQL. This makes it look like GraphQL on ASP.NET Core is complicated and a pain to get started with.&lt;/p&gt;

&lt;p&gt;I am here to show you, &lt;strong&gt;it is not&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In this article I will show you how to:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set up HotChocolate and GraphiQL for your ASP.NET Core application.&lt;/li&gt;
&lt;li&gt;Define types from your GraphQL schema using C#.&lt;/li&gt;
&lt;li&gt;Create your resolvers to wire up real data to those types. (Part 2)&lt;/li&gt;
&lt;li&gt;Implement pagination. (Part 2)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Who is this article for?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This series of articles focuses on &lt;strong&gt;authoring GraphQL APIs using the ASP.NET Core platform.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I am going to assume &lt;strong&gt;you have already acquired a solid understanding of the fundamental principles of GraphQL itself&lt;/strong&gt;. If not, I would suggest heading over to &lt;a href="https://graphql.org/"&gt;https://graphql.org/&lt;/a&gt; and going through the documentation first.&lt;/p&gt;

&lt;p&gt;We will not go into advanced topics - such as directives, fragments, custom scalars, etc, not even mutations - but &lt;strong&gt;you are expected to understand the basics of the type system, queries, lists, nullability, and enums&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The API you are going to build by the end of this article
&lt;/h2&gt;

&lt;p&gt;I don't like overly simplified tutorials, they usually miss out on the most important architectural problems.&lt;/p&gt;

&lt;p&gt;Instead, here we are going to pretend we're in charge of building a real-world API of an airline travel company: The dataset consists of airlines, airports, and routes between them. The dataset that we are going to use is a full-scale real-world snapshot of said data.&lt;/p&gt;

&lt;p&gt;I built a NuGet package that contains all our data, so we don't have to worry about a database now, &lt;strong&gt;we can instead focus on the important part: GraphQL&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I strongly believe APIs should be built &lt;a href="https://apisyouwonthate.com/blog/api-design-first-vs-code-first"&gt;design-first&lt;/a&gt; so that's what we are going to do. &lt;/p&gt;

&lt;p&gt;Let's pretend this is the schema our architects built and was given to us for implementation:&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;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&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;Route&lt;/span&gt;&lt;span class="p"&gt;!]!&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;airlines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&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;Airline&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;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;airline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Airline&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Airport&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Airport&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;codeshare&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="n"&gt;stops&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&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;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Airline&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="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="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="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="n"&gt;iata&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="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Country&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;destinations&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;Airport&lt;/span&gt;&lt;span class="p"&gt;!]!&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;routes&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;Route&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;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Airport&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="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="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="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="n"&gt;city&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="n"&gt;country&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Country&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;iata&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="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Float&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;airlines&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;Airline&lt;/span&gt;&lt;span class="p"&gt;!]!&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;routes&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;Route&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;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Country&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="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="n"&gt;isoCode&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="n"&gt;dafifCode&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="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Query&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;We have the schema, we have the "database", so let's get started. &lt;/p&gt;

&lt;h2&gt;
  
  
  Project setup (very simple)
&lt;/h2&gt;

&lt;p&gt;I promised you this is going to be very simple. So here it is:&lt;/p&gt;

&lt;p&gt;We are going to start with an &lt;strong&gt;Empty&lt;/strong&gt; &lt;em&gt;ASP.NET Core 3.1&lt;/em&gt; project template. We can even get rid of the &lt;code&gt;UseEndpoints&lt;/code&gt; call, so our method looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Startup&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IServiceCollection&lt;/span&gt; &lt;span class="n"&gt;services&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;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IApplicationBuilder&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IWebHostEnvironment&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsDevelopment&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseDeveloperExceptionPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseRouting&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;Can't get much simpler, right?&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding HotChocolate from NuGet
&lt;/h2&gt;

&lt;p&gt;As you might have noticed, ASP.NET Core does not include GraphQL support by default. This isn't going to stop us though, there are &lt;strong&gt;two great GraphQL server implementations&lt;/strong&gt; for ASP.NET Core: &lt;em&gt;graphql-dotnet&lt;/em&gt; and &lt;em&gt;HotChocolate&lt;/em&gt;. I am not going to talk about the differences here, but my choice is &lt;em&gt;HotChocolate&lt;/em&gt; which we're going to use for this tutorial. Having said that, all I am going to show you here could just as well be achieved with &lt;em&gt;graphql-dotnet&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Let's install 3 HotChocolate NuGet packages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;HotChocolate&lt;/em&gt; - contains the core GraphQL server logic&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;HotChocolate.AspNetCore&lt;/em&gt; - contains the AspNetCore bindings&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;HotChocolate.AspNetCore.GraphiQL&lt;/em&gt; - this one is optional, contains &lt;a href="https://github.com/graphql/graphiql"&gt;graphiql&lt;/a&gt; an amazing GraphQL playground we can use to test-drive our server.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Configuring the GraphQL middleware
&lt;/h3&gt;

&lt;p&gt;Let's add these two lines at the end of &lt;code&gt;Configure&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;      &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseGraphiQL&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseGraphQL&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This wires up both HotChocolate and GraphiQL to our application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring your GraphQL schema
&lt;/h2&gt;

&lt;p&gt;If you tried to run the above, it crashes on startup with some cryptic error message. That is because we haven't told HotChocolate what schema we'd like to use for our endpoint. So let's do that.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: the schema file
&lt;/h3&gt;

&lt;p&gt;A wonderful feature of HotChocolate is that it can read our schema from the &lt;code&gt;.graphql&lt;/code&gt; file directly, without us having to transform it into C# syntax.&lt;/p&gt;

&lt;p&gt;To do that, we have to add the schema file to our project (I am going to call it "schema.graphql") and make sure to set the &lt;code&gt;CopyToOutputDirectory&lt;/code&gt; property on it, so it gets placed right next to our output dll.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: &lt;code&gt;ConfigureServices&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Now that we have our file in the project let's tell HotChocolate to load it. This is done inside &lt;code&gt;ConfigureServices&lt;/code&gt;, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Startup&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IServiceCollection&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddGraphQL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;SchemaBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;New&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDocumentFromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"./schema.graphql"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&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;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IApplicationBuilder&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IWebHostEnvironment&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsDevelopment&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseDeveloperExceptionPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseRouting&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseGraphiQL&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseGraphQL&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;If you run your app again, you should now be facing a new error message:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1hFi4d8o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/s2i3n486zarydyeuzdsv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1hFi4d8o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/s2i3n486zarydyeuzdsv.png" alt="Schema errors"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is great.&lt;/strong&gt; It means the schema is recognized and HotChocolate doesn't know how to serve it yet - because we haven't written the logic for it yet. It is like a series of &lt;code&gt;NotImplementedException&lt;/code&gt;s.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The setup part is complete here.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Our goal for the next part of this article will be to add all backing &lt;em&gt;Type&lt;/em&gt; and &lt;em&gt;Resolver&lt;/em&gt; classes for all these GraphQL types until all the error messages are gone.&lt;/p&gt;

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

&lt;p&gt;In just a few minutes, &lt;strong&gt;you set up a GraphQL server&lt;/strong&gt; configuration in your ASP.NET Core application.&lt;/p&gt;

&lt;p&gt;Thank you for reading this first part of the article, feel free to share if you liked it.&lt;/p&gt;

&lt;p&gt;In the second part of the series, &lt;strong&gt;we will cover implementing the actual GraphQL types&lt;/strong&gt; for this airline example: Query, Route, Airport, et cetera.&lt;/p&gt;

&lt;p&gt;In the meanwhile, you can check out &lt;a href="https://github.com/marcelltoth/graphqlairlines-demo/tree/master/GraphQlAirlines.Api"&gt;the Github Repository&lt;/a&gt; containing the final source code of our sample API. &lt;/p&gt;

</description>
      <category>aspnetcore</category>
    </item>
  </channel>
</rss>
