<?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: Rémi de Juvigny</title>
    <description>The latest articles on DEV Community by Rémi de Juvigny (@remidej).</description>
    <link>https://dev.to/remidej</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%2F96154%2F6e48aa54-1786-473b-bc2e-71736f91df93.jpg</url>
      <title>DEV Community: Rémi de Juvigny</title>
      <link>https://dev.to/remidej</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/remidej"/>
    <language>en</language>
    <item>
      <title>End-to-end Type Safety with TypeScript and GraphQL</title>
      <dc:creator>Rémi de Juvigny</dc:creator>
      <pubDate>Wed, 22 Apr 2020 22:01:48 +0000</pubDate>
      <link>https://dev.to/remidej/end-to-end-type-safety-with-typescript-and-graphql-35ab</link>
      <guid>https://dev.to/remidej/end-to-end-type-safety-with-typescript-and-graphql-35ab</guid>
      <description>&lt;p&gt;In the JavaScript community, static types often get bad press. Configuring the right tools is hard, and a bad architecture can lead to a false sense of confidence. Let's try to fix this.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Consider this codebase written in TypeScript:&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%2Fremi.space%2Fstatic%2F77559f606cb625ada05501f4994c4d28%2F6295b%2Frest-architecture.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%2Fremi.space%2Fstatic%2F77559f606cb625ada05501f4994c4d28%2F6295b%2Frest-architecture.png" alt="Rest architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We never even use the &lt;code&gt;any&lt;/code&gt; type. It should be type-safe, right? Well, no, at least not yet.&lt;/p&gt;

&lt;p&gt;The problem is that our type safety is only as strong as its weakest point. In our case, the loose end is our API. The types from both ends live on their own separate world. So we write types on the frontend based on what we &lt;em&gt;assume&lt;/em&gt; the backend will send, and vice versa.&lt;/p&gt;

&lt;p&gt;We have multiple sources of truth, and no guarantee that they will keep in sync. It's already annoying for our small project,  and can become a huge issue if we have an architecture based on many microservices.&lt;/p&gt;

&lt;h2&gt;
  
  
  GraphQL to the rescue
&lt;/h2&gt;

&lt;p&gt;Unlike Rest, GraphQL has a type system in its &lt;a href="https://graphql.org/learn/schema/" rel="noopener noreferrer"&gt;core principles&lt;/a&gt;. We can use it to make type-safe API calls. The goal is to use it to propagate our backend types to the frontend.&lt;/p&gt;

&lt;p&gt;But we have to make sure there are no holes in our type safety. On both the frontend and the backend, we need a way to convert port the GraphQL schema to TypeScript types. Here's the outline of the architecture:&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%2Fremi.space%2Fstatic%2F17c6770ef769ebef0768ab0ca6e962f3%2F6295b%2Funsafe-graphql-architecture.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%2Fremi.space%2Fstatic%2F17c6770ef769ebef0768ab0ca6e962f3%2F6295b%2Funsafe-graphql-architecture.png" alt="Unsafe GraphQL architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In our case, we have 3 islands of type safety, but they're not connected to each other. To share our models from one end to the other, we need to build bridges on both sides.&lt;/p&gt;

&lt;h2&gt;
  
  
  The frontend bridge
&lt;/h2&gt;

&lt;p&gt;Our frontend has access to the GraphQL schema. We need a tool that will &lt;a href="https://graphql.org/learn/introspection/" rel="noopener noreferrer"&gt;introspect&lt;/a&gt; it, and map the types to TypeScript. Then we can use the TS types like we normally would.&lt;/p&gt;

