<?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: Arnaud Cortisse</title>
    <description>The latest articles on DEV Community by Arnaud Cortisse (@arnaudcortisse).</description>
    <link>https://dev.to/arnaudcortisse</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%2F234149%2F707113a1-7f5a-4f3b-b47c-ee5bef1d7a5a.jpg</url>
      <title>DEV Community: Arnaud Cortisse</title>
      <link>https://dev.to/arnaudcortisse</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/arnaudcortisse"/>
    <language>en</language>
    <item>
      <title>Trying out NestJS part 4: Generate Typescript clients from OpenAPI documents</title>
      <dc:creator>Arnaud Cortisse</dc:creator>
      <pubDate>Sun, 28 Mar 2021 13:55:27 +0000</pubDate>
      <link>https://dev.to/arnaudcortisse/trying-out-nestjs-part-4-generate-typescript-clients-from-openapi-documents-28mk</link>
      <guid>https://dev.to/arnaudcortisse/trying-out-nestjs-part-4-generate-typescript-clients-from-openapi-documents-28mk</guid>
      <description>&lt;p&gt;&lt;em&gt;This article was originally published on my &lt;a href="https://arnaudcortisse.com/blog"&gt;personal blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In my &lt;a href="https://dev.to/arnaudcortisse/trying-out-nestjs-part-3-creating-an-openapi-document-3800"&gt;last blog post&lt;/a&gt;, we saw how easy it is to get started with OpenAPI using NestJS.&lt;/p&gt;

&lt;p&gt;In this blog post, I'd like to show you how you can leverage the generated OpenAPI document in order to generate a typescript client that's going to be used in the React App.&lt;/p&gt;

&lt;p&gt;Why would I do that? I like to have statically-typed endpoints, rather than having to do the typing myself. Besides, the fact that it's automatically generated means that we can automate the generation in a CI and make sure everything is OK at compile-time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;The source code for this part of the project is available here: &lt;a href="https://github.com/arnaud-cortisse/trying-out-nestjs-part-4"&gt;https://github.com/arnaud-cortisse/trying-out-nestjs-part-4&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenAPI Generator
&lt;/h2&gt;

&lt;p&gt;There are plenty of tools that we can use in order to generate OpenAPI clients.&lt;/p&gt;

&lt;p&gt;The one I'm going to use is the following: &lt;a href="https://openapi-generator.tech/docs/generators/typescript-axios"&gt;typescript-axios&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The OpenAPI document
&lt;/h2&gt;

&lt;p&gt;In the last blog post I only told you about &lt;code&gt;http://localhost:3001/api/&lt;/code&gt;, which hosts the Swagger UI.&lt;/p&gt;

&lt;p&gt;But there is another key endpoint: &lt;code&gt;http://localhost:3001/api-json&lt;/code&gt;. This endpoint hosts the generated OpenAPI document that we'll refer to in order to generate the client.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up the environment for the OpenAPI generator
&lt;/h2&gt;

&lt;p&gt;The OpenAPI generator tool requires that we install several dependencies on our machine, but I don't like to bloat my machine with project-specific dependencies.&lt;/p&gt;

&lt;p&gt;Let's try to make use of Docker again!&lt;/p&gt;

&lt;h3&gt;
  
  
  Preparing the files
&lt;/h3&gt;

&lt;p&gt;In the root folder, execute the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mkdir -p tools/openapi-generator&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cd tools/openapi-generator&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;touch Dockerfile&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;touch openapitools.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;touch generate.sh&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;touch .gitignore&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  tools/openapi-generator/Dockerfile
&lt;/h4&gt;