&lt;p&gt;Apollo built a CLI with &lt;a href="https://github.com/apollographql/apollo-tooling#apollo-clientcodegen-output" rel="noopener noreferrer"&gt;this very feature&lt;/a&gt;. To help Apollo find your schema, you will first need to &lt;a href="https://www.apollographql.com/docs/devtools/apollo-config/" rel="noopener noreferrer"&gt;create an &lt;code&gt;apollo.config.js&lt;/code&gt; file&lt;/a&gt;. But the CLI itself also provides many options. After some tweaking, here is the script I added to my last project's &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&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="nl"&gt;"apollo:types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rm -rf src/__generated__ &amp;amp;&amp;amp; apollo client:codegen --config apollo.config.js --target typescript --outputFlat src/__generated__"&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 can then type &lt;code&gt;npm run apollo:types&lt;/code&gt; when there's a change in our schema or our queries and mutations. We can also keep it running in the background using &lt;code&gt;npm run apollo:types --watch&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Note that the Apollo CLI can also be replaced by other tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://graphql-code-generator.com/" rel="noopener noreferrer"&gt;GraphQL Code Generator&lt;/a&gt; generates queries with the types already built-in&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://gqless.dev/" rel="noopener noreferrer"&gt;gqless&lt;/a&gt; provides TypeScript types, and only requires code generation when our schema changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We have now fixed half of the problem. Let's see the other side!&lt;/p&gt;

&lt;h2&gt;
  
  
  The backend bridge
&lt;/h2&gt;

&lt;p&gt;The next challenge is to make sure our GraphQL schema is tightly linked to our backend application code, which includes the resolvers and all the business logic. The way we do this depends on our strategy to generate the schema: SDL-first or code-first.&lt;/p&gt;

&lt;h3&gt;
  
  
  SDL-first
&lt;/h3&gt;

&lt;p&gt;If we write our GraphQL schema by hand, then just like for the frontend, we will need to set up code generation. This is why Prisma built &lt;a href="https://github.com/prisma-labs/graphqlgen" rel="noopener noreferrer"&gt;&lt;code&gt;graphqlgen&lt;/code&gt;&lt;/a&gt;. It will make sure that our resolvers map one-to-one with our schema.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code-first
&lt;/h3&gt;

&lt;p&gt;Code-first GraphQL development is also a good strategy. The process is reversed, you first write your types by hand in TypeScript, then they are used to generate a GraphQL schema.&lt;/p&gt;

&lt;p&gt;Once again, several &lt;a href="https://github.com/graphql/graphql-js" rel="noopener noreferrer"&gt;other&lt;/a&gt; &lt;a href="https://nexus.js.org/" rel="noopener noreferrer"&gt;tools&lt;/a&gt; exist to create code-first GraphQL schemas in Node. But I'll only show the one I have used and loved, &lt;a href="https://typegraphql.com/" rel="noopener noreferrer"&gt;TypeGraphQL&lt;/a&gt;. It uses &lt;a href="https://www.typescriptlang.org/docs/handbook/decorators.html" rel="noopener noreferrer"&gt;decorators&lt;/a&gt; to extract a GraphQL schema from your TypeScript classes.&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;registerEnumType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Authorized&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;type-graphql&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Field&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;type&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;Rate&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="nx"&gt;ratings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Rate&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;nullable&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;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;averageRating&lt;/span&gt;&lt;span class="p"&gt;()&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;sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ratings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&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;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ratings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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;Not everyone loves decorators. They're still called an &lt;em&gt;experimental&lt;/em&gt; TypeScript feature, and you can see there are a few odd details in the syntax. But what they enable is huge. There's no script running in the background, we write TypeScript types like we're used to – but now we have a GraphQL schema.&lt;/p&gt;

&lt;p&gt;This aproach is powerful because it's extensible by design. We can use it hand in hand with a decorator-based ORM, like &lt;a href="https://github.com/typeorm/typeorm" rel="noopener noreferrer"&gt;TypeORM&lt;/a&gt; or &lt;a href="https://github.com/typegoose/typegoose" rel="noopener noreferrer"&gt;Typegoose&lt;/a&gt;. See what that looks like:&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Entity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PrimaryGeneratedColumn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Column&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;typeorm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;registerEnumType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Authorized&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;type-graphql&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Entity&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Available on both database and schema&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;PrimaryGeneratedColumn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Available on both database and schema&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;unique&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;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Field&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Available on database but NOT on schema&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;passwordHash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Available on both database and schema&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;OneToMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Rate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rate&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;type&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;Rate&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="nx"&gt;ratings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Rate&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="c1"&gt;// Available on schema but NOT database&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;nullable&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;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;averageRating&lt;/span&gt;&lt;span class="p"&gt;()&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;sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ratings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&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;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ratings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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;We have now colocated the types required by TypeScript, our database and GraphQL in a single class. To remove a field from GraphQL, or add it to our database, we add or remove a decorator. It's &lt;a href="https://github.com/MichalLytek/type-graphql/issues/296" rel="noopener noreferrer"&gt;still not 100% type-safe yet&lt;/a&gt;, but we greatly reduce the risk of making mistakes by having all the type information grouped together.&lt;/p&gt;

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

&lt;p&gt;Let's see the architecture we ended up with:&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%2Fremi.space%2Fstatic%2Fdc259ec182e6484cee316d63e14196e2%2Fffe34%2Ftype-safe-graphql-architecture.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%2Fremi.space%2Fstatic%2Fdc259ec182e6484cee316d63e14196e2%2Fffe34%2Ftype-safe-graphql-architecture.png" alt="Type-safe GraphQL architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope you get to love all the benefits that type safety brings. It improves the developer experience by making our tooling smarter. We get autocomplete in our code editor, and linters warn us when we write bugs. Most importantly, just like a good test suite, it gives us more confidence that our code is solid. Enjoy!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally posted on my &lt;a href="https://remi.space/blog/typescript-graphql-type-safety/" rel="noopener noreferrer"&gt;personal blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>git</category>
    </item>
    <item>
      <title>5 tips to keep a clean Git history</title>
      <dc:creator>Rémi de Juvigny</dc:creator>
      <pubDate>Mon, 13 Apr 2020 22:34:29 +0000</pubDate>
      <link>https://dev.to/remidej/5-tips-to-keep-a-clean-git-history-2593</link>
      <guid>https://dev.to/remidej/5-tips-to-keep-a-clean-git-history-2593</guid>
      <description>&lt;p&gt;Your project's Git history is a powerful time machine. Here's how to avoid flooding it with useless noise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Squash your commits
&lt;/h2&gt;

&lt;p&gt;I like committing my changes frequently. It makes me feel safe knowing my changes are saved somewhere, so I commit every time I take a small break. But that means my commits become meaningless. They reflect what my days look like, not what's in my code.&lt;/p&gt;

&lt;p&gt;Some changes only make sense when grouped together. But they end up spread out across commits like this:&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%2Fremi.space%2Fstatic%2Fab9637ce1263529ab05580e5f82e072d%2Fa3e09%2Fredundant-commits.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%2Fremi.space%2Fstatic%2Fab9637ce1263529ab05580e5f82e072d%2Fa3e09%2Fredundant-commits.png" alt="Redundant commits"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's hard giving each one a meaningful name, because they have no individual value. They're just here to let me go back in time if I mess up too much. And that's fine – as long as they don't stay around forever.&lt;/p&gt;

&lt;p&gt;To clean up a bunch of redundant commits, you can squash them. That means combining them into a single commit that contains all of their changes. Suddenly your history becomes neat and relevant.&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%2Fremi.space%2Fstatic%2F7593e36e5e87c21de8a5176995fe4415%2F08a84%2Fsquashed-commits.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%2Fremi.space%2Fstatic%2F7593e36e5e87c21de8a5176995fe4415%2F08a84%2Fsquashed-commits.png" alt="Squashed commits"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can squash the commits using the CLI, but the syntax is quite fuzzy (&lt;a href="http://stackoverflow.com/a/5201642/295797" rel="noopener noreferrer"&gt;from Stack Overflow&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Squashes the last 3 commits&lt;/span&gt;
git reset &lt;span class="nt"&gt;--soft&lt;/span&gt; HEAD~3 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git commit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The most convenient way to squash commits is during Pull Requests. Both GitHub and GitLab let you combine your commits when you decide to merge your branch.&lt;/p&gt;