&lt;p&gt;This docker image is going to be used for generating the OpenAPI document by reaching out to NestJS's &lt;code&gt;/api-json&lt;/code&gt; endpoint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM timbru31/java-node:jdk-14
RUN npm install @openapitools/openapi-generator-cli -g
RUN mkdir /local
WORKDIR /local
COPY . .
CMD ["sh", "generate.sh"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;We make use of a docker image with preinstalled JDK (because &lt;code&gt;openapi-generator-cli&lt;/code&gt; needs it).&lt;/li&gt;
&lt;li&gt;We install the &lt;code&gt;openapi-generator-cli&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We create a folder &lt;code&gt;/local&lt;/code&gt; and copy everything that's in &lt;code&gt;/tools/openapi-generator&lt;/code&gt; into it.&lt;/li&gt;
&lt;li&gt;When starting the image we launch the script &lt;strong&gt;generate.sh&lt;/strong&gt; (we still need to fill it).&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  tools/openapi-generator/openapitools.json
&lt;/h4&gt;

&lt;p&gt;The OpenAPI generator configuration file. See &lt;a href="https://github.com/OpenAPITools/openapi-generator-cli#configuration"&gt;Configuration&lt;/a&gt; for more info.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json",
  "spaces": 2,
  "generator-cli": {
    "version": "5.0.0"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  tools/openapi-generator/generate.sh
&lt;/h4&gt;

&lt;p&gt;The script that's executed when starting the newly defined &lt;code&gt;Dockerfile&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;openapi-generator-cli generate \
    -i http://nestjs:3001/api-json \
    --generator-name typescript-axios \
    -o /local/out \
    --additional-properties=useSingleRequestParameter=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-i&lt;/code&gt; param indicates where the OpenAPI document is located. Here I decided to use &lt;a href="http://nestjs:3001/api-json"&gt;http://nestjs:3001/api-json&lt;/a&gt; instead of &lt;a href="http://localhost:3001/api-json"&gt;http://localhost:3001/api-json&lt;/a&gt; (both work, but I prefer the former). You won't be able to access &lt;a href="http://nestjs:3001/api-json"&gt;http://nestjs:3001/api-json&lt;/a&gt; in your browser, since it's not a name you are able to resolve on your machine (but that is resolvable within docker-compose, since both images are going to run in the same network).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--generator-name&lt;/code&gt; to indicate that generator we wanna use.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-o&lt;/code&gt; to indicate where we want to ouput the generated files.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--additional-properties&lt;/code&gt; is used to provide additional parameters to the generator (see &lt;a href="https://github.com/OpenAPITools/openapi-generator/blob/master/docs/generators/typescript-axios.md"&gt;this page&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  tools/openapi-generator/.gitignore
&lt;/h4&gt;

&lt;p&gt;We don't want to version the file that are output by generator in this folder (but we will version the generated files in the React App).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Modifying docker-compose.yml
&lt;/h3&gt;

&lt;p&gt;Let's make it possible to start &lt;code&gt;openapi_generator&lt;/code&gt; from the existing &lt;code&gt;docker-compose&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  openapi_generator:
    build:
      context: ./tools/openapi-generator
      dockerfile: Dockerfile
    depends_on:
      - nestjs
    volumes:
      - ./tools/openapi-generator/.build:/local/out
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;We make the service depend on &lt;code&gt;nestjs&lt;/code&gt;. That way, &lt;code&gt;nestjs&lt;/code&gt; is going to be started if it hadn't been already before. Indeed, it is mandatory for &lt;code&gt;nestjs&lt;/code&gt; to be running in order for the &lt;code&gt;openapi_generator&lt;/code&gt; to be able to generate the client API.&lt;/li&gt;
&lt;li&gt;We mount the folder &lt;code&gt;./tools/openapi-generator/.build&lt;/code&gt; inside the service, where the client is going to be generated (we configured that path ourselves just above). That way, we get access to the generated files on the host machine.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Modifying the root package.json
&lt;/h3&gt;

&lt;p&gt;In the root &lt;code&gt;package.json&lt;/code&gt;, add the following script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"scripts": {
  ...
    "generate-api-client": "docker-compose up --build openapi_generator"
  ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Trying out the OpenAPI Generator
&lt;/h3&gt;

&lt;p&gt;In the root folder, type the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;npm run generate-api-client&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If everything went fine, you should have files in this folder: &lt;code&gt;tools/openapi-generator/.build&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you don't have any files, it might be because the &lt;code&gt;nestjs&lt;/code&gt; service wasn't ready yet when the generator tried to reach it. Just try to relaunch &lt;code&gt;npm run generate-api-client&lt;/code&gt; and everything should be OK.&lt;/p&gt;

&lt;h3&gt;
  
  
  Delivering the client to the React App.
&lt;/h3&gt;

&lt;p&gt;In the root folder, execute the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mkdir scripts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;touch scripts/update-api.sh&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  update-api.sh
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash
cd "$(dirname "$0")"

SOURCE_FOLDER="../tools/openapi-generator/.build"
DEST_FOLDER="../packages/react-app/src/api/generated"

rm -rf $DEST_FOLDER
mkdir -p $DEST_FOLDER
cp $SOURCE_FOLDER/**.ts $DEST_FOLDER
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this script, we essentially are delivering the files generated automatically by the service &lt;code&gt;openapi_generator&lt;/code&gt; to the React App.&lt;/p&gt;

&lt;h4&gt;
  
  
  Modifying the root package.json
&lt;/h4&gt;

&lt;p&gt;In the root &lt;code&gt;package.json&lt;/code&gt;, add the following scripts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"scripts": {
  ...
    "update-api-client": "sh ./scripts/update-api.sh",
    "generate-and-update-api-client": "npm run generate-api-client &amp;amp;&amp;amp; npm run update-api-client"
  ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Trying out the delivery mechanism
&lt;/h4&gt;

&lt;p&gt;In the root folder, type the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;npm run generate-and-update-api-client&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If everything went well, you should have files in &lt;code&gt;packages/react-app/src/api/generated&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make use of the client in the React App
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Installing new dependencies
&lt;/h3&gt;

&lt;p&gt;In the &lt;code&gt;packages/react-app/src&lt;/code&gt; directory, execute the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;npm install axios react-query&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Deleting some files
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cd packages/react-app/src&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rm App.css App.test.tsx App.tsx&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Creating new files
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cd packages/react-app/src&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mkdir axios&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mkdir api&lt;/code&gt; (but it should already exist)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mkdir components&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;touch axios/axios-client.ts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;touch api/api.ts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;touch components/App.tsx&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;touch components/Example.tsx&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  packages/react-app/src/axios/axios-client.ts
&lt;/h4&gt;

&lt;p&gt;Used to configure an axios instance so that it is preconfigured to reach to NestJS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import axios, { AxiosRequestConfig } from "axios";

export const axiosBaseUrl = `${process.env.REACT_APP_BACKEND_SCHEMA}://${process.env.REACT_APP_BACKEND_HOSTNAME}:${process.env.REACT_APP_BACKEND_PORT}`;

export const axiosConfig: AxiosRequestConfig = {
  baseURL: axiosBaseUrl,
};

const axiosBackendClient = axios.create(axiosConfig);

export default axiosBackendClient;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  packages/react-app/src/api/api.ts
&lt;/h4&gt;

&lt;p&gt;Configuration of an instance of &lt;code&gt;TasksApi&lt;/code&gt; (a class automatically generated by the generator) that we'll use to communicate with our backend.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import axiosBackendClient, { axiosBaseUrl } from "../axios/axios-client";
import { TasksApi } from "./generated";

export const tasksApi = new TasksApi(
  {
    basePath: axiosBaseUrl,
    isJsonMime: () =&amp;gt; false,
  },
  undefined,
  axiosBackendClient
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  packages/react-app/src/components/App.tsx
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from "react";

import { QueryClient, QueryClientProvider } from "react-query";
import Example from "./Example";

const queryClient = new QueryClient();

export default function App() {
  return (
    &amp;lt;QueryClientProvider client={queryClient}&amp;gt;
      &amp;lt;Example /&amp;gt;
    &amp;lt;/QueryClientProvider&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;We configure the &lt;code&gt;react-query&lt;/code&gt; provider.&lt;/li&gt;
&lt;li&gt;We render the &lt;code&gt;Example&lt;/code&gt; component (yet to be defined).&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  packages/react-app/src/components/Example.tsx
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useQuery } from "react-query";
import { tasksApi } from "../api/api";

export default function Example() {
  const id = "fake id";

  const { isLoading, error, data } = useQuery(`tasks_find_one_${id}`, () =&amp;gt;
    tasksApi.tasksControllerFindOne({
      id,
    })
  );

  if (isLoading) return &amp;lt;div&amp;gt;Loading...&amp;lt;/div&amp;gt;;

  if (error as Error) return &amp;lt;div&amp;gt;An error has occurred&amp;lt;/div&amp;gt;;

  return &amp;lt;div&amp;gt;{data?.data.title}&amp;lt;/div&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Have a look at the query. This is where the magic happens: we make use of the automatically-generated client and, as a result, have all the benefits of static types.&lt;/p&gt;

&lt;h3&gt;
  
  
  Modifying existing files
&lt;/h3&gt;

&lt;h4&gt;
  
  
  packages/react-app/src/index.tsx
&lt;/h4&gt;

&lt;p&gt;I just removed some useless lines (in the context of this blog) and imported the &lt;code&gt;App&lt;/code&gt; component from the appropriate path.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./components/App";

ReactDOM.render(
  &amp;lt;React.StrictMode&amp;gt;
    &amp;lt;App /&amp;gt;
  &amp;lt;/React.StrictMode&amp;gt;,
  document.getElementById("root")
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Trying out the client
&lt;/h3&gt;

&lt;p&gt;In the root folder, perform the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;docker-compose up --build&lt;/code&gt; (might take a while since there are new dependencies in the React App to be installed).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Go over &lt;code&gt;http://localhost:3000/&lt;/code&gt; in your browser.&lt;/p&gt;

&lt;p&gt;You should have the following message at some point: &lt;code&gt;An error has occurred&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Open your developer tools: you should see a CORS error. We can fix that by updating the Nest app.&lt;/p&gt;

&lt;h4&gt;
  
  
  Enable CORS
&lt;/h4&gt;

&lt;p&gt;In &lt;code&gt;packages/nestjs/src/main.ts&lt;/code&gt;, add the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
  app.enableCors();
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mind you, you should definitely configure the CORS rules appropriately in a production environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing everything out
&lt;/h2&gt;

&lt;p&gt;Now, if you go on &lt;code&gt;http://localhost:3000/&lt;/code&gt; in your browser, you should see the message &lt;code&gt;fake title&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It means we are indeed able to communicate with our API using an automatically-generated client.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Words
&lt;/h2&gt;

&lt;p&gt;Setting everything up wasn't straightforward. Nevertheless, we now have a nice way of communicating with our API: we have a typed client that's going to greatly improve the development experience inside React. What's more, it basically costs nothing to regenerate that client so that it matches the latest API. Lastly, we are now able to detect any desynchronization between the React App and the NestJS app at compile time.&lt;/p&gt;

</description>
      <category>nestjs</category>
      <category>react</category>
      <category>docker</category>
      <category>openapi</category>
    </item>
    <item>
      <title>Trying out NestJS part 3: Creating an OpenAPI document</title>
      <dc:creator>Arnaud Cortisse</dc:creator>
      <pubDate>Sat, 06 Mar 2021 15:31:03 +0000</pubDate>
      <link>https://dev.to/arnaudcortisse/trying-out-nestjs-part-3-creating-an-openapi-document-3800</link>
      <guid>https://dev.to/arnaudcortisse/trying-out-nestjs-part-3-creating-an-openapi-document-3800</guid>
      <description>&lt;p&gt;&lt;em&gt;This article was originally published on my &lt;a href="https://arnaudcortisse.com/blog" rel="noopener noreferrer"&gt;personal blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In my &lt;a href="https://dev.to/arnaudcortisse/trying-out-nestjs-part-2-creating-rest-endpoints-1cc8"&gt;last blog post&lt;/a&gt;, I showed you how easy it is to set up REST API routes using NestJS.&lt;/p&gt;

&lt;p&gt;In this blog post, I'd like to try out the OpenAPI module NestJS provides. I'm doing it at this stage because I want to be able to test my routes easily without resorting to any external tools (like Postman).&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;The source code for this part of the project is available here: &lt;a href="https://github.com/arnaud-cortisse/trying-out-nestjs-part-3" rel="noopener noreferrer"&gt;https://github.com/arnaud-cortisse/trying-out-nestjs-part-3&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The official documentation is here: &lt;a href="https://docs.nestjs.com/openapi/introduction" rel="noopener noreferrer"&gt;OpenAPI&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stop the running Docker containers, if any.&lt;/li&gt;
&lt;li&gt;Go in &lt;code&gt;packages/nestjs&lt;/code&gt; and run &lt;code&gt;npm install --save @nestjs/swagger swagger-ui-express&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Go in the root directory and type in &lt;code&gt;docker-compose up --build&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now you're good to go with the OpenAPI module.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the OpenAPI document
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;bootstrap&lt;/code&gt; function (in main.ts) is where you're going to instantiate the OpenAPI module.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { NestFactory } from '@nestjs/core';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  const config = new DocumentBuilder()
    .setTitle('Tasks')
    .setDescription('The tasks API description')
    .setVersion('1.0')
    .addTag('tasks')
    .build();

  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('api', app, document);

  await app.listen(process.env.NEST_PORT);
}
bootstrap();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code speaks for itself: we're essentially setting up the module that's going to deal with the OpenAPI documentation. &lt;code&gt;SwaggerModule.setup('api', app, document);&lt;/code&gt; makes the documentation available on the route /api.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing Current Setup
&lt;/h2&gt;

&lt;p&gt;Once &lt;code&gt;main.ts&lt;/code&gt; has been modified, just go over &lt;code&gt;http://localhost:3001/api/&lt;/code&gt; in the browser. You should have 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%2Fft2rc28odflm6wjdcx1x.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%2Fft2rc28odflm6wjdcx1x.png" alt="openapi_1"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;As you can see, the &lt;code&gt;OpenAPI&lt;/code&gt; module in NestJS is pretty cool: it automatically picks up the existing routes and DTOs and makes them available.&lt;/p&gt;

&lt;p&gt;However, you can see that every route is under the tab &lt;strong&gt;default&lt;/strong&gt;, while I would have liked the routes related to the &lt;strong&gt;Tasks&lt;/strong&gt; to be under the tab &lt;strong&gt;Tasks&lt;/strong&gt;. In order to fix that, modify the &lt;code&gt;tasks.controller.ts&lt;/code&gt; file and add the &lt;code&gt;@ApiTags('tasks')&lt;/code&gt; decorator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
import { ApiTags } from '@nestjs/swagger';

@ApiTags('tasks')
@Controller('tasks')
export class TasksController {
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and reload &lt;code&gt;http://localhost:3001/api/&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the Task DTO
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;TaskDto&lt;/code&gt; is going to be a class containing an exhaustive list of all the properties a Task contains.&lt;/p&gt;

&lt;p&gt;Let's create a file &lt;code&gt;task.dto.ts&lt;/code&gt; inside &lt;code&gt;./packages/nestjs/tasks/dto/&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { ApiProperty } from '@nestjs/swagger';

export class TaskDto {
  id: number;
  title: string;
  description: string;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Modifying CreateTaskDto
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;CreateTaskDto&lt;/code&gt; will be used to create new &lt;code&gt;Tasks&lt;/code&gt;. Right now it's empty (both in code and in the OpenAPI document). Let's change that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { PickType } from '@nestjs/swagger';
import { TaskDto } from './task.dto';

export class CreateTaskDto extends PickType(TaskDto, [
  'description',
  'title',
] as const) {}

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

&lt;/div&gt;



&lt;p&gt;You can see that I used the utility class &lt;code&gt;PickType&lt;/code&gt;. That way, we can keep the code as DRY as possible.&lt;/p&gt;

&lt;p&gt;When creating a task, we want to provide a title and a description, while the ID should somehow be generated internally (we don't have any DB yet).&lt;/p&gt;

&lt;p&gt;When I refresh &lt;code&gt;http://localhost:3001/api/&lt;/code&gt;, I can't see any of my changes reflected in the OpenAPI document:&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%2F4sh1gw7u8vo0jwjv7gk2.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%2F4sh1gw7u8vo0jwjv7gk2.png" alt="create-task-dto"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's because I didn't explicitly tell the OpenAPI module that I wanted the new properties to be exposed. To do so, just add the decorator &lt;code&gt;@ApiProperty&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export class TaskDto {
  @ApiProperty()
  id: number;

  @ApiProperty()
  title: string;

  @ApiProperty()
  description: string;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can see those properties if you go over &lt;code&gt;http://localhost:3001/api/&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7sa0mi6glmn07h72j7t2.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%2F7sa0mi6glmn07h72j7t2.png" alt="create-task-dto-1"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;But what's cool about it is that I can now play with the OpenAPI UI more easily.&lt;/p&gt;

&lt;p&gt;Say that you want to test the route POST /tasks. Just use the OpenAPI UI (by clicking on &lt;em&gt;Try it out&lt;/em&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvpf2g1pj5qlxe7mv7lz5.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%2Fvpf2g1pj5qlxe7mv7lz5.png" alt="post-tasks"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;The request body will be prefilled with some default values and you'll be able to visualize what is your API replying very easily:&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%2Fk4kndfsrr153ll7vygy0.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%2Fk4kndfsrr153ll7vygy0.png" alt="post-tasks-response"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Modifying UpdateTaskDto
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;UpdateTaskDto&lt;/code&gt; will be used to update our tasks. It currently looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { PartialType } from '@nestjs/mapped-types';
import { CreateTaskDto } from './create-task.dto';

export class UpdateTaskDto extends PartialType(CreateTaskDto) {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We want to be able to update both the &lt;em&gt;title&lt;/em&gt; and the &lt;em&gt;description&lt;/em&gt;, so we don't need the utility class &lt;code&gt;PartialType&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { CreateTaskDto } from './create-task.dto';

export class UpdateTaskDto extends CreateTaskDto {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That change is now reflected in the OpenAPI endpoint:&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%2F8e919bwyxtc7xts1ac32.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%2F8e919bwyxtc7xts1ac32.png" alt="update-task-dto"&gt;&lt;/a&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fudn1ig7y9nloqi5muqsp.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%2Fudn1ig7y9nloqi5muqsp.png" alt="put-tasks"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Add more information about the input parameters
&lt;/h2&gt;

&lt;p&gt;At present the only pieces of information we have about the &lt;em&gt;title&lt;/em&gt; and the &lt;em&gt;description&lt;/em&gt; is that there are strings. We could also decide to provide further information, such as the minimum and the maximum length of those strings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export class TaskDto {
  @ApiProperty({
    description: 'The ID of the task',
    default: 'This is a fake ID',
  })
  id: number;

  @ApiProperty({
    description: 'The title of the task',
    minLength: 3,
    maxLength: 30,
    default: 'This is a fake title',
  })
  title: string;
  @ApiProperty({
    description: 'The description of the task',
    minLength: 0,
    maxLength: 200,
    default: 'This is a fake description',
  })
  description: string;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which translates like this in the OpenAPI endpoint:&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%2Fenquif8ct34akisjsxvu.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%2Fenquif8ct34akisjsxvu.png" alt="api-property-decorator"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Be aware that those decorators don't perform any data validation operations. By the way, NestJS recommends the use of the &lt;a href="https://docs.nestjs.com/techniques/validation" rel="noopener noreferrer"&gt;class-validator&lt;/a&gt; package for data validation (which I'll try out later).&lt;/p&gt;

&lt;h2&gt;
  
  
  Add further information about the responses
&lt;/h2&gt;

&lt;p&gt;Currently our API don't expose what any of its responses look like. The default is status code 200 and doesn't specify the shape of the data. Let's improve that.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;tasks.controller.ts&lt;/code&gt;, add these lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
  @Get(':id')
  @ApiOkResponse({
    description: 'The task has been successfully found.',
    type: TaskDto,
  })
  findOne(@Param('id') id: string) {
    return this.tasksService.findOne(+id);
  }
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;@ApiOkResponse&lt;/code&gt; decorator allows us to specify what our API responds code-wise (200 in this case, but we could have used the &lt;code&gt;@ApiResponse&lt;/code&gt; decorator and specified the response code ourselves) as well as the shape of the data returned. It yields 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%2Fmi82ucfhcm61mr5nc7wh.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%2Fmi82ucfhcm61mr5nc7wh.png" alt="findone-response"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;You can also specify what the response looks like when you can't find the requested task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
  @ApiNotFoundResponse({
    description: "Couldn't find the task",
  })
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final Words
&lt;/h2&gt;

&lt;p&gt;As you can see, getting started with OpenAPI inside NestJS is very easy. I would strongly encourage people to document their APIs using such tools: It's directly integrated inside the source code and it's very easy to maintain. The benefits of documenting your APIs are multiples. Among other things, that makes it very easy to understand and test them.&lt;/p&gt;

</description>
      <category>nestjs</category>
      <category>openapi</category>
    </item>
    <item>
      <title>Trying out NestJS part 2: Creating REST endpoints</title>
      <dc:creator>Arnaud Cortisse</dc:creator>
      <pubDate>Wed, 27 Jan 2021 11:40:03 +0000</pubDate>
      <link>https://dev.to/arnaudcortisse/trying-out-nestjs-part-2-creating-rest-endpoints-1cc8</link>
      <guid>https://dev.to/arnaudcortisse/trying-out-nestjs-part-2-creating-rest-endpoints-1cc8</guid>
      <description>&lt;p&gt;&lt;em&gt;This article was originally published on my &lt;a href="https://arnaudcortisse.com/blog"&gt;personal blog&lt;/a&gt;.&lt;/em&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In my &lt;a href="https://dev.to/arnaudcortisse/trying-out-nestjs-part-1-setting-up-dev-environment-for-your-react-nestjs-applications-that-rocks-12f6"&gt;last blog post&lt;/a&gt;, I talked about why I wanted to give NestJS a try and what project I would build in order to evaluate it.&lt;/p&gt;

&lt;p&gt;In this blog post, I'd like to set up the CRUD endpoints for the &lt;code&gt;Tasks&lt;/code&gt; concept.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before going on
&lt;/h2&gt;

&lt;p&gt;One of the main benefits of using NestJS is that it's very opinionated about the way you should structure your application. As a result, you'll end up with an application that's loosely coupled and highly testable without even thinking about it. &lt;/p&gt;

&lt;p&gt;However, NestJS can be hard to apprehend (especially if you are a junior developer). In my opinion, it's very important that you understand what &lt;code&gt;Dependency Injection&lt;/code&gt; (and broadly speaking, the SOLID principles) and DI containers are since NestJS relies heavily on those concepts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up new endpoints
&lt;/h2&gt;

&lt;p&gt;The source code for this part of the project is available here: &lt;a href="https://github.com/arnaud-cortisse/trying-out-nestjs-part-2"&gt;https://github.com/arnaud-cortisse/trying-out-nestjs-part-2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Creating CRUD endpoints is not very exciting. It's actually very tedious because the code always look the same.&lt;br&gt;
Fortunately, NestJS provides &lt;a href="https://docs.nestjs.com/recipes/crud-generator"&gt;a sweet tool&lt;/a&gt; to generate all the boilerplate required to setup REST endpoints.&lt;/p&gt;
&lt;h3&gt;
  
  
  Using the CRUD generator
&lt;/h3&gt;

&lt;p&gt;Before using the CRUD generator, please install the following package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install @nestjs/mapped-types
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the root of your project, just type in&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nest g resource tasks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will be prompted with several choices. In this case, we're interested in developing a REST API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;? What transport layer do you use? (Use arrow keys)
❯ REST API
  GraphQL (code first)
  GraphQL (schema first)
  Microservice (non-HTTP)
  WebSockets
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, choose YES.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;? Would you like to generate CRUD entry points? (Y/n)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should have the following output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE src/tasks/tasks.controller.spec.ts (566 bytes)
CREATE src/tasks/tasks.controller.ts (890 bytes)
CREATE src/tasks/tasks.module.ts (247 bytes)
CREATE src/tasks/tasks.service.spec.ts (453 bytes)
CREATE src/tasks/tasks.service.ts (609 bytes)
CREATE src/tasks/dto/create-task.dto.ts (30 bytes)
CREATE src/tasks/dto/update-task.dto.ts (169 bytes)
CREATE src/tasks/entities/task.entity.ts (21 bytes)
UPDATE src/app.module.ts (312 bytes)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The NestJS CLI has just generated a lot of boilerplate code for you.&lt;/p&gt;

&lt;p&gt;Let's have a closer look into some of the generated files.&lt;/p&gt;

&lt;h4&gt;
  
  
  tasks.service.ts
&lt;/h4&gt;

&lt;p&gt;It's a &lt;a href="https://docs.nestjs.com/providers"&gt;service&lt;/a&gt; that's going to deal with the data storage and retrieval.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Injectable } from '@nestjs/common';
import { CreateTaskDto } from './dto/create-task.dto';
import { UpdateTaskDto } from './dto/update-task.dto';

@Injectable()
export class TasksService {
  create(createTaskDto: CreateTaskDto) {
    return 'This action adds a new task';
  }

  findAll() {
    return `This action returns all tasks`;
  }

  findOne(id: number) {
    return `This action returns a #${id} task`;
  }

  update(id: number, updateTaskDto: UpdateTaskDto) {
    return `This action updates a #${id} task`;
  }

  remove(id: number) {
    return `This action removes a #${id} task`;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;@Injectable()&lt;/code&gt; is a &lt;a href="https://www.typescriptlang.org/docs/handbook/decorators.html"&gt;TypeScript decorator&lt;/a&gt;.&lt;br&gt;
Decorating classes with &lt;code&gt;@Injectable&lt;/code&gt; allows them to act as &lt;a href="https://docs.nestjs.com/providers"&gt;providers&lt;/a&gt;, that is, instances of classes that can be injected in other instances of classes (at runtime) via &lt;a href="https://en.wikipedia.org/wiki/Dependency_injection"&gt;Dependency Injection&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  tasks.controller.ts
&lt;/h4&gt;

&lt;p&gt;It's a &lt;a href="https://docs.nestjs.com/controllers"&gt;controller&lt;/a&gt; that's going to handle incoming requests related to &lt;code&gt;Tasks&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Controller, Get, Post, Body, Put, Param, Delete } from '@nestjs/common';
import { TasksService } from './tasks.service';
import { CreateTaskDto } from './dto/create-task.dto';
import { UpdateTaskDto } from './dto/update-task.dto';

@Controller('tasks')
export class TasksController {
  constructor(private readonly tasksService: TasksService) {}

  @Post()
  create(@Body() createTaskDto: CreateTaskDto) {
    return this.tasksService.create(createTaskDto);
  }

  @Get()
  findAll() {
    return this.tasksService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.tasksService.findOne(+id);
  }

  @Put(':id')
  update(@Param('id') id: string, @Body() updateTaskDto: UpdateTaskDto) {
    return this.tasksService.update(+id, updateTaskDto);
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.tasksService.remove(+id);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;@Controller()&lt;/code&gt; is a &lt;a href="https://www.typescriptlang.org/docs/handbook/decorators.html"&gt;TypeScript decorator&lt;/a&gt;.&lt;br&gt;
Decorating classes with &lt;code&gt;@Controller&lt;/code&gt;, along with methods decorated with HTTP verbs (&lt;code&gt;@Get()&lt;/code&gt;, &lt;code&gt;@Post()&lt;/code&gt; etc.), allows you to declare the routing of your app.&lt;/p&gt;

&lt;p&gt;You can also notice that the constructor expects an instance of &lt;code&gt;TaskService&lt;/code&gt; (the class we talked about earlier). Behind the scene, an instance of the &lt;code&gt;TaskService&lt;/code&gt; is going to be injected by the DI container (built in the NestJS runtime, see &lt;a href="https://docs.nestjs.com/fundamentals/custom-providers"&gt;custom-providers&lt;/a&gt;).&lt;/p&gt;
&lt;h4&gt;
  
  
  tasks.module.ts
&lt;/h4&gt;

&lt;p&gt;It's a &lt;a href="https://docs.nestjs.com/modules"&gt;module&lt;/a&gt; that's going to hold all the instances of classes related to the &lt;code&gt;Tasks&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Module } from '@nestjs/common';
import { TasksService } from './tasks.service';
import { TasksController } from './tasks.controller';

@Module({
  controllers: [TasksController],
  providers: [TasksService]
})
export class TasksModule {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;@Module()&lt;/code&gt; is a &lt;a href="https://www.typescriptlang.org/docs/handbook/decorators.html"&gt;TypeScript decorator&lt;/a&gt;.&lt;br&gt;
Decorating classes with &lt;code&gt;@Module&lt;/code&gt; allows for regrouping and encapsulating code that's closely related.&lt;/p&gt;

&lt;p&gt;It's also going to affect how the DI container behaves.&lt;/p&gt;
&lt;h4&gt;
  
  
  app.module.ts
&lt;/h4&gt;

&lt;p&gt;The app module is the root module (see &lt;a href="https://docs.nestjs.com/modules"&gt;modules&lt;/a&gt;). It's created inside &lt;code&gt;main.ts&lt;/code&gt;, at startup.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TasksModule } from './tasks/tasks.module';

@Module({
  imports: [TasksModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see the CRUD generator updated &lt;code&gt;imports&lt;/code&gt; and now references &lt;code&gt;TasksModule&lt;/code&gt;, which is required for our app to expose all the routes declared inside &lt;code&gt;TasksController&lt;/code&gt;. &lt;code&gt;TasksModule&lt;/code&gt; is going to be directly connected to the &lt;code&gt;AppModule&lt;/code&gt; in the application graph. &lt;/p&gt;

&lt;p&gt;Not putting &lt;code&gt;TasksModule&lt;/code&gt; inside the imports array would cause your tasks routes not to be available. &lt;/p&gt;

&lt;h3&gt;
  
  
  Test current setup
&lt;/h3&gt;

&lt;p&gt;Go in the root of your project and type in&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose up --build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once everything is up and running, go to &lt;code&gt;http://localhost:3001/tasks&lt;/code&gt; and verify that your get the message "&lt;code&gt;This action returns all tasks&lt;/code&gt;". &lt;/p&gt;

&lt;h3&gt;
  
  
  Final words
&lt;/h3&gt;

&lt;p&gt;As you can see, setting up new REST endpoints with NestJS is extremely easy. &lt;/p&gt;

&lt;p&gt;However, you might be thinking there are way too many files and concepts involved. While I think that's quite true for such a small project, keep in mind that NestJS shines best when dealing with medium to large size projects.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Trying out NestJS part 1: Setting up a dev environment for your React / NestJS applications that rocks</title>
      <dc:creator>Arnaud Cortisse</dc:creator>
      <pubDate>Sun, 03 Jan 2021 11:15:11 +0000</pubDate>
      <link>https://dev.to/arnaudcortisse/trying-out-nestjs-part-1-setting-up-dev-environment-for-your-react-nestjs-applications-that-rocks-12f6</link>
      <guid>https://dev.to/arnaudcortisse/trying-out-nestjs-part-1-setting-up-dev-environment-for-your-react-nestjs-applications-that-rocks-12f6</guid>
      <description>&lt;p&gt;&lt;em&gt;This article was originally published on my &lt;a href="https://arnaudcortisse.com/blog"&gt;personal blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In the context of my current job, I wanted to evaluate the various existing backend frameworks based on NodeJS. Why is that? The only Node.js backend framework I had ever used so far was express, which is an awesome light-weight framework, but that doesn't have any opinion whatsover on how you should structure your app.&lt;/p&gt;

&lt;p&gt;During my investigation, I came across NestJS several times. The most appealing thing to me was its thorough documentation and its large ecosystem. I was especially interested in the OpenAPI integration, which I knew could greatly improve the frontend development experience when coupled with a code generator.&lt;br&gt;
In the end, I decided to create a small POC to see whether it would be a fit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Specifications of the project
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Functional requirements
&lt;/h3&gt;

&lt;p&gt;The POC is going to be a minimal, hideous "TODO list" app (styling is not in the scope of this endeavor).&lt;br&gt;
In this POC, I'll be able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add tasks,&lt;/li&gt;
&lt;li&gt;Remove tasks,&lt;/li&gt;
&lt;li&gt;List all tasks&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Technical requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use Typescript everywhere.&lt;/li&gt;
&lt;li&gt;NestJS for the backend.&lt;/li&gt;
&lt;li&gt;React for the frontend.&lt;/li&gt;
&lt;li&gt;Tasks are saved in a Postgres DB.&lt;/li&gt;
&lt;li&gt;Redis is used for caching responses.&lt;/li&gt;
&lt;li&gt;API endpoints are documented using OpenAPI.&lt;/li&gt;
&lt;li&gt;API endpoints' parameters are validated in the backend.&lt;/li&gt;
&lt;li&gt;Frontend code related to the API endpoints is auto-generated.&lt;/li&gt;
&lt;li&gt;The development environment is set up in docker.&lt;/li&gt;
&lt;li&gt;Monorepo containing both the backend and the frontend.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Building the project
&lt;/h2&gt;

&lt;p&gt;The source code for this part of the project is available here: &lt;a href="https://github.com/arnaud-cortisse/trying-out-nestjs-part-1"&gt;https://github.com/arnaud-cortisse/trying-out-nestjs-part-1&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up Docker dev environment
&lt;/h3&gt;

&lt;p&gt;Since I took &lt;a href="https://www.udemy.com/course/docker-and-kubernetes-the-complete-guide/"&gt;Docker and Kubernetes: The Complete Guide&lt;/a&gt; and &lt;a href="https://www.udemy.com/course/microservices-with-node-js-and-react/"&gt;Microservices with Node JS and React&lt;/a&gt; courses, I've been a huge fan of setting up my dev environment inside docker instances instead of setting it up directly on my machine. I love the fact that I can have everything up and running with a single command, without having to worry about dependency conflicts (is my current version of NPM compatible with that project?, etc.).&lt;/p&gt;

&lt;h4&gt;
  
  
  A few commands to execute
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Install the Nest CLI: &lt;code&gt;npm i -g @nestjs/cli&lt;/code&gt; (you might need to prefix it with &lt;code&gt;sudo&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Create an empty folder: &lt;code&gt;mkdir todo-list-app&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Go inside the folder: &lt;code&gt;cd todo-list-app&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Init npm package: &lt;code&gt;npm init -y&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Init git, if you want to save your work: &lt;code&gt;git init&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create frontend folder: &lt;code&gt;mkdir -p packages/react-app&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create backend folder: &lt;code&gt;mkdir -p packages/nestjs&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create the React app: &lt;code&gt;npx create-react-app packages/react-app --template typescript&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create the NestJS app: &lt;code&gt;nest new packages/nestjs&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Delete the .git folder automatically created by NestJS: &lt;code&gt;rm -rf packages/nestjs/.git&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create frontend env variables file: &lt;code&gt;touch packages/react-app/.env.dev&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create backend env variables file: &lt;code&gt;touch packages/nestjs/.env.dev&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create frontend Dockerfile for dev environment: &lt;code&gt;touch packages/react-app/Dockerfile.dev&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create backend Dockerfile for dev environment: &lt;code&gt;touch packages/nestjs/Dockerfile.dev&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create docker-compose file for dev environment: &lt;code&gt;touch docker-compose.yml&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create frontend .dockerignore file: &lt;code&gt;touch packages/react-app/.dockerignore&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create backend .dockerignore file: &lt;code&gt;touch packages/nestjs/.dockerignore&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  A few files to fill / change
&lt;/h4&gt;

&lt;h5&gt;
  
  
  packages/react-app/Dockerfile.dev
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM node:alpine
WORKDIR /app

COPY package.json .
RUN npm install --legacy-peer-deps
COPY . .

CMD ["npm", "run", "start"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--legacy-peer-deps&lt;/code&gt; is just a temporary fix for &lt;a href="https://github.com/facebook/create-react-app/issues/9515"&gt;https://github.com/facebook/create-react-app/issues/9515&lt;/a&gt;.&lt;/p&gt;

&lt;h5&gt;
  
  
  packages/nestjs/Dockerfile.dev
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM node:alpine

WORKDIR /app
RUN npm install -g @nestjs/cli
COPY package.json .
RUN npm install
COPY . .

CMD ["npm", "run", "start:dev"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing crazy here, but we just make sure we install the NestJS CLI globally before doing anything else.&lt;/p&gt;

&lt;h5&gt;
  
  
  packages/react-app/.env.dev
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REACT_APP_BACKEND_SCHEMA=http
REACT_APP_BACKEND_HOSTNAME=localhost
REACT_APP_BACKEND_PORT=3001
CHOKIDAR_USEPOLLING=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;CHOKIDAR_USEPOLLING&lt;/code&gt; is required when developing inside a docker container and using create-react-app (&lt;a href="https://github.com/facebook/create-react-app/issues/1049#issuecomment-261731734"&gt;https://github.com/facebook/create-react-app/issues/1049#issuecomment-261731734&lt;/a&gt;).&lt;br&gt;
The other variables are defined so that we can communicate with the NestJS API.&lt;/p&gt;

&lt;h5&gt;
  
  
  packages/nestjs/.env.dev
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NEST_PORT=3001
PGHOST=postgres
PGPORT=5432
PGUSER=postgres
PGPASSWORD=example
PGDATABASE=postgres
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=password
REDIS_TTL=10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We define the port on which NestJS will run as well as the Postgres and Redis configs.&lt;/p&gt;

&lt;h5&gt;
  
  
  packages/react-app/.dockerignore
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node_modules
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We don't want the local &lt;code&gt;node_modules&lt;/code&gt; folder to be copied over the instance.&lt;/p&gt;

&lt;h5&gt;
  
  
  packages/nestjs/.dockerignore
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node_modules
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  docker-compose.yml
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: "3.5"

services:
  nestjs:
    build:
      context: ./packages/nestjs
      dockerfile: Dockerfile.dev
    env_file:
     - ./packages/nestjs/.env.dev
    ports:
      - 3001:3001
    volumes:
      - ./packages/nestjs/:/app
      - /app/node_modules

  react_app:
    build:
      context: ./packages/react-app
      dockerfile: Dockerfile.dev
    env_file:
     - ./packages/react-app/.env.dev
    ports:
      - 3000:3000
    volumes:
      - ./packages/react-app/:/app
      - /app/node_modules

  postgres:
    image: postgres:13.1
    environment:
      POSTGRES_PASSWORD: example
    ports:
     - 5432:5432

  redis:
    image: redis:6.2-rc1
    environment:
      REDIS_PASSWORD: password

  redis_commander:
    image: rediscommander/redis-commander:latest
    restart: always
    environment:
      - REDIS_HOSTS=local:redis:6379
    ports:
      - 8081:8081
    depends_on:
      - redis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;nestjs&lt;/code&gt; is our backend.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;react_app&lt;/code&gt; is our frontend.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;postgres&lt;/code&gt; is going to be used to store the tasks.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;redis&lt;/code&gt; is going to be used by NestJS to cache responses.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;redis_commander&lt;/code&gt; is just a tool that allows us to examine the Redis DB quickly.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;volumes&lt;/code&gt; under react_app and nestjs is key to get auto-reload whenever you modify files inside your editor. The only annoying thing with this setup is that you'll need to rebuild your docker images whenever you add a new dependency inside your node_modules (see &lt;a href="https://github.com/BretFisher/node-docker-good-defaults"&gt;https://github.com/BretFisher/node-docker-good-defaults&lt;/a&gt; for fixes). &lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  packages/nestjs/src/main.ts
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(process.env.NEST_PORT);
}
bootstrap();

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

&lt;/div&gt;



&lt;p&gt;Modify the port the app is listening to using the process.env.NEST_POST environment variable (defined in packages/nestjs/.env.dev).&lt;/p&gt;

&lt;h3&gt;
  
  
  Test current setup
&lt;/h3&gt;

&lt;p&gt;You should now be able to start your dev environment typing &lt;code&gt;docker-compose up&lt;/code&gt; in the root directory.&lt;/p&gt;

&lt;p&gt;You can then go to the following addresses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;localhost:3000&lt;/code&gt; --&amp;gt; React app.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;localhost:3001&lt;/code&gt; --&amp;gt; NestJS app.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;localhost:8081&lt;/code&gt; --&amp;gt; Redis Commander (which should be connected to your Redis instance).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Final words
&lt;/h3&gt;

&lt;p&gt;With the current state, I've a working dev environment inside dev containers. All I have to do to get started is &lt;code&gt;docker-compose up&lt;/code&gt; (sometimes, I have to do a &lt;code&gt;docker-compose up --build&lt;/code&gt;, depending on whether or not new npm packages have been installed).&lt;br&gt;
Whenever I update any &lt;code&gt;.ts&lt;/code&gt; files in my code editor, the apps are reloaded accordingly, making it a very pleasant dev experience for the task at hand: asserting whether or not NestJS is going to be a good fit for me by developing a POC.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/blog/trying-out-nestjs-part-2/"&gt;Part 2 is available here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>react</category>
      <category>docker</category>
      <category>nestjs</category>
    </item>
    <item>
      <title>My Journey Creating My First Solo Project (part 3): Cloud Architecture</title>
      <dc:creator>Arnaud Cortisse</dc:creator>
      <pubDate>Sat, 29 Aug 2020 14:22:56 +0000</pubDate>
      <link>https://dev.to/arnaudcortisse/my-journey-creating-my-first-solo-project-part-3-cloud-architecture-4fil</link>
      <guid>https://dev.to/arnaudcortisse/my-journey-creating-my-first-solo-project-part-3-cloud-architecture-4fil</guid>
      <description>&lt;p&gt;&lt;em&gt;This article was originally published on &lt;a href="https://arnaudcortisse.com/blog"&gt;my personal blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In my &lt;a href="https://dev.to/arnaudcortisse/my-journey-creating-my-first-solo-project-part-2-technologies-and-tools-3ecm"&gt;previous blog post&lt;/a&gt;, I talked about the technologies and tools I used to create &lt;a href="https://donotskip.com/en/"&gt;DoNotSkip&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this blog post, I'd like to describe the overall cloud architecture of &lt;a href="https://donotskip.com/en/"&gt;DoNotSkip&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cloud Architecture
&lt;/h2&gt;

&lt;p&gt;Below, the big picture of the cloud architecture.&lt;br&gt;
Many things are not represented in the schema, but it gives you an idea.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZOjjW3oW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/iyha34gl4yet5svjwjpi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZOjjW3oW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/iyha34gl4yet5svjwjpi.png" alt="DoNotSkip Architecture" width="880" height="755"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://firebase.google.com/"&gt;Firebase&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://firebase.google.com/docs/hosting"&gt;Hosting&lt;/a&gt;: Serves static files such as HTML files, CSS files, JavaScript files, images, etc. with a global CDN.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://firebase.google.com/docs/storage"&gt;Storage&lt;/a&gt;: Stores coaches' assets such as images. These assets are only available to those uploading them.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://firebase.google.com/docs/auth"&gt;Authentication&lt;/a&gt;: Allows authentication via identity providers such as Google, Apple, etc.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://firebase.google.com/docs/functions"&gt;Cloud Functions&lt;/a&gt;: Hosts the backend logic. Examples of what is done in those functions: crop and resize images, transfer images from one bucket to another, etc.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.heroku.com/"&gt;Heroku&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://hasura.io/"&gt;Hasura&lt;/a&gt;: GraphQL API over postgreSQL, event system (enabling the [3factor app] architecture), etc.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.postgresql.org/"&gt;PostgreSQL&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://cloud.google.com/"&gt;Google Cloud&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://cloud.google.com/storage"&gt;Storage&lt;/a&gt;: Host workout programs' assets for the public.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://cloud.google.com/appengine"&gt;App Engine&lt;/a&gt;: Host &lt;a href="https://nextjs.org/"&gt;Next.js app&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In reality, things are a bit more complex than they are in the schema:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There are several instances of Hasura and postgreSQL.&lt;/li&gt;
&lt;li&gt;There are two different Firebase projects: one for the coaches and one for clients.&lt;/li&gt;
&lt;li&gt;Everything is duplicated between staging and production environments.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A few examples
&lt;/h2&gt;

&lt;p&gt;To give you an idea of how everything is put together, I want to show some practical examples of how every piece communicates with one another in some user use cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  What happens when logging in?
&lt;/h3&gt;

&lt;p&gt;Below is an overview of how the login process takes place. This logic is the same across the different parts of the platform.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--o8XDEyJn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/61nikiga0u1h91zdbf7v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--o8XDEyJn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/61nikiga0u1h91zdbf7v.png" alt="Login Workflow" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Using the &lt;a href="https://firebase.google.com/"&gt;Firebase SDK&lt;/a&gt;, sends a request to the &lt;strong&gt;Google Authentication service&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;In the client, an observer is listening to all Firebase auth state changes. When the previous request succeeds, the observer gets information about the user such as their unique &lt;strong&gt;User ID&lt;/strong&gt; and a &lt;strong&gt;JWT token&lt;/strong&gt;. The &lt;em&gt;User ID&lt;/em&gt; allows to uniquely identify in the whole platform while the &lt;em&gt;JWT token&lt;/em&gt; is used to confirm the identity of the user making requests.&lt;/li&gt;
&lt;li&gt;When the user is identified successfully, the client sends a request to the backend (in this case, &lt;a href="https://hasura.io/"&gt;Hasura&lt;/a&gt;) to notify that the user has actually logged in.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Hasura GraphQL Engine&lt;/strong&gt; automatically checks the validity of the JWT token.&lt;/li&gt;
&lt;li&gt;Communicates with the &lt;em&gt;Authentication Service&lt;/em&gt; if JWK has expired to get a new one.&lt;/li&gt;
&lt;li&gt;When the JWT token is valid, the database is updated.&lt;/li&gt;
&lt;li&gt;The backend replies with a successful response.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  How are coaches' assets uploaded?
&lt;/h3&gt;

&lt;p&gt;Below is an overview of how images are uploaded in &lt;a href="https://app.coaches.donotskip.com/"&gt;DoNotSkip Coaches&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WjkyRwaU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/wok5r3a3imjalx6oqicr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WjkyRwaU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/wok5r3a3imjalx6oqicr.png" alt="Asset Upload Workflow" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Using the &lt;a href="https://firebase.google.com/"&gt;Firebase SDK&lt;/a&gt;, an upload task is started client-side.&lt;/li&gt;
&lt;li&gt;The file is uploaded successfully, the client receives a successful response.&lt;/li&gt;
&lt;li&gt;Metadata about the file is added to the SQL database via Hasura. The JWT token is sent automatically with every request.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Hasura GraphQL Engine&lt;/strong&gt; automatically checks the validity of the JWT token.&lt;/li&gt;
&lt;li&gt;If the JWK has expired, get a new one.&lt;/li&gt;
&lt;li&gt;The token is valid, Hasura proceeds with the update.&lt;/li&gt;
&lt;li&gt;Sends a successful response to the client.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  How are workout programs published?
&lt;/h3&gt;

&lt;p&gt;Below is an overview of how program publications work inside &lt;a href="https://app.coaches.donotskip.com/"&gt;DoNotSkip Coaches&lt;/a&gt;. The architecture used here is called &lt;a href="https://3factor.app/"&gt;3factor app&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Br_jYeRn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/07m2o45cxzfazg33nru6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Br_jYeRn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/07m2o45cxzfazg33nru6.png" alt="Program Publication Workflow" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A &lt;em&gt;GraphQL mutation&lt;/em&gt; is sent to Hasura. This GraphQL mutation inserts a row in a table called &lt;em&gt;Publish Program&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Hasura GraphQL Engine&lt;/strong&gt; automatically checks the validity of the JWT token.&lt;/li&gt;
&lt;li&gt;If the JWK has expired, get a new one.&lt;/li&gt;
&lt;li&gt;The token is valid and a row is added to &lt;em&gt;Publish Program&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;A response is sent to the client saying that the row has been added successfully.&lt;/li&gt;
&lt;li&gt;Because the response is successful, the client creates a &lt;em&gt;GraphQL subscription&lt;/em&gt; to get real-time updates on the publication of the program (using WebSocket).&lt;/li&gt;
&lt;li&gt;An &lt;em&gt;event trigger&lt;/em&gt; is set up in the database so that whenever a row is added in &lt;em&gt;Publish Program&lt;/em&gt;, a request is sent to a custom webhook (in this case, it's a cloud function hosted on Firebase).&lt;/li&gt;
&lt;li&gt;The cloud function does everything it needs to do in order to make the program available publicly (compresses images, sanitizes text, etc.). Once everything is done, it updates the SQL database to notify that the program has been successfully uploaded. The client is in-turn notified.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The publication process itself is explained in more details below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ag38dGbr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/sh1pfezrj1xfzx6jtno7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ag38dGbr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/sh1pfezrj1xfzx6jtno7.png" alt="Program Publication Backend" width="880" height="1226"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The webhook "Publish Program" is called. A cloud function is started. This function has two purposes: &lt;strong&gt;1)&lt;/strong&gt; the creation of the landing page for the program and &lt;strong&gt;2)&lt;/strong&gt; the creation of the program that's going to be used in the app.&lt;/li&gt;
&lt;li&gt;All information regarding the program to publish is retrieved in Hasura.&lt;/li&gt;
&lt;li&gt;Various stuff is done in the function:

&lt;ol&gt;
&lt;li&gt;Images are cropped and optimized (if required).&lt;/li&gt;
&lt;li&gt;Pieces of text are sanitized.&lt;/li&gt;
&lt;li&gt;Images are moved in a public bucket.&lt;/li&gt;
&lt;li&gt;Various JSON documents are created.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;li&gt;The JSON documents are published to an Hasura instance hosting the published programs.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Final words
&lt;/h2&gt;

&lt;p&gt;As you can see, &lt;a href="https://hasura.io/"&gt;Hasura&lt;/a&gt; plays a crucial role in this architecture (&lt;a href="https://3factor.app/"&gt;3factor app&lt;/a&gt;): most of the time, whenever the frontend app wants to perform a backend action that requires authorization, it does so by inserting a new row in the SQL database via the GraphQL API and then subscribes to some database row changes. The cloud functions are invoked behind the scene via a clever event system provided by Hasura.&lt;/p&gt;

&lt;p&gt;This cloud architecture would be able to accept a high volume of traffic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It's going to be a long time before Hasura becomes a bottleneck (see &lt;a href="https://github.com/hasura/graphql-engine/blob/master/architecture/live-queries.md"&gt;Scaling to 1 million active GraphQL subscriptions with Hasura&lt;/a&gt;). Furthermore, I have set up three different Hasura instances that can scale vertically independently of each other (one for &lt;strong&gt;Coaches&lt;/strong&gt;, one for &lt;strong&gt;Publications&lt;/strong&gt; and another one for &lt;strong&gt;User savings&lt;/strong&gt;). It wouldn't be difficult to put a load-balancer in front of the Hasura instances in order to scale horizontally.&lt;/li&gt;
&lt;li&gt;Cloud functions automatically scale.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I could go on and on and explain other use cases, but I think you get the gist of &lt;a href="https://donotskip.com/en/"&gt;DoNotSkip&lt;/a&gt;'s cloud architecture.&lt;br&gt;
In the next blog post, I'd like to talk about the code architecture of &lt;a href="https://donotskip.com/en/mobile-app"&gt;DoNotSkip App&lt;/a&gt;, the mobile app of the platform built with &lt;a href="https://reactnative.dev/"&gt;React Native&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>project</category>
      <category>hasura</category>
      <category>firebase</category>
    </item>
    <item>
      <title>My Journey Creating My First Solo Project (part 2): Technologies and tools</title>
      <dc:creator>Arnaud Cortisse</dc:creator>
      <pubDate>Mon, 17 Aug 2020 15:10:38 +0000</pubDate>
      <link>https://dev.to/arnaudcortisse/my-journey-creating-my-first-solo-project-part-2-technologies-and-tools-3ecm</link>
      <guid>https://dev.to/arnaudcortisse/my-journey-creating-my-first-solo-project-part-2-technologies-and-tools-3ecm</guid>
      <description>&lt;p&gt;&lt;em&gt;This article was originally published on &lt;a href="https://arnaudcortisse.com/blog"&gt;my personal blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In my &lt;a href="https://dev.to/arnaudcortisse/my-journey-creating-my-first-solo-project-part-1-23ik"&gt;previous blog post&lt;/a&gt;, I described the motivations behind building &lt;a href="https://donotskip.com/en/"&gt;DoNotSkip&lt;/a&gt;, what problems it would try to solve and the big picture of how I would go about building it.&lt;/p&gt;

&lt;p&gt;In this blog post, I'd like to describe what are the main technologies / tools I used to create it and why I chose them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Main parts
&lt;/h2&gt;

&lt;p&gt;As described in my &lt;a href="https://dev.to/arnaudcortisse/my-journey-creating-my-first-solo-project-part-1-23ik"&gt;previous blog post&lt;/a&gt;, the project consists of 3 main parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;platform for coaches&lt;/strong&gt; to create and publish workout programs: a single-page application.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;platform for the workout programs' landing pages&lt;/strong&gt;: a server-side rendered application.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A mobile app to use the workout programs&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Technologies / tools
&lt;/h2&gt;

&lt;p&gt;I'm going to list the most important libraries and tools I used in this project. Just so you know, I didn't have to pay anything for the entire development period (and I think that's pretty amazing).&lt;/p&gt;

&lt;h3&gt;
  
  
  Common
&lt;/h3&gt;

&lt;p&gt;Most of the technologies and tools are common to the three main parts of the platform, and that's one of the reasons JavaScript is so awesome!&lt;/p&gt;

&lt;p&gt;The community is very strong and a lot of empowering tools have been built for you.&lt;/p&gt;

&lt;p&gt;Even though I had to build 3 quite different products (a SPA, a SSR app and mobile app), I was able to leverage stuff from each app and apply it to the others. No need to relearn everything from scratch as soon as the requirements change!&lt;/p&gt;

&lt;h4&gt;
  
  
  Front-end
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.typescriptlang.org/"&gt;Typescript&lt;/a&gt;&lt;/strong&gt; as the programming language. I actually got started with JavaScript, but I soon realized it didn't scale well (more on that in another blog post). I do not regret migrating to Typescript whatsoever.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://reactjs.org/"&gt;React&lt;/a&gt;&lt;/strong&gt; for the UI. I could have chosen Vue or Angular, but React was the only modern web UI framework I'd used so far.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://redux.js.org/"&gt;Redux&lt;/a&gt;&lt;/strong&gt; for local state management. In my opinion (and I know it's a moot point), Redux is the go-to state management library when developing complex React apps. In retrospect, I shouldn't have used it in DoNotSkip Programs (because the state isn't complex in that app). Otherwise, I think it's an awesome library that scales very well! If I was to start a new React project with Redux, I would definitely have a look at &lt;a href="https://redux-toolkit.js.org/"&gt;Redux Toolkit&lt;/a&gt;, which seems to reduce the amount of boilerplate code to write.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/reduxjs/reselect"&gt;Reselect&lt;/a&gt;&lt;/strong&gt; to access the Redux state. Not only can you centralize and test the way you access your state, but you can also compute derived state and memoize the result.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/agiledigital/typed-redux-saga"&gt;Redux Typed Saga&lt;/a&gt;&lt;/strong&gt; for side-effects. It's just a library providing Typescript over &lt;a href="https://redux-saga.js.org/"&gt;Redux Saga&lt;/a&gt;. I basically had to choose between Redux Saga and Redux Thunk. Redux Saga documentation looked very clear and I liked how the code read (I find it way easier to read asynchronous code than nested callbacks).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://react.i18next.com/"&gt;react-18next&lt;/a&gt;&lt;/strong&gt; for internationalization. I actually don't remember why I chose it over &lt;a href="https://formatjs.io/"&gt;react-intl&lt;/a&gt; but all I can say is it's very easy to use.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.apollographql.com/docs/react/"&gt;Apollo Client&lt;/a&gt;&lt;/strong&gt; to perform client-side GraphQL requests. There exists plenty of other libraries, but this is one of the most famous. Furthermore, it supports &lt;a href="https://www.apollographql.com/docs/react/data/subscriptions/"&gt;subscriptions&lt;/a&gt; which I knew I would need. I just think I haven't really used it properly because I don't leverage its built-in cache system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://immerjs.github.io/immer/docs/introduction"&gt;Immer&lt;/a&gt;&lt;/strong&gt; makes updating states in Redux reducers way easier than in plain JavaScript. I started out with plain JavaScript and felt the code of my reducers was becoming too difficult to read.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Back-end
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.typescriptlang.org/"&gt;Typescript&lt;/a&gt;&lt;/strong&gt; as the programming language. Like I said before, I started out with JavaScript but didn't like where my codebase was going.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://nodejs.org/en/"&gt;Node.JS&lt;/a&gt;&lt;/strong&gt; as the backend framework. Very popular backend framework in the web development world since the programming language is JavaScript. &lt;a href="https://deno.land/"&gt;Deno&lt;/a&gt; might be its replacement, but not anytime soon.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://firebase.google.com/"&gt;Firebase&lt;/a&gt;&lt;/strong&gt; for:

&lt;ul&gt;
&lt;li&gt;Authentication: you get OAuth authentication &lt;strong&gt;for free&lt;/strong&gt; (with all major sign-in providers).&lt;/li&gt;
&lt;li&gt;Serverless functions: to me, serverless is the future of backend. Why would I even bother managing my own servers when I can have them managed by experts? You only pay when the functions are up (and they go back to sleep after a while) and they scale automatically as demand grows. Of course, they have drawbacks (such as cold-starts), but the advantages far outweighed them in my case.&lt;/li&gt;
&lt;li&gt;Storage: I didn't really compare it with anything else, since I was already all in on Firebase.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://cloud.google.com/"&gt;Google Cloud&lt;/a&gt;&lt;/strong&gt; is the cloud provider I chose. First of all, it's what Firebase uses behind the scene. Secondly, they offer a generous 300€ credit (only for the first year). Finally, after having tested Azure and AWS, I found it way easier to use.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://hasura.io/"&gt;Hasura&lt;/a&gt;&lt;/strong&gt; here is the official description of the product: &lt;em&gt;"Hasura GraphQL Engine is an opensource product that connects to your databases &amp;amp; services and gives you a realtime GraphQL API, instantly."&lt;/em&gt; I won't go into the details in this blog post but what I can already tell you is that it's really awesome. You basically have fine-grained access to your database via an automatically generated GraphQL API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.postgresql.org/"&gt;PostgreSQL&lt;/a&gt;&lt;/strong&gt; for the database. I didn't really choose this one, since this is the only database compatible with Hasura.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.heroku.com/home"&gt;Heroku&lt;/a&gt;&lt;/strong&gt; to host Hasura engines and PostgreSQL databases. Their free tier is also very generous and you can get up and running in a few minutes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Miscellaneous
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/axios/axios"&gt;Axios&lt;/a&gt;&lt;/strong&gt; as the HTTP clients for both the frontend and the backend. That way, I can use the same syntax everywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/jquense/yup"&gt;Yup&lt;/a&gt;&lt;/strong&gt; to parse and validate JavaScript objects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://jestjs.io/"&gt;Jest&lt;/a&gt;&lt;/strong&gt; for unit-testing. I picked up this framework because it was the only one I was familiar with in the JavaScript ecosystem.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/"&gt;Github&lt;/a&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://lerna.js.org/"&gt;Lerna&lt;/a&gt;&lt;/strong&gt; makes dealing with &lt;a href="https://en.wikipedia.org/wiki/Monorepo"&gt;monorepos&lt;/a&gt; easier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/typicode/husky"&gt;Husky&lt;/a&gt;&lt;/strong&gt; to launch linting + testing automatically whenever I commit changes to Github.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://graphql-code-generator.com/"&gt;GraphQL Code Generator&lt;/a&gt;&lt;/strong&gt; to automatically generate Typescript types for my GraphQL requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://marketingplatform.google.com/about/analytics/"&gt;Google Analytics&lt;/a&gt;&lt;/strong&gt; for analytics. The free version of Google Analytics provides more than enough insight.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://sentry.io/welcome/"&gt;Sentry&lt;/a&gt;&lt;/strong&gt; for application monitoring and error tracking. I had heard it advertised many times in some of the podcasts I listen to and decided to give it a go.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/features/actions"&gt;Github Actions&lt;/a&gt;&lt;/strong&gt; for CI/CD. Pretty easy to use, integrated with Github and generous free tier.
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Project-specific
&lt;/h3&gt;

&lt;p&gt;I needed to use the right tool for the right job. The 3 projects are each of different nature.&lt;/p&gt;

&lt;h4&gt;
  
  
  DoNotSkip Coaches
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://create-react-app.dev/"&gt;Create React App&lt;/a&gt;&lt;/strong&gt; to set up the project. You get everything configured and you are good to go in no time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://material-ui.com/"&gt;Material-UI&lt;/a&gt;&lt;/strong&gt; for the UI components and design system. I've always loved the simplicity of Google products and since I'm not good at designing, I thought it would be a wise decision to have strict guidelines and predefined components.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  DoNotSkip Programs
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://nextjs.org/"&gt;Next.JS&lt;/a&gt;&lt;/strong&gt; to render server-side pages. It's the go-to framework when it comes to building SSR apps with React. It's also the framework I had the most difficulty getting started with because I didn't immediatly understand the core concepts. A few major releases came out since the first time I used it that have made it easier to get started with.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://cloud.google.com/appengine"&gt;App Engine&lt;/a&gt;&lt;/strong&gt; to host the &lt;a href="https://nextjs.org/"&gt;Next.JS&lt;/a&gt; app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://material-ui.com/"&gt;Material-UI&lt;/a&gt;&lt;/strong&gt; for the UI components and design system.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  DoNotSkip App
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://reactnative.dev/"&gt;React Native&lt;/a&gt;&lt;/strong&gt; to build hybrid apps. It was a no-brainer since I already had used React so much.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://expo.io/"&gt;Expo&lt;/a&gt;&lt;/strong&gt; to have even more tools and services around React Native.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://nativebase.io/"&gt;NativeBase&lt;/a&gt;&lt;/strong&gt; for the UI components and design system.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  DoNotSkip Landing Page
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.gatsbyjs.com/"&gt;Gatsby&lt;/a&gt;&lt;/strong&gt; to create static pages with React.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://material-ui.com/"&gt;Material-UI&lt;/a&gt;&lt;/strong&gt; for the UI components and design system.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;As you can see, I've used a lot of third-party libraries in order to build &lt;a href="https://donotskip.com/en/"&gt;DoNotSkip&lt;/a&gt; (and I didn't mention all of them...). It's pretty common to do that in the JavaScript ecosystem instead of reinventing the wheel. When picking a library, I just made sure it was still maintained.&lt;/p&gt;

&lt;p&gt;Now that you know the tech stack I used, I'm going to describe the cloud architecture of &lt;a href="https://donotskip.com/en/"&gt;DoNotSkip&lt;/a&gt; in the &lt;a href="https://dev.to/arnaudcortisse/my-journey-creating-my-first-solo-project-part-3-cloud-architecture-4fil"&gt;next blog post&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
    </item>
    <item>
      <title>My Journey Creating My First Solo Project (part 1): Defining the project</title>
      <dc:creator>Arnaud Cortisse</dc:creator>
      <pubDate>Mon, 10 Aug 2020 14:30:20 +0000</pubDate>
      <link>https://dev.to/arnaudcortisse/my-journey-creating-my-first-solo-project-part-1-23ik</link>
      <guid>https://dev.to/arnaudcortisse/my-journey-creating-my-first-solo-project-part-1-23ik</guid>
      <description>&lt;p&gt;&lt;em&gt;This article was originally published on &lt;a href="https://arnaudcortisse.com/blog"&gt;my personal blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In my &lt;a href="https://dev.to/arnaudcortisse/my-journey-learning-web-development-2k51"&gt;previous blog post&lt;/a&gt;, I talked about how I got into web development and that at some point, I felt that I needed to create a project on my own in order to consolidate all my newly-acquired knowledge.&lt;/p&gt;

&lt;p&gt;In this article, I'm going to talk about &lt;a href="https://donotskip.com/en/"&gt;DoNotSkip&lt;/a&gt;, the project I decided to create.&lt;/p&gt;

&lt;h2&gt;
  
  
  Coming up with an idea
&lt;/h2&gt;

&lt;p&gt;I wanted to find a project idea meeting the following requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;make &lt;strong&gt;use&lt;/strong&gt; of my &lt;strong&gt;previously acquired knowledge&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;is &lt;strong&gt;complex enough&lt;/strong&gt; that I still need to &lt;strong&gt;learn new stuff&lt;/strong&gt; in the process,&lt;/li&gt;
&lt;li&gt;is &lt;strong&gt;unique&lt;/strong&gt; and &lt;strong&gt;solve a real world "problem"&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;can potentially &lt;strong&gt;make a little money&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If all of these requirements were met, I knew the project would keep me interested and motivated, which meant I would go through to the end of it, whatever it takes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The project idea
&lt;/h2&gt;

&lt;p&gt;It's a fair statement to say that it's easier to solve problems in domains you already know about.&lt;/p&gt;

&lt;p&gt;Therefore, I decided to try and find an idea related to fitness since I've been working out for about 8 years.&lt;br&gt;
When I'm working out, I like to take note of my performance and see whether I'm improving. Maybe I could develop a mobile app that would allow me to record my performance? Let's be honest, that's not original! I wanted to go a bit further...&lt;/p&gt;

&lt;p&gt;And then, I came up with something that would make it more original. You see, I like to watch fitness Youtubers every once in a while. I've noticed that some of them sell or share workout programs in PDF files. PDF files... Really? Maybe that's something I should fix with my project.&lt;/p&gt;

&lt;p&gt;What if I created &lt;strong&gt;a platform that would allow coaches to create their own workout program&lt;/strong&gt; (with their own images, pieces of text, etc.) &lt;strong&gt;and to share it with their community, which in turn would use it in a mobile application&lt;/strong&gt;? I found similar platforms existed, but I couldn't find any that offered the possibility to distribute workout programs at scale (because their pricing model is not thought out that way).&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;The product would consist of three main parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A platform coaches can use to &lt;strong&gt;create highly customizable workout programs&lt;/strong&gt;. That platform must also allow coaches to &lt;strong&gt;publish their workout programs&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;A platform used to &lt;strong&gt;host the workout program landing pages&lt;/strong&gt;. When coaches are done with creating their workout program, they need to be able to publish it and share it with their community.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An app&lt;/strong&gt; members of the communities can use with their coach's workout program.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Initially, I wanted coaches to be able to &lt;strong&gt;sell&lt;/strong&gt; their programs on the platform, but I decided against it in the end (I'll explain why in another blog post).&lt;/p&gt;

&lt;h2&gt;
  
  
  Roadmap
&lt;/h2&gt;

&lt;p&gt;My first roadmap was pretty straightforward:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get an idea of which technologies I would use for each part of the product.&lt;/li&gt;
&lt;li&gt;Create a prototype of:

&lt;ol&gt;
&lt;li&gt;the coach platform.&lt;/li&gt;
&lt;li&gt;the user app.&lt;/li&gt;
&lt;li&gt;the platform hosting the workout landing pages.&lt;/li&gt;
&lt;li&gt;the landing page of the project.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I couldn't plan too much in advance at that point because there were too many unknowns.&lt;/p&gt;

&lt;h2&gt;
  
  
  Picking up technologies
&lt;/h2&gt;

&lt;p&gt;It was time for me to choose which technologies I would use in each part of &lt;a href="https://donotskip.com/en/"&gt;DoNotSkip&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/arnaudcortisse/my-journey-creating-my-first-solo-project-part-2-technologies-and-tools-3ecm"&gt;In the next blog post&lt;/a&gt;, I'll tell you how I picked them and what my thought process was.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
    </item>
    <item>
      <title>My Journey Learning Web Development</title>
      <dc:creator>Arnaud Cortisse</dc:creator>
      <pubDate>Thu, 06 Aug 2020 05:14:05 +0000</pubDate>
      <link>https://dev.to/arnaudcortisse/my-journey-learning-web-development-2k51</link>
      <guid>https://dev.to/arnaudcortisse/my-journey-learning-web-development-2k51</guid>
      <description>&lt;p&gt;&lt;em&gt;This article was originally published on &lt;a href="https://arnaudcortisse.com/blog"&gt;my personal blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;My name is Arnaud Cortisse and I've been a developer for 5 years. The first 4 years of my professional career were mostly spent using low-level languages. At present, I'm exclusively using web technologies.&lt;/p&gt;

&lt;p&gt;In this article, I want to explain my journey getting into web development: Why I got into it, how I went about learning it, the resources I used, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  What got me into web development
&lt;/h2&gt;

&lt;p&gt;In February 2019, I decided to resign from the company I was working for to go work for a startup.&lt;/p&gt;

&lt;p&gt;Only about 6 months after I joined, me and half of my coworkers got laid off because they decided to cut costs.&lt;/p&gt;

&lt;p&gt;I saw there an opportunity to learn stuff I'd been eager to learn for a long time, that is, web development. Why web development? I'm a huge podcast listener and a big part of what I listen to includes web technologies. Being exposed to those talks over and over made me want to dig more into that topic.&lt;/p&gt;

&lt;p&gt;I decided that I would learn it full-time, before taking another job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learning web development
&lt;/h2&gt;

&lt;p&gt;At that time, even though I had been a professional developer for several years, I hadn't done any serious web-related projects. I knew I would basically have to start from the beginning but that I would be able to leverage my previous work experiences and move (way) faster than somebody who has never been exposed to programming before.&lt;/p&gt;

&lt;h3&gt;
  
  
  The methodology
&lt;/h3&gt;

&lt;p&gt;From my own experience, I know what works best for me when it comes to learning new stuff is watching video courses while coding along.&lt;br&gt;
Back then, I had already used &lt;a href="https://www.udemy.com/"&gt;Udemy&lt;/a&gt; a couple of times and I had enjoyed it, so I decided to stick with it.&lt;/p&gt;

&lt;p&gt;What I also do when I feel like one concept is poorly explained in a course is search for other resources and delve deeper on my own (e.g. &lt;a href="https://medium.com/"&gt;Medium&lt;/a&gt;, &lt;a href="https://dev.to/"&gt;dev.to&lt;/a&gt;, &lt;a href="https://www.youtube.com/"&gt;Youtube&lt;/a&gt;, etc.).&lt;/p&gt;

&lt;h3&gt;
  
  
  The journey
&lt;/h3&gt;

&lt;h4&gt;
  
  
  HTML and CSS (2 weeks)
&lt;/h4&gt;

&lt;p&gt;The first thing I set out to do was learning the web foundations, that is, &lt;strong&gt;HTML&lt;/strong&gt; and &lt;strong&gt;CSS&lt;/strong&gt;. To do so, I took "&lt;a href="https://www.udemy.com/course/modern-html-css-from-the-beginning/"&gt;Modern HTML &amp;amp; CSS From The Beginning&lt;/a&gt;". From what I remember, this course was OK.&lt;/p&gt;

&lt;h4&gt;
  
  
  Javascript (2 weeks)
&lt;/h4&gt;

&lt;p&gt;Once I was done with it, I immediately started learning about &lt;strong&gt;JavaScript&lt;/strong&gt;. Once again, I had to choose a resource that would guide me and decided to go with "&lt;a href="https://www.udemy.com/course/modern-javascript-from-the-beginning/learn/lecture/8762504#overview"&gt;Modern JavaScript From The Beginning&lt;/a&gt;". I don't recommend this one because the instructor makes plenty of mistakes and I remember feeling quite frustrated at some point (which is why I only went through 60% of it). Still, I had gained some insights into JavaScript, especially in a browser context and I felt like it was enough for now.&lt;/p&gt;

&lt;h4&gt;
  
  
  General knowledge (4 weeks)
&lt;/h4&gt;

&lt;p&gt;Now that I had the basics of web development, I wanted to expand my &lt;strong&gt;general knowledge&lt;/strong&gt; of the web world. It just so happens that "&lt;a href="https://www.udemy.com/course/the-complete-web-developer-zero-to-mastery/"&gt;The Complete Web Developer in 2020&lt;/a&gt;" is designed to give you an overview of web development. Granted, this course contains some stuff I already knew about, but repetition is good!&lt;/p&gt;

&lt;p&gt;After I finished this course, that wasn't enough. I still wanted to have more insights into web development.&lt;br&gt;
I bought yet another course: &lt;a href="https://www.udemy.com/course/the-complete-junior-to-senior-web-developer-roadmap/"&gt;The Complete Junior to Senior Web Developer Roadmap&lt;/a&gt;. It is kind of the follow-up of the previous one.&lt;/p&gt;

&lt;p&gt;Overall these two courses were, in my opinion, really great.&lt;/p&gt;

&lt;h4&gt;
  
  
  Front-end framework (4 weeks)
&lt;/h4&gt;

&lt;p&gt;OK... I had the basics covered and knew a bit more as to what it takes to create a full-fledged web application.&lt;br&gt;
But I didn't feel like I was ready to create my own application just yet. I had dabbled in React and Redux in the previous course, but it was clearly not enough to get started and create a project on my own (besides, I didn't know what to develop).&lt;/p&gt;

&lt;p&gt;I decided to take one last course exclusively focusing on the React ecosystem: &lt;a href="https://www.udemy.com/course/complete-react-developer-zero-to-mastery/"&gt;Complete React Developer in 2020&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Why React?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I had already been in touch with it in one of the previous courses I took and I had enjoyed it.&lt;/li&gt;
&lt;li&gt;I did some research and it appeared to be one of the most (if not the most) popular front-end frameworks.&lt;/li&gt;
&lt;li&gt;I would be able to use it for a lot of stuff: web applications, static websites, server-side rendered websites, mobile applications, desktop applications, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This last course gave me a good grounding in React. In hindsight, I do not regret having chosen the React framework.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating my own project
&lt;/h2&gt;

&lt;p&gt;At that point, I was already 3 months into my learning journey. I remember thinking to myself "now, you ought to create something on your own and get out of this tutorial hell". I'd learned enough that I felt like I could create my own project to consolidate all my newly-acquired knowledge. I wanted to create something from scratch.&lt;/p&gt;

&lt;p&gt;And then, I got an idea. In the &lt;a href="https://dev.to/arnaudcortisse/my-journey-creating-my-first-solo-project-part-1-23ik"&gt;next blog post&lt;/a&gt;, I'll give the big picture of my journey developing &lt;a href="https://donotskip.com/en/"&gt;DoNotSkip&lt;/a&gt;, the project I chose to develop.&lt;/p&gt;

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