&lt;p&gt;Using GitHub : &lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fremi.space%2Fstatic%2F6546ce42aa631485470fa017bf674ddd%2F96658%2Fgithub-squash-and-merge.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%2Fremi.space%2Fstatic%2F6546ce42aa631485470fa017bf674ddd%2F96658%2Fgithub-squash-and-merge.png" alt="GitHub squashed commits"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using GitLab :&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fremi.space%2Fstatic%2Fb313dd8a9a6e8f64bb630503ebce3420%2Fd74fe%2Fgitlab-squash-and-merge.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%2Fremi.space%2Fstatic%2Fb313dd8a9a6e8f64bb630503ebce3420%2Fd74fe%2Fgitlab-squash-and-merge.png" alt="GitLab commits"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Rebase before merging
&lt;/h2&gt;

&lt;p&gt;When you're coding a new feature, it's a good practice to work on a separate branch – often called a &lt;em&gt;feature branch&lt;/em&gt;. But by the time your feature is ready, new commits have probably been added to the master branch.&lt;/p&gt;

&lt;p&gt;This means Git won't be able to perform a &lt;em&gt;fast-forward&lt;/em&gt; merge. Instead, it will have to create a merge commit to reconcile the history of the two branches.&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%2Fremi.space%2Fstatic%2Ff3960fed840828381bec5ca96a18245a%2F2a50c%2Fmerge-commit.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%2Fremi.space%2Fstatic%2Ff3960fed840828381bec5ca96a18245a%2F2a50c%2Fmerge-commit.png" alt="Merge commit"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This adds quite a lot of complexity to your history. You get a new weird, auto-generated commit, and your tree becomes two-dimensional, making it hard to read.&lt;/p&gt;

&lt;p&gt;Ideally, we would have waited for the commits &lt;code&gt;B&lt;/code&gt; and &lt;code&gt;C&lt;/code&gt; from our diagram before making our own branch. This would be a terrible workflow, preventing us from working in parallel with our colleagues. But we can simulate this scenario with a simple command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# from the feature branch&lt;/span&gt;
git rebase origin/master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rebase modifies the history of our branch. It re-applies all of our changes on top of the latest version of &lt;code&gt;master&lt;/code&gt;.&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%2Fremi.space%2Fstatic%2F8d9bc04bf9fce36fa13ffac4c5d25adc%2F07a9c%2Frebase-merge.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%2Fremi.space%2Fstatic%2F8d9bc04bf9fce36fa13ffac4c5d25adc%2F07a9c%2Frebase-merge.png" alt="Rebase and merge"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After rebasing, when we run &lt;code&gt;git merge master&lt;/code&gt;, Git is able to do a fast-forward merge. Now we have a neat, linear and readable Git history. If you're using GitHub pull requests, you can use "Rebase and merge" to make this process automatic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stash your unfinished changes
&lt;/h2&gt;

&lt;p&gt;Sometimes I start making changes in my code before realizing that I'm in the wrong branch. The trap would be to checkout to that branch immediately – then our uncommitted changes would be &lt;a href="https://stackoverflow.com/questions/2961240/get-back-the-changes-after-accidental-checkout" rel="noopener noreferrer"&gt;lost&lt;/a&gt;! We could also commit these changes before running &lt;code&gt;git checkout&lt;/code&gt;, but we would end up having an unwanted commit in the wrong branch.&lt;/p&gt;

&lt;p&gt;Git has the perfect command for that use case: &lt;code&gt;git stash&lt;/code&gt;. Stashing clears all the changes we've made since our last commit. This gives us a clean state that allows us to update our context, whether we want to switch branches or pull changes. But the changes aren't lost, they are saved in invisible storage. We can think of it as a glorified clipboard, able to store changes across many files.&lt;/p&gt;

&lt;p&gt;Then, we just type &lt;code&gt;git stash pop&lt;/code&gt;, and our changes are restored.&lt;/p&gt;

&lt;h2&gt;
  
  
  Amend your last commit
&lt;/h2&gt;

&lt;p&gt;It's usually right after making a commit that my mistakes stand out. Maybe it's a typo, a forgotten console log, or useless commented code. Either way, it's silly can be fixed by a one-line commit.&lt;/p&gt;

&lt;p&gt;But then our history feels wrong. It puts our original commit, which may represent a full day of work, on the same level as our tiny hotfix. We're also exposing our mistake to everyone on the project, which sucks.&lt;/p&gt;

&lt;p&gt;To avoid this, we can instead modify the history, and add our fix to the last commit. After staging your changes, run this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;--amend&lt;/span&gt; &lt;span class="nt"&gt;--no-edit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And &lt;em&gt;voilà&lt;/em&gt;, we sneakily corrected our mistake. Note that if we already pushed our last commit, we would have to force push our amendment. This is fine if we're working on your own branch, but we should avoid doing it on &lt;code&gt;master&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Visualize your Git tree
&lt;/h2&gt;

&lt;p&gt;When we work with several people on multiple branches, it's easy to get lost. We then make mistakes because we don't have a good understanding of how we got there. To avoid this, we can display a visualization of our Git history. We can do this using the Git CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git log &lt;span class="nt"&gt;--graph&lt;/span&gt; &lt;span class="nt"&gt;--oneline&lt;/span&gt; &lt;span class="nt"&gt;--all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we want more comprehensive visual feedback, we can use a Git GUI. For example, here's what Sublime Merge would look like:&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%2Fremi.space%2Fstatic%2Fd38624c7cb3107ec35d4a46924526d7e%2F5a190%2Fsublime-merge.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%2Fremi.space%2Fstatic%2Fd38624c7cb3107ec35d4a46924526d7e%2F5a190%2Fsublime-merge.png" alt="Sublime Merge"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can perform all Git commands using the CLI, but sometimes using an app makes more sense. Some complex actions – like interactive rebases, cherry-picking, or managing stashes – are hard to grasp using only the command line. Having a GUI helps us make less mistakes.&lt;/p&gt;

&lt;p&gt;I hope this was helpful. If you want to improve your mental model of advanced Git commands, I recommend reading &lt;a href="https://dev.to/lydiahallie/cs-visualized-useful-git-commands-37p1"&gt;Lydia Hallie's article&lt;/a&gt;. Let me know if you have more tips to share, and happy coding!&lt;/p&gt;

</description>
      <category>git</category>
    </item>
    <item>
      <title>How do you introduce yourself to recruiters?</title>
      <dc:creator>Rémi de Juvigny</dc:creator>
      <pubDate>Sat, 21 Dec 2019 15:55:11 +0000</pubDate>
      <link>https://dev.to/remidej/how-do-you-introduce-yourself-to-recruiters-3757</link>
      <guid>https://dev.to/remidej/how-do-you-introduce-yourself-to-recruiters-3757</guid>
      <description>&lt;p&gt;The &lt;a href="https://2019.stateofjs.com/" rel="noopener noreferrer"&gt;2019 State of JS survey results&lt;/a&gt; were published a few days ago, revealing many precious insights about the industry.&lt;/p&gt;

&lt;p&gt;Among the questions asked was an odd one: &lt;em&gt;How do you introduce yourself at parties?&lt;/em&gt; Of course, Twitter did its thing and had a good laugh picturing that social situation.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1208040661737496576-161" src="https://platform.twitter.com/embed/Tweet.html?id=1208040661737496576"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1208040661737496576-161');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1208040661737496576&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;But what I find most unfortunate is that the options were limited to the traditional front-end, back-end, web and full-stack developers. As I'll be on the job market soon, I've been thinking about the best way to sell myself on the job market, while staying authentic.&lt;/p&gt;

&lt;p&gt;If you could write your own job description, what would it be? &lt;strong&gt;How would you introduce yourself to a recruiter?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I've gathered a few titles that I find interesting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UI developer/engineer&lt;/li&gt;
&lt;li&gt;Creative developer&lt;/li&gt;
&lt;li&gt;Product developer/engineer&lt;/li&gt;
&lt;li&gt;Devops&lt;/li&gt;
&lt;li&gt;Software developer/engineer&lt;/li&gt;
&lt;li&gt;Software architect&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The one that resonates with me is &lt;em&gt;Product Developer&lt;/em&gt;. I like to have an impact on what I'm building, not just how I build it.&lt;/p&gt;

&lt;p&gt;What about you?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>career</category>
    </item>
  </channel>
</rss>
