<?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: Zaki Mohammed</title>
    <description>The latest articles on DEV Community by Zaki Mohammed (@zakimohammed).</description>
    <link>https://dev.to/zakimohammed</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%2F841132%2Fd1502f6b-df5e-40b3-b077-6cdf83e02291.jpg</url>
      <title>DEV Community: Zaki Mohammed</title>
      <link>https://dev.to/zakimohammed</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zakimohammed"/>
    <language>en</language>
    <item>
      <title>ISRO React App — Working on Component — Part 2</title>
      <dc:creator>Zaki Mohammed</dc:creator>
      <pubDate>Thu, 03 Aug 2023 18:37:59 +0000</pubDate>
      <link>https://dev.to/zakimohammed/isro-react-app-working-on-component-part-2-3cb5</link>
      <guid>https://dev.to/zakimohammed/isro-react-app-working-on-component-part-2-3cb5</guid>
      <description>&lt;h3&gt;
  
  
  ISRO React App — Working on Component — Part 2
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ed3XKuYz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/0%2A_j7X5tJa6S_TyG-O.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ed3XKuYz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/0%2A_j7X5tJa6S_TyG-O.jpg" alt="" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The time has brought us to talk about the components of ISRO React App. In this article we will continue to talk about the remnants of the app. How the services been created, what are the components present to show the listing of ISRO API data.&lt;/p&gt;

&lt;p&gt;If you haven’t read the &lt;a href="https://codeomelet.com/posts/isro-react-app-initial-setup-part-1"&gt;previous&lt;/a&gt; article regarding the initial setup of the app, then this is very good time to check it out before beginning with this one. The part 1 has the details about initial setup of ISRO React App you can check it out from here &lt;a href="https://codeomelet.com/posts/isro-react-app-initial-setup-part-1"&gt;ISRO React App — Initial Setup — Part 1&lt;/a&gt;. Let us construct components and services for the amazing &lt;a href="https://github.com/isro/api"&gt;ISRO-API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We will head in this direction:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating Axios Services&lt;/li&gt;
&lt;li&gt;Configuring Routes&lt;/li&gt;
&lt;li&gt;Designing List Component&lt;/li&gt;
&lt;li&gt;Designing Search Component&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Creating Axios Services
&lt;/h3&gt;

&lt;p&gt;Without further ado, let us create services those will make actual calls to the ISRO-API for the data. We will create methods for each of the GET endpoints.&lt;/p&gt;

&lt;p&gt;For this we have created a services folder, inside of it a “core.service.ts” will hold up the Axios configuration code and a generic GET method to get the API response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// services/core.service.ts

import axios from 'axios';
import { urlIsro } from '../constants/core.constant';

const axiosInstance = axios.create({
  baseURL: urlIsro.base,
});

const get = async &amp;lt;T&amp;gt;(url: string): Promise&amp;lt;T&amp;gt; =&amp;gt; {
  const response = await axiosInstance.get(url);
  if (response.status === 200) {
    return response.data as T;
  } else {
    throw new Error(`[${response.status}]: ${response.statusText}`);
  }
};

const service = {
  get,
};

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

&lt;/div&gt;



&lt;p&gt;Here, the axiosInstance configures the baseURL of the ISRO-API. Later, a generic get method takes URL and return supplied typed response. Consider this file to have your wrapper methods for the API calls in future for POST, PUT, DELETE sort of operations.&lt;/p&gt;

&lt;p&gt;On the basis of this we have app.service.ts file that holds up the ISRO-API endpoint calls.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// services/app.service.ts

import { urlIsro, urlRestCountries } from '../constants/core.constant';
import { Country } from '../models/Country';
import { CentreDto, CustomerSatelliteDto, LauncherDto, SpacecraftDto } from '../models/Dto';
import core from './core.service';

const getSpacecrafts = async () =&amp;gt; {
  return await core.get(urlIsro.spacecrafts);
};

const getLaunchers = async () =&amp;gt; {
  return await core.get(urlIsro.launchers);
};

const getCustomerSatellites = async () =&amp;gt; {
  return await core.get(urlIsro.customerSatellites);
};

const getCentres = async () =&amp;gt; {
  return await core.get(urlIsro.centres);
};

const getCountries = async () =&amp;gt; {
  return await core.get(`${urlRestCountries.base}${urlRestCountries.all}`);
};

const service = {
  getSpacecrafts,
  getLaunchers,
  getCustomerSatellites,
  getCentres,
  getCountries,
};

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

&lt;/div&gt;



&lt;p&gt;Here, we are using the core service get method to make the calls based on the endpoints. Note that we have also created a constant file to hold up the app level constants values. We are exposing these method to be consumed by the components.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring Routes
&lt;/h3&gt;

&lt;p&gt;Now, create bunch of routed components, club them into a folder name “pages”.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pages
|-- About.tsx
|-- Centre.tsx
|-- CustomerSatellite.tsx
|-- Home.tsx
|-- Launcher.tsx
|-- Spacecraft.tsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Register these components in the route configuration, which is present in App.tsx file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Routes&amp;gt;
  &amp;lt;Route path='/' element={&amp;lt;Home /&amp;gt;} /&amp;gt;
  &amp;lt;Route path='/spacecrafts' element={&amp;lt;Spacecraft /&amp;gt;} /&amp;gt;
  &amp;lt;Route path='/launchers' element={&amp;lt;Launcher /&amp;gt;} /&amp;gt;
  &amp;lt;Route path='/customer-satellites' element={&amp;lt;CustomerSatellite /&amp;gt;} /&amp;gt;
  &amp;lt;Route path='/centers' element={&amp;lt;Centre /&amp;gt;} /&amp;gt;
  &amp;lt;Route path='/about' element={&amp;lt;About /&amp;gt;} /&amp;gt;
  &amp;lt;Route path='*' element={&amp;lt;Navigate to={'/'} /&amp;gt;} /&amp;gt;
&amp;lt;/Routes&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we are configuring the routes with corresponding components.&lt;/p&gt;

&lt;p&gt;This app is hosted at &lt;a href="https://www.netlify.com/"&gt;Netlify&lt;/a&gt; so for making React Router to work without 404 on direct path hits, we need to add a file named “_redirects” to public folder. The content of this fill will be 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;/* /index.html 200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Designing List Component
&lt;/h3&gt;

&lt;p&gt;The components Spacecraft, Launcher, CustomerSatellite and Centre does more or less the same thing with only UI differences. We will take one of such components (Spacecraft) and understand how its implemented, rest components are following the same pattern.&lt;/p&gt;

&lt;p&gt;The Spacecraft components shows the data from the spacecraft endpoint of the ISRO-API. Let us break down the Spacecraft component.&lt;/p&gt;

&lt;p&gt;The initial setup of Spacecraft components 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;// pages/Spacecraft.ts

import Container from '../components/Container';
import PageTitle from '../components/PageTitle';

const Spacecraft = () =&amp;gt; {
  return (
    &amp;lt;Container&amp;gt;
      &amp;lt;PageTitle title="Spacecraft" icon={'rocket'} /&amp;gt;
    &amp;lt;/Container&amp;gt;
  );
};

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

&lt;/div&gt;



&lt;p&gt;Here, we have created a Container component for wrapping within TailwindCSS container classes. There is a PageTitle component too that shows the page title and icon related to the page.&lt;/p&gt;

&lt;p&gt;Let us add the dispatch and selector hooks:&lt;br&gt;
&lt;/p&gt;

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

...
import { useAppDispatch, useAppSelector } from '../hooks';
import {
  selectSpacecraftsFiltered,
  selectSearchValue,
} from '../reducers/SpacecraftReducers';

const Spacecraft = () =&amp;gt; {
  const filtered = useAppSelector(selectSpacecraftsFiltered);
  const searchValue = useAppSelector(selectSearchValue);
  const dispatch = useAppDispatch();

  ...

  return (...);
};

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

&lt;/div&gt;



&lt;p&gt;Here, we have added the hooks for selector and dispatch those we have created in the part 1 (previous article). Using these hooks we are creating the selectors namely filtered and searchValue constants and dispatch a method using which we will invoke the actions.&lt;/p&gt;

&lt;p&gt;Let us call the ISRO-API to get the data on the page load, for this we will be needing useEffect() hook:&lt;br&gt;
&lt;/p&gt;

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

...
import { useEffect } from 'react';
import appService from '../services/app.service';

const Spacecraft = () =&amp;gt; {
  ...

  useEffect(() =&amp;gt; {
    const getAll = async () =&amp;gt; {
      try {
        const data = await appService.getSpacecrafts();
        console.log(data);
      } catch (error) {
        console.log('Error Occurred', error);
      }
    };

    getAll();
  }, []);

  return (...);
};

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

&lt;/div&gt;



&lt;p&gt;Here, we are making an network call to ISRO-API spacecraft endpoint. The data will be logged to console.&lt;/p&gt;

&lt;p&gt;Let us dispatch the actions now based on the sequence of operations:&lt;br&gt;
&lt;/p&gt;

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

...
import { useEffect } from 'react';
import appService from '../services/app.service';
import { setLoading } from '../reducers/AppReducers';
import {
  getAll as getSpacecrafts,
} from '../reducers/SpacecraftReducers';

const Spacecraft = () =&amp;gt; {
  ...

  useEffect(() =&amp;gt; {
    const getAll = async () =&amp;gt; {
      try {
        dispatch(setLoading(true));
        const data = await appService.getSpacecrafts();
        dispatch(getSpacecrafts(data.spacecrafts));
      } catch (error) {
        console.log('Error Occurred', error);
      } finally {
        dispatch(setLoading(false));
      }
    };

    if (filtered === null) {
      getAll();
    }
  }, []);

  return (...);
};

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

&lt;/div&gt;



&lt;p&gt;Here, we have dispatched the corresponding actions like setLoading and getSpacecrafts as per the need. Also, notice that we are calling getAll() method within useEffect() hook only when the filtered is null, otherwise the data is already present and not need to call the API.&lt;/p&gt;

&lt;p&gt;Let us now work on the listing of the spacecraft data in a tabular format:&lt;br&gt;
&lt;/p&gt;

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

...
import Container from '../components/Container';
import PageTitle from '../components/PageTitle';
import NoRecords from '../components/NoRecords';
import Icon from '../components/Icon';

const Spacecraft = () =&amp;gt; {
  ...

  return (
    &amp;lt;Container&amp;gt;
      &amp;lt;PageTitle title="Spacecraft" icon={'rocket'} /&amp;gt;

      {filtered &amp;amp;&amp;amp; (
        &amp;lt;div className="overflow-x-auto"&amp;gt;
          &amp;lt;table className="table table-zebra"&amp;gt;
            &amp;lt;thead&amp;gt;
              &amp;lt;tr&amp;gt;
                &amp;lt;th className="w-1"&amp;gt;#&amp;lt;/th&amp;gt;
                &amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;
              &amp;lt;/tr&amp;gt;
            &amp;lt;/thead&amp;gt;
            &amp;lt;tbody&amp;gt;
              {filtered.map(item =&amp;gt; (
                &amp;lt;tr key={item.id}&amp;gt;
                  &amp;lt;td&amp;gt;{item.id}&amp;lt;/td&amp;gt;
                  &amp;lt;td&amp;gt;
                    &amp;lt;div className="flex items-center space-x-3"&amp;gt;
                      &amp;lt;div className="text-4xl"&amp;gt;
                        &amp;lt;Icon icon="rocket" classes="w-10" /&amp;gt;
                      &amp;lt;/div&amp;gt;
                      &amp;lt;div&amp;gt;
                        &amp;lt;div className="font-bold"&amp;gt;{item.name}&amp;lt;/div&amp;gt;
                        &amp;lt;div className="text-sm opacity-50"&amp;gt;Spacecraft&amp;lt;/div&amp;gt;
                      &amp;lt;/div&amp;gt;
                    &amp;lt;/div&amp;gt;
                  &amp;lt;/td&amp;gt;
                &amp;lt;/tr&amp;gt;
              ))}
            &amp;lt;/tbody&amp;gt;
          &amp;lt;/table&amp;gt;
        &amp;lt;/div&amp;gt;
      )}

      &amp;lt;NoRecords filtered={filtered} /&amp;gt;

    &amp;lt;/Container&amp;gt;
  );
};

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

&lt;/div&gt;



&lt;p&gt;Here, we created a responsive table using TailwindCSS and DaisyUI. Nothing fancy here, just using our Icon component to create the rocket icon. At the very end we have our NoRecords component that shows no entry if data is not found after doing a search. Notice that we are using the “filtered” selector constant for iterating the rows.&lt;/p&gt;

&lt;p&gt;Let us put the last piece of the puzzle, the Search component.&lt;/p&gt;

&lt;h3&gt;
  
  
  Designing Search Component
&lt;/h3&gt;

&lt;p&gt;We have created a Search component that will do the searching within the given list irrespective of where its been used. It will work on the supplied data. First we will see its application and usage in our list component of Spacecraft.&lt;br&gt;
&lt;/p&gt;

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

...
import Container from '../components/Container';
import PageTitle from '../components/PageTitle';
import {
  clearSearch,
  search,
  selectSearchValue,
} from '../reducers/SpacecraftReducers';
import Search from '../components/Search';

const Spacecraft = () =&amp;gt; {
  ...
  const searchValue = useAppSelector(selectSearchValue);

  return (
    &amp;lt;Container&amp;gt;
      &amp;lt;PageTitle title="Spacecraft" icon={'rocket'} /&amp;gt;

      &amp;lt;Search
        placeholder="Search spacecrafts..."
        value={searchValue}
        search={search}
        clearSearch={clearSearch}
      /&amp;gt;

      ...

    &amp;lt;/Container&amp;gt;
  );
};

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

&lt;/div&gt;



&lt;p&gt;Here, we have added the Search component that takes 4 inputs, first the placeholder text that will be displayed to the user. Second the search value, this value is provided by one of the selectors named selectSearchValue; the reason to provide the selector’s value is to keep it updated through store and not from local value, so when user traverse back and forth between pages the search value will still be persisted providing great user experience. We then providing the search and clearSearch actions to the Search component; so it will dispatch these actions when user does some UI operations (like entering search text or clicking on clear button).&lt;/p&gt;

&lt;p&gt;Now, let us look into the inside of Search component:&lt;br&gt;
&lt;/p&gt;

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

import { ChangeEvent } from 'react';
import { useAppDispatch } from '../hooks';
import { SearchModel } from '../models/Search';
import iconCancel from './../assets/icons/cancel.png';

const Search = ({ placeholder, value, search, clearSearch }: SearchModel) =&amp;gt; {
  const dispatch = useAppDispatch();

  const onSearch = (event: ChangeEvent&amp;lt;HTMLInputElement&amp;gt;) =&amp;gt; {
    const value = event.target.value?.toLowerCase();
    if (!value) {
      dispatch(clearSearch());
      return;
    }

    dispatch(search(value));
  };

  const onClearSearch = () =&amp;gt; {
    dispatch(clearSearch());
  };

  return (
    &amp;lt;&amp;gt;
      &amp;lt;div className="flex mb-5"&amp;gt;
        &amp;lt;input
          type="text"
          placeholder={placeholder}
          className="input input-bordered w-full"
          onChange={onSearch}
          value={value || ''}
        /&amp;gt;
        &amp;lt;button className="btn btn-light ms-3" onClick={onClearSearch}&amp;gt;
          &amp;lt;img src={iconCancel} alt="" className="w-4 inline m-0" /&amp;gt;
        &amp;lt;/button&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/&amp;gt;
  );
};

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

&lt;/div&gt;



&lt;p&gt;Here, we are getting these 4 props and using them appropriately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;placeholder:&lt;/strong&gt; Used inside the input’s placeholder attribute.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;value:&lt;/strong&gt; Assigned to the value attribute of the input control (if the value is not truthy then its kept empty).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;search:&lt;/strong&gt; The search action method is invoked within the onSearch event handler method only if the search text is provided.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;clearSearch:&lt;/strong&gt; The clearSearch action method is invoked when user clicks on the clear button or user clears out the search text from the input control.&lt;/p&gt;

&lt;p&gt;Rest of the UI components are simple and self-explanatory like Icon, Container, PageTitle, etc. those are built using TailwindCSS and DaisyUI. Another worth to mention item is the usage of another API for the country flags that is &lt;a href="https://restcountries.com/"&gt;REST Countries&lt;/a&gt;. This API is used in the Customer Satellite page where the ISRO deployed the satellites of different countries into the space. To show the flags of these countries we are using the REST Countries API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Git Repository
&lt;/h3&gt;

&lt;p&gt;Check out the git repository for this project or download the code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://hello-isro.netlify.app/"&gt;Application&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ZakiMohammed/react-isro-app/archive/refs/heads/master.zip"&gt;Download Code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ZakiMohammed/react-isro-app"&gt;Git Repository&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;The ISRO React App will surely help the developer out there to understand concepts like React TypeScript, Redux Toolkit, Axios, Routing etc. The amazing ISRO-API is simple and self-explanatory, which actually allow us to consume it and build simple apps and working on our UI skills. Again, giving a shout out to the ISRO-API team to bringing such an amazing API to the world.&lt;/p&gt;

&lt;p&gt;Hope this article helps.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at&lt;/em&gt; &lt;a href="https://codeomelet.com/posts/isro-react-app-working-on-the-components-part-2"&gt;&lt;em&gt;https://codeomelet.com&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>reduxtoolkit</category>
      <category>isro</category>
      <category>redux</category>
      <category>react</category>
    </item>
    <item>
      <title>ISRO React App — Initial Setup — Part 1</title>
      <dc:creator>Zaki Mohammed</dc:creator>
      <pubDate>Wed, 02 Aug 2023 13:54:41 +0000</pubDate>
      <link>https://dev.to/zakimohammed/isro-react-app-initial-setup-part-1-4ala</link>
      <guid>https://dev.to/zakimohammed/isro-react-app-initial-setup-part-1-4ala</guid>
      <description>&lt;h3&gt;
  
  
  ISRO React App — Initial Setup — Part 1
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qRRg15z7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/1%2Aisu_Ow7gvjq7hNP6Qv5WSA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qRRg15z7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/1%2Aisu_Ow7gvjq7hNP6Qv5WSA.jpeg" alt="" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hats off to amazing ISRO team to their ongoing space missions. There is an amazing ISRO API available to use for the folks who admire ISRO and their contributions. In this article we will talk about the app created using React that consumes the ISRO API endpoints. The React ISRO App is also based on Vite, Typescript, Redux, TailwindCSS, DaisyUI and some other cool techs. The app will help to explore the ISRO API along with the React UI ecosystem.&lt;/p&gt;

&lt;p&gt;Through this app, we extend our heartfelt gratitude to the brilliant minds at ISRO for their invaluable contributions, elevating India’s presence in space research. The ISRO API has 4 endpoints namely Spacecraft, Launcher, Customer Satellite and Centre. These endpoints can be found here &lt;a href="https://isro.vercel.app/"&gt;ISRO-API&lt;/a&gt; along with the &lt;a href="https://github.com/isro/api"&gt;GitHub Repository&lt;/a&gt;. All of these APIs are GET returning data in the form of array.&lt;/p&gt;

&lt;p&gt;Thanks to the devs (&lt;a href="https://github.com/isroadmin"&gt;@isroadmin&lt;/a&gt;, &lt;a href="https://github.com/Pramod-Devireddy"&gt;@Pramod-Devireddy&lt;/a&gt; and many more) behind the ISRO-API, those have contributed to have such an API for the devs out there. This will surely help to learn and grow new skills and techs.&lt;/p&gt;

&lt;p&gt;Respecting the time taken to develop this app, this topic will be divided in to 2 parts. The first part will talk about the initial project setup and dependency installation while the part 2 will talk about the app development and component creation. Let us get started with the application development.&lt;/p&gt;

&lt;p&gt;We will head in this direction:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Project Structure&lt;/li&gt;
&lt;li&gt;Create Project&lt;/li&gt;
&lt;li&gt;Add Dependencies&lt;/li&gt;
&lt;li&gt;Create Models&lt;/li&gt;
&lt;li&gt;Redux Toolkit Setup&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Project Structure
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Qj3lr_L8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2A28X7OV9-XgyFyOKP.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Qj3lr_L8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2A28X7OV9-XgyFyOKP.png" alt="" width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just to showcase the final project structure before we begin with developing from scratch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src
|-- assets
| |-- react.svg
|-- components
| |-- Container.tsx
| |-- Footer.tsx
| |-- Header.tsx
| |-- Navbar.tsx
| |-- NoRecords.tsx
| |-- PageTitle.tsx
| |-- Search.tsx
| |-- Spinner.tsx
|-- constants
| |-- core.constant.ts
|-- models
| |-- Centre.ts
| |-- Container.ts
| |-- Country.ts
| |-- CustomerSatellite.ts
| |-- Dto.ts
| |-- Launcher.ts
| |-- Search.ts
| |-- Spacecraft.ts
| |-- States.ts
|-- pages
| |-- About.tsx
| |-- Centre.tsx
| |-- CustomerSatellite.tsx
| |-- Home.tsx
| |-- Launcher.tsx
| |-- Spacecraft.tsx
|-- reducers
| |-- AppReducers.ts
| |-- CentreReducers.ts
| |-- CustomerSatelliteReducers.ts
| |-- LauncherReducers.ts
| |-- SpacecraftReducers.ts
|-- services
| |-- app.service.ts
| |-- core.service.ts
|-- App.tsx
|-- hooks.ts
|-- index.css
|-- main.tsx
|-- store.ts
|-- vite-env.d.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create Project
&lt;/h3&gt;

&lt;p&gt;Now, let us get started with creating project.&lt;/p&gt;

&lt;p&gt;Make sure you have Node.js installed and Vite available globally. Checkout about &lt;a href="https://vitejs.dev/"&gt;Vite&lt;/a&gt;, also checkout this tutorial about &lt;a href="https://codeomelet.com/videos/vite-for-typescript-geekleus-typescript-global-summit-2023"&gt;Vite For TypeScript&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm create vite@latest

&amp;gt; Give your project a name: react-isro-app
&amp;gt; Select React + TypeScript option

cd react-isro-app
npm i
npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add Dependencies
&lt;/h3&gt;

&lt;p&gt;The ISRO app is built using many different packages and dependencies, let us explore few of the important ones.&lt;/p&gt;

&lt;h4&gt;
  
  
  TailwindCSS
&lt;/h4&gt;

&lt;p&gt;Working with TailwindCSS for the very first time seems little bit scary, but don’t worry I have also overcome my fears by facing them and any one can simply do the same. Nothing fancy here, pretty usual steps to add TailwindCSS to your React + Vite app, you can check the steps out in more detail from &lt;a href="https://tailwindcss.com/docs/guides/vite"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Install TailwindCSS:&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 -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open “tailwind.config.js” and update with below code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/** @type {import('tailwindcss').Config} */
export default {
  content: [
    "./index.html",
    "./src/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then add these dependencies to your “index.css” file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@tailwind base;
@tailwind components;
@tailwind utilities;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Daisy UI
&lt;/h4&gt;

&lt;p&gt;I loved the DaisyUI components, they are beautiful and mesmerizing. Checkout them from &lt;a href="https://daisyui.com/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Install daisyUI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i -D daisyui@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then add daisyUI to your “tailwind.config.js” files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.exports = {
  //...
  plugins: [require("daisyui")],
  daisyui: {
    themes: ["light", "dark", "cupcake"],
  },
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  TailwindCSS Typography
&lt;/h4&gt;

&lt;p&gt;There is a sperate plugin for Typography in TailwindCSS that you have to install separately. Checkout TailwindCSS Typography from &lt;a href="https://tailwindcss.com/docs/typography-plugin"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Install “&lt;a class="mentioned-user" href="https://dev.to/tailwindcss"&gt;@tailwindcss&lt;/a&gt;/typography”:&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 -D @tailwindcss/typography
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then add daisyUI to your “tailwind.config.js” files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.exports = {
  //...
  plugins: [require('@tailwindcss/typography'), require("daisyui")],
  //...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  RTK
&lt;/h4&gt;

&lt;p&gt;When some says Redux then think about rituals and ceremonies you have to perform. Checkout Redux Toolkit from &lt;a href="https://redux-toolkit.js.org/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Install Redux Toolkit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i @reduxjs/toolkit
npm i react-redux

npm i @types/react-redux -D
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure you install the “@types/react-redux” as dev dependencies to get the TypeScript support.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create Models
&lt;/h3&gt;

&lt;p&gt;Models, entities, data structures set the basis for any apps. If these are created in a correct fashion, then your app guarantees to grow and scale easily. Let us focus on creating the necessary models.&lt;/p&gt;

&lt;p&gt;Create ISRO-API models:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export interface Spacecraft {
  id: number;
  name: string;
}

export interface Launcher {
  id: string;
}

export interface CustomerSatellite {
  id: string;
  country: string;
  launch_date: string;
  mass: string;
  launcher: string;
  flag: string;
}

export interface Centre {
  id: number;
  name: string;
  Place: string;
  State: string;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create corresponding DTOs:&lt;br&gt;
&lt;/p&gt;

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

import { Centre } from './Centre';
import { CustomerSatellite } from './CustomerSatellite';
import { Launcher } from './Launcher';
import { Spacecraft } from './Spacecraft';

export interface LauncherDto {
  launchers: Launcher[];
}

export interface CentreDto {
  centres: Centre[];
}

export interface CustomerSatelliteDto {
  customer_satellites: CustomerSatellite[];
}

export interface SpacecraftDto {
  spacecrafts: Spacecraft[];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, you can see a pattern in the properties. The first property will hold the data obtained from ISRO-API, the “filtered” property will hold the filtered data based on the search input. Last property is the search value that holds the value of the search text box. Let us do the RTK setup for overall project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Redux Toolkit Setup
&lt;/h3&gt;

&lt;p&gt;For setting up RTK one has to follow these steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configure Store&lt;/li&gt;
&lt;li&gt;Register Store&lt;/li&gt;
&lt;li&gt;Initialize States, Reducers and Selectors&lt;/li&gt;
&lt;li&gt;Register Reducer&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Configure Store
&lt;/h3&gt;

&lt;p&gt;Create a file named “store.ts” and First configure the store:&lt;br&gt;
&lt;/p&gt;

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

import { configureStore } from '@reduxjs/toolkit';

export const store = configureStore({
  reducer: {
  },
});

export type RootState = ReturnType&amp;lt;typeof store.getState&amp;gt;;
export type AppDispatch = typeof store.dispatch;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, reducer is empty for now.&lt;/p&gt;

&lt;h3&gt;
  
  
  Register Store
&lt;/h3&gt;

&lt;p&gt;Register the store in “main.tsx” file by wrapping the  component within store provider:&lt;br&gt;
&lt;/p&gt;

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

import React from 'react'
import { Provider } from 'react-redux';
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'
import { store } from './store.ts'

ReactDOM.createRoot(document.getElementById('root')!).render(
  &amp;lt;React.StrictMode&amp;gt;
    &amp;lt;Provider store={store}&amp;gt;
      &amp;lt;App /&amp;gt;
    &amp;lt;/Provider&amp;gt;
  &amp;lt;/React.StrictMode&amp;gt;,
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The “Provider” comes from “react-redux”.&lt;/p&gt;

&lt;h3&gt;
  
  
  Initialize States, Reducers and Selectors
&lt;/h3&gt;

&lt;p&gt;Let us do the hard work now, initializing states, reducers and selectors. For this create a folder named “reducers”. We will see this setup for one state called “spacecraft”.&lt;br&gt;
&lt;/p&gt;

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

const initialState: SpacecraftState = {
  spacecrafts: null,
  filtered: null,
  searchValue: null,
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are the initial values of the spacecraft state.&lt;br&gt;
&lt;/p&gt;

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

export const spacecraftSlice = createSlice({
  name: 'spacecraft',
  initialState,
  reducers: {
    getAll: (state, action: PayloadAction&amp;lt;Spacecraft[]&amp;gt;) =&amp;gt; {
      state.spacecrafts = action.payload;
      state.filtered = action.payload;
      state.searchValue = null;
    },
    clearSearch: state =&amp;gt; {
      state.filtered = state.spacecrafts;
      state.searchValue = null;
    },
    search: (state, action: PayloadAction&amp;lt;string&amp;gt;) =&amp;gt; {
      state.searchValue = action.payload;
      state.filtered = state.spacecrafts
        ? state.spacecrafts?.filter(i =&amp;gt; i.name.toLowerCase().includes(action.payload))
        : null;
    },
  },
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we are creating the reducer slices, this will include the definitions of name of the state slice, initial state values, and reducers methods. There are 3 reducers namely getAll, clearSearch and search. The getAll is invoked post obtaining the value from the ISRO-API call and responsible to set all the state values. The clearSearch is to reset the filtered list and search value. The search is called when user performs a search operation resulting in updated filtered array.&lt;br&gt;
&lt;/p&gt;

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

export const { getAll, clearSearch, search } = spacecraftSlice.actions;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Export the action methods getAll, clearSearch and search.&lt;br&gt;
&lt;/p&gt;

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

export const selectSpacecrafts = (state: RootState) =&amp;gt; state.spacecraft.spacecrafts;
export const selectSpacecraftsFiltered = (state: RootState) =&amp;gt; state.spacecraft.filtered;
export const selectSearchValue = (state: RootState) =&amp;gt; state.spacecraft.searchValue;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the selectors for all the 3 states.&lt;br&gt;
&lt;/p&gt;

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

export default spacecraftSlice.reducer;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, export the reducer as the default export for the file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Register Reducers
&lt;/h3&gt;

&lt;p&gt;Finally, you can now register this reducer to your store in the “store.ts” file:&lt;br&gt;
&lt;/p&gt;

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

import { configureStore } from '@reduxjs/toolkit';
import spacecraftReducers from './reducers/SpacecraftReducers';

export const store = configureStore({
  reducer: {
    spacecraft: spacecraftReducers,
  },
});

export type RootState = ReturnType&amp;lt;typeof store.getState&amp;gt;;
export type AppDispatch = typeof store.dispatch;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we are creating a property named spacecraft and assigning it to spacecraftReducers.&lt;/p&gt;

&lt;p&gt;Similar to this we will create the other reducers in the same fashion. The final store.ts file will look 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;// store.ts

import { configureStore } from '@reduxjs/toolkit';
import appReducer from './reducers/AppReducers';
import spacecraftReducers from './reducers/SpacecraftReducers';
import launcherReducers from './reducers/LauncherReducers';
import customerSatelliteReducers from './reducers/CustomerSatelliteReducers';
import centreReducers from './reducers/CentreReducers';

export const store = configureStore({
  reducer: {
    app: appReducer,
    spacecraft: spacecraftReducers,
    launcher: launcherReducers,
    customerSatellite: customerSatelliteReducers,
    centre: centreReducers,
  },
});

export type RootState = ReturnType&amp;lt;typeof store.getState&amp;gt;;
export type AppDispatch = typeof store.dispatch;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This does mark completion of the initial setup of our amazing isro-react-app. We will now move on to work on the app components in the next article.&lt;/p&gt;

&lt;h3&gt;
  
  
  Git Repository
&lt;/h3&gt;

&lt;p&gt;Check out the git repository for this project or download the code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://hello-isro.netlify.app/"&gt;Application&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ZakiMohammed/react-isro-app/archive/refs/heads/master.zip"&gt;Download Code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ZakiMohammed/react-isro-app"&gt;Git Repository&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;Creating spaceship, researching about cosmos actually involves rocket science (like literally — very difficult). The team ISRO is doing wonderful job in the exploration and research area. It was fun and great learning while working on this app and consuming the ISRO-API. Thanks to folks those are behind creating the ISRO-API available for the devs like us to learn and grow. This app will surely help to understand React, Redux, TypeScript concepts and provide a starting point for learning further.&lt;/p&gt;

&lt;p&gt;Hope this article helps.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at&lt;/em&gt; &lt;a href="https://codeomelet.com/posts/isro-react-app-initial-setup-part-1"&gt;&lt;em&gt;https://codeomelet.com&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>isro</category>
      <category>tailwindcss</category>
      <category>isroapi</category>
    </item>
    <item>
      <title>Angular Performance Improvement</title>
      <dc:creator>Zaki Mohammed</dc:creator>
      <pubDate>Fri, 28 Jul 2023 15:34:14 +0000</pubDate>
      <link>https://dev.to/zakimohammed/angular-performance-improvement-47n4</link>
      <guid>https://dev.to/zakimohammed/angular-performance-improvement-47n4</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--a8IAPfSq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/0%2AyD-2COzAPZlO_8mn.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--a8IAPfSq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/0%2AyD-2COzAPZlO_8mn.jpg" alt="" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Gaining performance is not an easy job; a lot of hair loss cases has been reported in order to achieve tiniest bit of improvement in term of app speed.&lt;/p&gt;

&lt;p&gt;In this series our focus will be to save those losing hairs by doing what best we can do to speeden up the app. This includes post related to Brotli, Gzip, Caching, API data caching, etc.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://codeomelet.com/posts/gzip-dockerized-angular-app-with-nginx-ngdocker"&gt;Gzip Dockerized Angular App With Nginx — Ngdocker | CodeOmelet&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DyGISz7y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/1%2AkqP4ScQ7VapPryRFuKEQcg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DyGISz7y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/1%2AkqP4ScQ7VapPryRFuKEQcg.jpeg" alt="" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Leveling up by taking performance improvement measures for our super friendly NgDocker app. In this article, we will explore how we can enable Gzip to improve performance at a hulking scale.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codeomelet.com/posts/nginx-cache-config-for-dockerized-angular-app-ngdocker"&gt;2. Nginx Cache Config For Dockerized Angular App — Ngdocker | CodeOmelet&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3-1sIdps--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/1%2AZMLtVuxGQ6OGcJaxTgMF_Q.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3-1sIdps--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/1%2AZMLtVuxGQ6OGcJaxTgMF_Q.jpeg" alt="" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;App running so fast, even the cops are chasing! Not breaking any law here just configuring Nginx caching for our Dockerized Angular app. In this article we will address the need for speed for our super silly NgDocker app by setting up the Nginx cache configurations for static files.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codeomelet.com/posts/cache-angular-http-request-using-ngneat-cashew"&gt;3. Cache Angular Http Request Using @Ngneat/Cashew | CodeOmelet&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HWEb331a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/1%2A4BRAL2zK85BdcFoQpQb09g.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HWEb331a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/1%2A4BRAL2zK85BdcFoQpQb09g.jpeg" alt="" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The time of not wasting time on caching HTTP request data has come. Presenting @ngneat/cashew package to do the needfully; it takes our stress of manually creating service variables to hold up the HTTP requests data again and again. In this article we will explore @ngneat/cashew dope package for caching HTTP requests.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codeomelet.com/posts/brotli-dockerized-angular-app-with-nginx-ngdocker"&gt;4. Brotli Dockerized Angular App With Nginx — Ngdocker | CodeOmelet&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gekFohZh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/1%2ArlNP-9ZlxudJ0nClpTixZg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gekFohZh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/1%2ArlNP-9ZlxudJ0nClpTixZg.jpeg" alt="" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Patience is the key to success; not heard or followed by any of the web app visitors. We want everything to be loaded within blink of an eye. This is the place where Brotli shines. In this article, we will explore how we can enable Brotli in the NgDocker app to improve performance at a marvelous scale.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at&lt;/em&gt; &lt;a href="https://codeomelet.com/series/angular-performance-improvement"&gt;&lt;em&gt;https://codeomelet.com&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>improvement</category>
      <category>performance</category>
      <category>cache</category>
    </item>
    <item>
      <title>Brotli Dockerized Angular App with Nginx — NgDocker</title>
      <dc:creator>Zaki Mohammed</dc:creator>
      <pubDate>Thu, 20 Jul 2023 10:25:26 +0000</pubDate>
      <link>https://dev.to/zakimohammed/brotli-dockerized-angular-app-with-nginx-ngdocker-2gh8</link>
      <guid>https://dev.to/zakimohammed/brotli-dockerized-angular-app-with-nginx-ngdocker-2gh8</guid>
      <description>&lt;h3&gt;
  
  
  Brotli Dockerized Angular App with Nginx — NgDocker
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gekFohZh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/1%2ArlNP-9ZlxudJ0nClpTixZg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gekFohZh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/1%2ArlNP-9ZlxudJ0nClpTixZg.jpeg" alt="" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Patience is the key to success; not heard or followed by any of the web app visitors. We want everything to be loaded within blink of an eye. This is the place where Brotli shines. In this article, we will explore how we can enable Brotli in the NgDocker app to improve performance at a marvelous scale.&lt;/p&gt;

&lt;p&gt;If you love breads/rolls then you will surely love Brotli (Brotli: it’s a Swiss German word for a bread roll). Brotli is level up compression algorithm, faster than Gzip, widely available, developed by Google under MIT license. So, no valid reason available for not using it. There are some useful articles where we can understand the back story of Brotli compression and why it’s better than Gzip.&lt;/p&gt;

&lt;p&gt;You can check these links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kinsta.com/blog/brotli-compression/"&gt;Brotli Compression: A Fast Alternative to GZIP Compression&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://world.siteground.com/blog/brotli-vs-gzip-compression/"&gt;The Difference Between Brotli And Gzip Compression Algorithms To Speed Up Your Site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.fastly.com/blog/brotli-is-great-at-making-things-small-and-no-we-arent-talking-about-the"&gt;Brotli is great at making things small&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article our focus will be to check how we can enable Brotli Compression with Angular, Docker, Nginx kind of setup. We will take the same app from this article &lt;a href="https://codeomelet.com/posts/gzip-dockerized-angular-app-with-nginx-ngdocker"&gt;Gzip Dockerized Angular App with Nginx — NgDocker&lt;/a&gt; and instead of Gzip we will enable Brotli.&lt;/p&gt;

&lt;p&gt;We will head in this direction:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Setup Angular App&lt;/li&gt;
&lt;li&gt;Create a Dockerfile with Brotli&lt;/li&gt;
&lt;li&gt;Enable Brotli in Nginx Configuration&lt;/li&gt;
&lt;li&gt;Run Docker Container&lt;/li&gt;
&lt;li&gt;Celebrate the Impact of Brotli&lt;/li&gt;
&lt;li&gt;Brotli vs Gzip vs No Encoding&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Setup Angular App
&lt;/h3&gt;

&lt;p&gt;Similar to NgDocker mark 2, we will do the daily chores of Angular app and as per the need of the hour we are considering v16 of Angular; just for fun we are using Cirrus UI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng new ng-docker-mark-4

npm i cirrus-ui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Below shows the skeleton of the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng-docker-mark-4
|-- nginx
| |-- nginx.conf
|-- src
| |-- app
| | |-- core
| | | |-- components
| | | | |-- footer
| | | | |-- header
| | | |-- pages
| | | | |-- home
| | | |-- services
| | | |-- core.service.ts
| | | |-- products.service.ts
| | |-- modules
| | | |-- posts
| | | |-- users
| | |-- app-routing.module.ts
| | |-- app.component.html
| | |-- app.component.ts
| | |-- app.module.ts
|-- angular.json
|-- Dockerfile
|-- package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This application respect minimalism and have less number of routes and components. For the sake of understanding how compression affects the lazy loaded modules we have added 2 feature standalone modules Posts and Users. Thanks to Angular v15 these are standalone components routed in a lazy loaded fashion. The data is coming from our beloved JSON Placeholder API.&lt;/p&gt;

&lt;p&gt;Below shows the business logic for Posts component, the Users component is also created in the similar manner:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;posts.component.html&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="section"&amp;gt;
  &amp;lt;div class="content"&amp;gt;
    &amp;lt;div class="row mb-3" *ngFor="let post of posts$ | async"&amp;gt;
      &amp;lt;div class="col-sm-4"&amp;gt;
        &amp;lt;img
          class="img-stretch u-round-md"
          [src]="getImage(post)"
          alt="img-stretch"
          height="400"
          (error)="getDefaultImage($event)" /&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;div class="col-lg-6 col-md-8"&amp;gt;
        &amp;lt;h3&amp;gt;{{ post.title | titlecase }}&amp;lt;/h3&amp;gt;
        &amp;lt;p&amp;gt;{{ post.body }}&amp;lt;/p&amp;gt;
        &amp;lt;div&amp;gt;
          &amp;lt;span class="icon"&amp;gt;
            &amp;lt;i class="far fa-wrapper fa-clock" aria-hidden="true"&amp;gt;&amp;lt;/i&amp;gt;
          &amp;lt;/span&amp;gt;
          {{randomDate | date: 'fullDate'}}
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;posts.component.ts&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;posts$?: Observable;

constructor(private coreService: CoreService) {
  this.posts$ = coreService.getPosts();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Below shows the post service, the user service is also created in similar manner:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;products.service.ts&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private url: string = environment.apiUrl;

constructor(private http: HttpClient) {}

get(name: string) {
  const url = `${this.url}${name}`;
  return this.http.get(url);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Below shows the routes of the application:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;app-routing.module.ts&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const routes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'posts', loadComponent: () =&amp;gt; import('./modules/posts/posts.component').then(x =&amp;gt; x.PostsComponent) },
  { path: 'users', loadComponent: () =&amp;gt; import('./modules/users/users.component').then(x =&amp;gt; x.UsersComponent) },
  { path: '**', component: HomeComponent },
];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create Dockerfile with Brotli
&lt;/h3&gt;

&lt;p&gt;After fighting a constant battle with ChatGPT to get the correct answer, I came to final solution that actually works to enable Brotli compression with Dockerized Angular app; thanks to StackOverflow user &lt;a href="https://stackoverflow.com/users/131021/stwissel"&gt;stwissel&lt;/a&gt; ( &lt;a href="https://stackoverflow.com/questions/76237701/how-to-enable-brotli-compression-in-nginx-and-docker-for-angular-app/76544700"&gt;StackOverflow Question&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Our Dockerfile must be looking like below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Build container
FROM node:18-alpine AS builder
WORKDIR /app

# Make sure we got brotli
RUN apk update
RUN apk add --upgrade brotli

# NPM install and build
ADD package.json .
RUN npm install
ADD . .
RUN npm run build:prod

RUN cd /app/dist &amp;amp;&amp;amp; find . -type f -exec brotli {} \;

# Actual runtime container
FROM alpine
RUN apk add brotli nginx nginx-mod-http-brotli

# Minimal config
COPY nginx/nginx.conf /etc/nginx/http.d/default.conf

# Actual data
COPY --from=builder /app/dist/ng-docker-mark-4 /usr/share/nginx/html
CMD ["nginx", "-g", "daemon off;"]
EXPOSE 80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, most of the thing seems pretty normal and related to default Angular-Docker setup. We will highlight the important steps those are needed for Brotli.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Make sure we got brotli
RUN apk update
RUN apk add --upgrade brotli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the first line updates the package index inside the container. The second line installs or upgrades the “brotli” compression library inside the container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RUN cd /app/dist &amp;amp;&amp;amp; find . -type f -exec brotli {} \;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the statement “cd /app/dist” changes the working directory to “/app/dist” and the statement “find . -type f -exec brotli {} \;” finds all files in the current directory and its subdirectories (“.”), then applies the “brotli” compression command to each file (“{}”).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Actual runtime container
FROM alpine
RUN apk add brotli nginx nginx-mod-http-brotli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the first line specifies the base image for the runtime container. In this case, it uses the “alpine” image, which is a lightweight Linux distribution. The second line installs the “brotli” library, “nginx” web server, and the “nginx-mod-http-brotli” module inside the container.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enable Brotli in Nginx Configuration
&lt;/h3&gt;

&lt;p&gt;The time has come to enable Brotli in Nginx configuration file which we have seen in the previous article for re-writing the route for SPA:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
    brotli on;
    brotli_static on;
    brotli_comp_level 6;
    brotli_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
    ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it, no need to check or look anywhere else, this much is required for super speeding up your app performance! Also, these settings are self explanatory, the first 2 lines enables the Brotli and Brotli for static files, followed by setting the compression level to 6 that ranges from 0 to 11 (default is 6), then we are mentioning the types for which the Brotli will be enabled (note: no need to compress already compressed images like JPG).&lt;/p&gt;

&lt;h3&gt;
  
  
  Run Docker Container
&lt;/h3&gt;

&lt;p&gt;For this we require to have Docker Desktop, otherwise we are unworthy to proceed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build Docker Image&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# build image
docker build -t ng-docker:mark-4 .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Run Docker Container&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# run container
docker run -p 3300:80 --name ng-docker-mark-4-container ng-docker:mark-4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, you can change to your favorite port, we are going with “3300” for a change. Once, executed then open &lt;code&gt;http://localhost:3300/&lt;/code&gt; to run the application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Celebrate the Impact of Brotli
&lt;/h3&gt;

&lt;p&gt;Its time for the celebration! Due to drastic improvement in the performance. Here, we will compare the time to load files with and without Brotli through below screen shots:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No Content Encoding (before)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LSWXtggk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2A4qoUID1zxgG0TXXa.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LSWXtggk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2A4qoUID1zxgG0TXXa.jpg" alt="" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gzip Enabled (before)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rp-V6M_3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2ALv9fvsIqT88VNvGa.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rp-V6M_3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2ALv9fvsIqT88VNvGa.jpg" alt="" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Brotli Enabled (after)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4T2a2RCZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2AVGqbNYVx1Jir7Twx.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4T2a2RCZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2AVGqbNYVx1Jir7Twx.jpg" alt="" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, we can clearly see the before and after effect on the file size, the biggest file (main.js) early which was around 265KB turns out to be of 97.7KB of size after enabling the Gzip and then finally it becomes 72.5KB once Brotli is enabled. The snowball effect can clearly be seen in other files too. The “Content-Encoding” header usually doesn’t appear in the browser’s Network tab of Developer Tools, we can add it by simply right clicking on the table of Network tab and go to “Headers” option and then add the “Content-Encoding”.&lt;/p&gt;

&lt;p&gt;Similar effect can also be observed with lazy-loaded modules:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lazy Loaded Module — No Content Encoding (before)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TUq9J2i5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2A8zUWSEvFebHRE-iT.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TUq9J2i5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2A8zUWSEvFebHRE-iT.jpg" alt="" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lazy Loaded Module — Brotli Enabled (after)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FuajuUcv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2AK52-8bSDOTprzq8U.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FuajuUcv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2AK52-8bSDOTprzq8U.jpg" alt="" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Brotli vs Gzip vs No Encoding
&lt;/h3&gt;

&lt;p&gt;It’s time to compare everything with everything. Not holding back any punches and comparing 3 scenarios for the base JavaScript file here that is “main.js”.&lt;/p&gt;

&lt;p&gt;Below tables shows the original file size of “main.js” without any content encoding and after encoding using Gzip and Brotli:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;File Size (main.js):&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
None = 265 KB&lt;br&gt;&lt;br&gt;
Gzip (gzip) = 97.7 KB&lt;br&gt;&lt;br&gt;
Brotli (br) = 72.5 KB&lt;/p&gt;

&lt;p&gt;Below tables shows the percentage differences between no encoding to Gzip to Brotli for “main.js”:&lt;/p&gt;

&lt;p&gt;None =&amp;gt; Gzip =&amp;gt; = 92.2526% difference&lt;br&gt;&lt;br&gt;
None =&amp;gt; Brotli =&amp;gt; = 114.074% difference&lt;br&gt;&lt;br&gt;
Gzip =&amp;gt; Brotli =&amp;gt; = 29.6122% difference&lt;/p&gt;

&lt;p&gt;The Brotli compression from None to Brotli is insane around 114% while from None to Gzip is around 92%. The difference between Brotli and Gzip is around 30%.&lt;/p&gt;

&lt;p&gt;So, in a toe-to-toe comparison Brotli is clearly winner here. NOTE: We are only comparing based on JavaScript file.&lt;/p&gt;

&lt;p&gt;In this article &lt;a href="https://world.siteground.com/blog/brotli-vs-gzip-compression/"&gt;The Difference Between Brotli And Gzip Compression Algorithms To Speed Up Your Site&lt;/a&gt;, it is mentioned that the benchmark difference between Gzip and Brotli is as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;14% smaller JavaScript files&lt;/li&gt;
&lt;li&gt;21% smaller HTML files&lt;/li&gt;
&lt;li&gt;17% smaller CSS files&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Git Repository
&lt;/h3&gt;

&lt;p&gt;Check out the git repository for this project or download the code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ZakiMohammed/ng-docker-mark-4/archive/refs/heads/master.zip"&gt;Download Code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ZakiMohammed/ng-docker-mark-4"&gt;Git Repository&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;Add Brotli to your to-do list of performance improvement of your frontend app, you surely will see significance performance improvement in your static files (JS/CSS). CodeOmelet is having Brotli enabled and I have enabled Brotli in many of other Angular/React applications. Brotli is one of the small contributions to your app with such a great impact.&lt;/p&gt;

&lt;p&gt;Hope this article helps.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at&lt;/em&gt; &lt;a href="https://codeomelet.com/posts/brotli-dockerized-angular-app-with-nginx-ngdocker"&gt;&lt;em&gt;https://codeomelet.com&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>brotli</category>
      <category>angular</category>
      <category>nginx</category>
    </item>
    <item>
      <title>NgFate — A tool for deciding the fate of an Angular app</title>
      <dc:creator>Zaki Mohammed</dc:creator>
      <pubDate>Thu, 06 Jul 2023 19:12:46 +0000</pubDate>
      <link>https://dev.to/zakimohammed/ngfate-a-tool-for-deciding-the-fate-of-an-angular-app-4an0</link>
      <guid>https://dev.to/zakimohammed/ngfate-a-tool-for-deciding-the-fate-of-an-angular-app-4an0</guid>
      <description>&lt;h3&gt;
  
  
  NgFate — A tool for deciding the fate of an Angular app
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JpKcGxCw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/1%2AHUpsOMuE7UUmcCp_wi7kGQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JpKcGxCw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/1%2AHUpsOMuE7UUmcCp_wi7kGQ.jpeg" alt="" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A tool for salvation and exploration of your beloved Angular app. It is what’s inside that matters; NgFate will provide your app’s inside, app structure, component routes, dependencies and relations. Made by CodeOmelet with love and a bunch of sleepless nights using dotnet csharp. In this article, we will walk you through, what, why and how of NgFate so that you can deep dive inside your Angular app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CjJwjB-f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2AxAAXvE2tj-nECXdl.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CjJwjB-f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2AxAAXvE2tj-nECXdl.jpg" alt="" width="800" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When our Angular app grows it brings a lot of modules and components to manage and keep track of. One of the problems which I personally face when I start working on any new Angular app is getting the hold on the structure of the application. It is challenging to understand a project who’s who quickly and it takes a good amount of time. To address this problem once it for all, I thought why not we just create a tool that shows a hierarchy and relation between the modules and components. This tool will tell whether the component is routed or not, where it’s been referred and used, how many components are there in a module etc.&lt;/p&gt;

&lt;p&gt;For this I have taken an oath to create a tool that will put a full stop to this suffering and help me and other fellow developers to get a relief. Introducing &lt;a href="https://github.com/ZakiMohammed/ng-fate"&gt;NgFate&lt;/a&gt; tool, built using .NET 6 and C#, providing you bunch of options to view your project structure and hierarchy (JSON, HTML, CLI, etc.).&lt;/p&gt;

&lt;p&gt;In this article we will go through below points to explore &lt;a href="https://github.com/ZakiMohammed/ng-fate"&gt;NgFate&lt;/a&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download and dotnet Run&lt;/li&gt;
&lt;li&gt;Run Application&lt;/li&gt;
&lt;li&gt;Limitations&lt;/li&gt;
&lt;li&gt;Algorithm and Data Structure&lt;/li&gt;
&lt;li&gt;Future Scope&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  1. Download and dotnet Run
&lt;/h3&gt;

&lt;p&gt;The tool is published to GitHub repository &lt;a href="https://github.com/ZakiMohammed/ng-fate"&gt;NgFate&lt;/a&gt;. Below shows the download and dotnet run steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Download the app from the latest &lt;a href="https://github.com/ZakiMohammed/ng-fate/releases"&gt;Releases&lt;/a&gt; section.&lt;/li&gt;
&lt;li&gt;Unzip the project in the directory of your choice.&lt;/li&gt;
&lt;li&gt;Open terminal.&lt;/li&gt;
&lt;li&gt;Go to “publish” folder in the terminal.&lt;/li&gt;
&lt;li&gt;C:\Users\zshaikh&amp;gt;cd C:\Users\zshaikh\Downloads\publish&lt;/li&gt;
&lt;li&gt;Run the app by using the executable:&lt;/li&gt;
&lt;li&gt;C:\Users\zshaikh&amp;gt;ng-fate.exe&lt;/li&gt;
&lt;li&gt;Run the app by using the dotnet command:&lt;/li&gt;
&lt;li&gt;C:\Users\zshaikh&amp;gt;dotnet ng-fate.dll&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Run Application
&lt;/h3&gt;

&lt;p&gt;The tool requires bunch of input from you in order to generate the report of your app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example:
&lt;/h3&gt;

&lt;p&gt;An example of such input values are shown below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Project Path: C:\Zaki\Study\Angular\service-desk\service-desk-app&lt;/li&gt;
&lt;li&gt;Project Prefix (comma separated): app,jam,van&lt;/li&gt;
&lt;li&gt;Select Output Type (1 — JSON, 2 — HTML, 3 — CLI, 4 — ALL): 4&lt;/li&gt;
&lt;li&gt;Output Folder Path: C:\Zaki\Study\Angular\service-desk\des&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Input Details:
&lt;/h3&gt;

&lt;p&gt;Below gives detailing to these inputs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Project Path:&lt;/strong&gt; Path to your Angular project.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Project Prefix (comma separated):&lt;/strong&gt; Multiple comma separated values can be provided as app prefix (e.g app,jam,van).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Select Output Type (1 — JSON, 2 — HTML, 3 — CLI, 4 — ALL):&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;1. If you want a .json file as an output select first option.&lt;/li&gt;
&lt;li&gt;2. If you want a .html file as an output select second option.&lt;/li&gt;
&lt;li&gt;3. If you want output in the terminal it self select third option.&lt;/li&gt;
&lt;li&gt;4. If you want all of the above select fourth option.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output Folder Path:&lt;/strong&gt; Path to your directory where you want all the output files to be generated.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Below shows recording of the above performed steps:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codeomelet.com/Media/Images/posts/ng-fate-download-run-output.webm"&gt;codeomelet.com/Media/Images/posts/ng-fate-download-run-output.webm&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Limitations
&lt;/h3&gt;

&lt;p&gt;Currently the NgFate is rookie and have to evolve a lot. For starter the tool is looking for some ideal structuring of the Angular project as per the Angular official document.&lt;/p&gt;

&lt;p&gt;Check and verify if your app is adhering to these limitations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NgFate doesn’t support standalone components yet.&lt;/li&gt;
&lt;li&gt;The module and component naming must follow Angular guide.&lt;/li&gt;
&lt;li&gt;Each module and component files must ends with the convention &lt;strong&gt;*.module.ts&lt;/strong&gt; and &lt;strong&gt;*.component.ts&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Properly formatted declarations of components within @NgModule decorator (either in single line or new line).&lt;/li&gt;
&lt;li&gt;Properly formatted routes configurations (path and component properties).&lt;/li&gt;
&lt;li&gt;Prefix must be provided while providing the input to NgFate (it is case sensitive).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Algorithm and Data Structure
&lt;/h3&gt;

&lt;p&gt;The NgFate is working on below base algorithm right now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;get all ‘.module.ts’ files&lt;/li&gt;
&lt;li&gt;get all ‘declarations’ array data from NgModules&lt;/li&gt;
&lt;li&gt;decompose fileName based on camelCasing of name&lt;/li&gt;
&lt;li&gt;get file path by search fileName in the directory&lt;/li&gt;
&lt;li&gt;read ‘*-routing.module.ts’ file and search name of component if existed&lt;/li&gt;
&lt;li&gt;get the path name while searching for component in routing file&lt;/li&gt;
&lt;li&gt;search for ‘header.component.html’ file in entire project and get the .html parent files&lt;/li&gt;
&lt;li&gt;decompose fileName from name&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To support the above algorithm below data structure is at core of the tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[
  // 1. get all '.module.ts' files
  {
    name: 'AppModule',
    fileName: 'app.module.ts',
    components: [
      // 2. get all 'declarations' array data from NgModules
      {
        name: 'LoginComponent',
        fileName: 'login.component.ts', // 3. decompose fileName based on camelCasing of name
        filePath: 'src/app/pages/login.component.ts', // 4. get file path by search fileName in the directory
        routed: true, // 5. read '*-routing.module.ts' file and search name of component if existed
        routePath: 'login', // 6. get the path name while searching for component in routing file
        parents: [],
      },
      {
        name: 'HeaderComponent',
        fileName: 'header.component.ts',
        filePath: 'src/app/components/header.component.ts',
        routed: false, // 5. read '*-routing.module.ts' file and search name of component if existed
        routePath: null,
        parents: [
          // 7. search for 'header.component.html' file in entire project and get the .html parent files
          {
            name: 'MainComponent',
            fileName: 'main.component.ts', // 8. decompose fileName from name
            filePath: 'src/app/components/main.component.ts', // 4. get file path by search fileName in the directory
          },
        ],
      },
    ],
  },
];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Future Scope
&lt;/h3&gt;

&lt;p&gt;For the interstellar travel of NgFate below are some key features considered as a future scope for the tool:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Diagrams:&lt;/li&gt;
&lt;li&gt;1. Dependency graph in .html report.&lt;/li&gt;
&lt;li&gt;2. Routing graph in .html report.&lt;/li&gt;
&lt;li&gt;Pipes and directives considerations.&lt;/li&gt;
&lt;li&gt;Version Support: Adhering to the latest releases of Angular.&lt;/li&gt;
&lt;li&gt;AI support for speeding up performance.&lt;/li&gt;
&lt;li&gt;Fault tolerance and better logging.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Git Repository
&lt;/h3&gt;

&lt;p&gt;Check out the git repository for this project or download the code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ZakiMohammed/ng-fate/releases"&gt;Application&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ZakiMohammed/ng-fate/archive/master.zip"&gt;Download Code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ZakiMohammed/ng-fate"&gt;Git Repository&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;This is just an inception of NgFate, many more releases yet to come to make it with stand with lot of exceptional scenarios; those are highlighted in the future scope. The project is available on GitHub and maintained by CodeOmelet. Looking forward to having more developers to jam in and make this tool more and more mature. Hope this tool help others and bring joy to their life and families.&lt;/p&gt;

&lt;p&gt;Hope this article helps.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at&lt;/em&gt; &lt;a href="https://codeomelet.com/posts/ng-fate-a-tool-for-deciding-the-fate-of-an-angular-app"&gt;&lt;em&gt;https://codeomelet.com&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cli</category>
      <category>dotnet</category>
      <category>csharp</category>
      <category>angular</category>
    </item>
    <item>
      <title>Cache Angular HTTP request using @ngneat/cashew</title>
      <dc:creator>Zaki Mohammed</dc:creator>
      <pubDate>Thu, 25 May 2023 16:21:50 +0000</pubDate>
      <link>https://dev.to/zakimohammed/cache-angular-http-request-using-ngneatcashew-4pd9</link>
      <guid>https://dev.to/zakimohammed/cache-angular-http-request-using-ngneatcashew-4pd9</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HWEb331a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/1%2A4BRAL2zK85BdcFoQpQb09g.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HWEb331a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/1%2A4BRAL2zK85BdcFoQpQb09g.jpeg" alt="" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The time of not wasting time on caching HTTP request data has come. Presenting @ngneat/cashew package to do the needfully; it takes our stress of manually creating service variables to hold up the HTTP requests data again and again. In this article we will explore @ngneat/cashew dope package for caching HTTP requests.&lt;/p&gt;

&lt;p&gt;In any Angular project we create HTTP services to make API calls GET, POST, etc. to operate on server data. Now, in order to preserve the loaded data, we usually create service variables to hold up the data until user make a reload or login again into the system. For this we have to maintain this variable with our bare hand which kind of repetitive task for each newly created service. Storing the response data like forever is not what we usually get paid for. Here, comes super chill &lt;a href="https://www.npmjs.com/package/@ngneat/cashew"&gt;@ngneat/cashew&lt;/a&gt; package!&lt;/p&gt;

&lt;p&gt;We will head in this direction:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Setup Angular App&lt;/li&gt;
&lt;li&gt;Add @ngneat/cashew&lt;/li&gt;
&lt;li&gt;Add to HTTP service&lt;/li&gt;
&lt;li&gt;Use LocalStorage&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Setup Angular App
&lt;/h3&gt;

&lt;p&gt;Let us first do the daily chores of any Angular app and as per the need of the hour we are considering v16 of Angular; just for fun we are using Cirrus UI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng new ng-cashew-app

npm i cirrus-ui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Below shows the skeleton of the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng-cashew-app
|-- src
| |-- app
| | |-- core
| | | |-- components
| | | | |-- footer
| | | | |-- header
| | | |-- pages
| | | | |-- home
| | | |-- services
| | | |-- core.service.ts
| | |-- modules
| | | |-- posts
| | | |-- users
| | |-- app-routing.module.ts
| | |-- app.component.html
| | |-- app.component.ts
| | |-- app.module.ts
|-- angular.json
|-- package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This application respect minimalism and have less number of routes and components. For the sake of understanding how compression affects the lazy loaded modules we have added 2 feature standalone modules Posts and Users. Thanks to Angular v16 these are standalone components routed in a lazy loaded fashion. The data is coming from our beloved JSON Placeholder API.&lt;/p&gt;

&lt;p&gt;Below shows the business logic for Posts component, the Users component is also created in the similar manner:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;posts.component.html&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="section"&amp;gt;
  &amp;lt;div class="content"&amp;gt;
    &amp;lt;div class="row mb-3" *ngFor="let post of posts$ | async"&amp;gt;
      &amp;lt;div class="col-sm-4"&amp;gt;
        &amp;lt;img
          class="img-stretch u-round-md"
          [src]="getImage(post)"
          alt="img-stretch"
          height="400"
          (error)="getDefaultImage($event)" /&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;div class="col-lg-6 col-md-8"&amp;gt;
        &amp;lt;h3&amp;gt;{{ post.title | titlecase }}&amp;lt;/h3&amp;gt;
        &amp;lt;p&amp;gt;{{ post.body }}&amp;lt;/p&amp;gt;
        &amp;lt;div&amp;gt;
          &amp;lt;span class="icon"&amp;gt;
            &amp;lt;i class="far fa-wrapper fa-clock" aria-hidden="true"&amp;gt;&amp;lt;/i&amp;gt;
          &amp;lt;/span&amp;gt;
          {{randomDate | date: 'fullDate'}}
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;posts.component.ts&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;posts$?: Observable;

constructor(private coreService: CoreService) {
  this.posts$ = coreService.getPosts();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;core.service.ts&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private url: string = environment.apiUrl;
private postUrl: string = 'posts';
private userUrl: string = 'users';

constructor(private http: HttpClient) {}

getPosts() {
  const url = `${this.url}${this.postUrl}?userId=1`;
  return this.http.get(url);
}

getUsers() {
  const url = `${this.url}${this.userUrl}`;
  return this.http.get(url);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;app-routing.module.ts&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const routes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'posts', loadComponent: () =&amp;gt; import('./modules/posts/posts.component').then(x =&amp;gt; x.PostsComponent) },
  { path: 'users', loadComponent: () =&amp;gt; import('./modules/users/users.component').then(x =&amp;gt; x.UsersComponent) },
  { path: '**', component: HomeComponent },
];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Done with the chores!&lt;/p&gt;

&lt;h3&gt;
  
  
  Add @ngneat/cashew
&lt;/h3&gt;

&lt;p&gt;Now, let us install @ngneat/cashew 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 i @ngneat/cashew
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adding cashew to app module:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;app.module.ts&lt;/strong&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 { HttpCacheInterceptorModule } from '@ngneat/cashew';

@NgModule({
  declarations: [...],
  imports: [
    ...
    HttpCacheInterceptorModule.forRoot(), 
  ],
  providers: [],
  bootstrap: [...],
})
export class AppModule {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add to HTTP service
&lt;/h3&gt;

&lt;p&gt;Time to add the cashew config to the HTTP options:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;core.service.ts&lt;/strong&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 { withCache } from '@ngneat/cashew';
...

private options = {
  context: withCache()
};

getPosts() {
  const url = `${this.url}${this.postUrl}?userId=1`;
  return this.http.get(url, this.options);
}

getUsers() {
  const url = `${this.url}${this.userUrl}`;
  return this.http.get(url, this.options);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the options object holds the context property which is coming from the withCache() method of cashew package. We have added this object as second parameter in the HTTP’s get method for post and users endpoints.&lt;/p&gt;

&lt;p&gt;Just simply adding withCache() method gives your service methods wings of cache. If you check the network tab you will only find a single call being made to the endpoint, and if you revisit the pages after navigating to some other route, you won’t find any calls to the server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use LocalStorage
&lt;/h3&gt;

&lt;p&gt;Another question your senior dev in your team will ask is, what about if I refresh the page, will it persist the data in cache? You simply need to assure them that, we need to configure @ngneat/cashew to use localStorage instead of run time memory. This is as simple as eating an omelet (omelet reference!).&lt;/p&gt;

&lt;p&gt;We need to add “useHttpCacheLocalStorage” to the providers of app module:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;app.module.ts&lt;/strong&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 { HttpCacheInterceptorModule, useHttpCacheLocalStorage } from '@ngneat/cashew';

@NgModule({
  declarations: [...],
  imports: [...],
  providers: [useHttpCacheLocalStorage],
  bootstrap: [...],
})
export class AppModule {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Last thing is adding version and key to the “withCache” method’s object:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;core.service.ts&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

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

private options = {
  context: withCache({
      version: 'v1',
      key: 'omelet'
    })
};

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

&lt;/div&gt;



&lt;p&gt;Done! Thats it, now we can see the localStorage for this change. Just go to either “posts” or “users” route and check the “Application” tab of developer too and go to “Local Storage”. You will see something like this in your local storage.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HpBZdPxd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2AflbrMKNn8VHjyEYj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HpBZdPxd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2AflbrMKNn8VHjyEYj.png" alt="" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Additionally, we can set the “ttl” (in milliseconds) for this, by default it is for 1 hour. The version is a trick to start using a new local storage as a cache to disband the old cache.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private options = {
  context: withCache({
      version: 'v1',
      key: 'omelet',
      ttl: 3000
    })
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Git Repository
&lt;/h3&gt;

&lt;p&gt;Check out the git repository for this project or download the code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ng-cashew-app.netlify.app/"&gt;Application&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ZakiMohammed/ng-cashew-app/archive/master.zip"&gt;Download Code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ZakiMohammed/ng-cashew-app"&gt;Git Repository&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;Caching is developer’s love and if it can happen in frontend too with this much of little effort, it’s a salvation! You can clearly feel that how a fan boy I am for this package. This actually given us good grades in our performance audits. You can simply consider @ngneat/cashew as a plug and plaything. It also has some hackable areas, worth giving a try, providing full blown customization. Consider this a performance boosting thing for your next or current app.&lt;/p&gt;

&lt;p&gt;Hope this article helps.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at&lt;/em&gt; &lt;a href="https://codeomelet.com/posts/cache-angular-http-request-using-ngneat-cashew"&gt;&lt;em&gt;https://codeomelet.com&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cache</category>
      <category>angular</category>
      <category>ngneatcashew</category>
      <category>api</category>
    </item>
    <item>
      <title>Nginx Cache Config for Dockerized Angular App — NgDocker</title>
      <dc:creator>Zaki Mohammed</dc:creator>
      <pubDate>Wed, 17 May 2023 14:08:54 +0000</pubDate>
      <link>https://dev.to/zakimohammed/nginx-cache-config-for-dockerized-angular-app-ngdocker-3473</link>
      <guid>https://dev.to/zakimohammed/nginx-cache-config-for-dockerized-angular-app-ngdocker-3473</guid>
      <description>&lt;h3&gt;
  
  
  Nginx Cache Config for Dockerized Angular App — NgDocker
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3-1sIdps--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/1%2AZMLtVuxGQ6OGcJaxTgMF_Q.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3-1sIdps--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/1%2AZMLtVuxGQ6OGcJaxTgMF_Q.jpeg" alt="Unsplash" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;App running so fast, even the cops are chasing! Not breaking any law here just configuring Nginx caching for our Dockerized Angular app. In this article we will address the need for speed for our super silly NgDocker app by setting up the Nginx cache configurations for static files.&lt;/p&gt;

&lt;p&gt;We are on a spiritual journey of performance improvement for a Dockereized Angular app. In continuation of the previous article &lt;a href="https://codeomelet.com/posts/gzip-dockerized-angular-app-with-nginx-ngdocker"&gt;Gzip Dockerized Angular App with Nginx — NgDocker&lt;/a&gt; where we have already enabled the Gzip and seen a significant performance boost for the static files; we will use the same app for this article too and add the cache control header to the static files by configuring this setting in Nginx config.&lt;/p&gt;

&lt;p&gt;We will head in this direction:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Cache Control Header&lt;/li&gt;
&lt;li&gt;Add Cache Control in Nginx Configuration&lt;/li&gt;
&lt;li&gt;Investigating the Cache Control&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Cache Control Header
&lt;/h3&gt;

&lt;p&gt;The Cache Control is a response header, that can be set from the server side; it tells the browser whether the file needs to be cached or not. As per the MDN documentation for the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control"&gt;Cache-Control&lt;/a&gt;, we need to add the cache control header to the respective files. Once this header is present the browser then simply caches the files till the expiry provided in the header. For this we need to provide the value “max-age=31449600” to the cache control header. Here, the max-age value is set in seconds; so “31449600” is approximately 365 days, roughly a year. Through the same cache control header, we can even tell the browser to not cache some of the file by setting the value for cache control to “no-cache”. We will be needing this control to decide whether to cache or not cache some of the files as per our app needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add Cache Control in Nginx Configuration
&lt;/h3&gt;

&lt;p&gt;The time has come to add the majestic cache configuration to the Nginx config file “nginx/nginx.conf”. Consider these settings can be used for any other SPA application too like React, Vue, etc. We will add different configuration for different static files:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Index File (index.html)&lt;/li&gt;
&lt;li&gt;JavaScript and CSS Files (.js/.css)&lt;/li&gt;
&lt;li&gt;Image Files (.jpg, .png, .svg, etc.)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;1. Index File (index.html)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We never cache the index.html file otherwise it will never provide the updated application results when we deploy a new build. So for “index.html” it is must to not cache and always fetched from the server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
    ...
    location ~ /index.html {
        add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
    }
    ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will ensure to disable the caching for index.html by the browser. Read more about these directives here: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#directives"&gt;Cache-Control Directives&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. JavaScript and CSS Files (.js/.css)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cache JS/CSS for a year since every new build will have its own build UUID the JS/CSS files in a newer build will never be the same. So, till the time there is no new build deployed the JS/CSS files once fetched on client-side will never be fetched again until a new build is deployed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
    ...
    location ~ .*\.css$|.*\.js$ {
        add_header Cache-Control 'max-age=31449600'; # one year
    }
    ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Image Files (.jpg, .png, .svg, etc.)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The image files we are talking here are mostly from the “assets” folder. These images are mainly related to the UI and App, example: icons, background, avatar, etc. These images are static images for client-side, those can be cached for one month without any problem. This can differ application to application, set as per your app needs and requirement. For this article we are setting it up to one month.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
    ...
    location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
        expires 1M; # one month
    }
    ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can even use “expires” value too in Nginx for location directive.&lt;/p&gt;

&lt;h3&gt;
  
  
  Investigating the Cache
&lt;/h3&gt;

&lt;p&gt;After building the Docker image and running the Docker container let us investigate if the things are aligning or not as per our expectations. We will use Firefox as it shows the transferred file source correctly as compared to Chrome; from where it has been served, from server or browser cache. For this we will first hard-reload (Ctrl + F5) the app and observe the Developer Tool’s Network tab; then we will reload (F5) the app and again observe the Network tab to see the source of the transferred file either server or cache.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Docker: Build and Run&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker build -t ng-docker:mark-3 
docker run -p 3300:80 --name ng-docker-mark-3-container-1 ng-docker:mark-3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Initial Load or Hard-Reload (Ctrl + F5)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3jPHzJMS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2AzxzDbABLtn-txupd.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3jPHzJMS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2AzxzDbABLtn-txupd.jpg" alt="Initial Load or Hard-Reload" width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, the “Transferred” column shows the size of the transferred file, indicating the file has been transferred from server and not coming from browser cache.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reload (F5)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nHsKAzup--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2AKyyAPTdNloL7We9r.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nHsKAzup--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2AKyyAPTdNloL7We9r.jpg" alt="Reload " width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, the “Transferred” column shows the “cached”, indicating the file has been served from browser cache.&lt;/p&gt;

&lt;p&gt;If we look it more closely, we can see that the cache control header is appearing as Response Header once we clicked on the file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Index File&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LXHWXuwZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2AA0akMtKV4B3TgDvs.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LXHWXuwZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2AA0akMtKV4B3TgDvs.jpg" alt="Index File" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As expected the “index.html” file is appearing as “no-cache” header value.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JavaScript Files&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WydQ8rr1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2AC4LcKRemAQY-LC4W.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WydQ8rr1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2AC4LcKRemAQY-LC4W.jpg" alt="JavaScript Files" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As expected the “.js” files are appearing as “max-age” header value.&lt;/p&gt;

&lt;p&gt;Checkout the entire nginx.conf file below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;nginx/nginx.conf&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
    gzip on;
    gzip_static on;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
    gzip_proxied no-cache no-store private expired auth;
    gzip_min_length 1000;

    listen 80;

    root /usr/share/nginx/html;

    location ~ /index.html {
        add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'; # no cache
    }

    location ~ .*\.css$|.*\.js$ {
        add_header Cache-Control 'max-age=31449600'; # one year
    }

    location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
        expires 1M; # one month
    }

    location / {
        index index.html index.htm;
        try_files $uri $uri/ /index.html;
    }

    error_page 500 502 503 504 /50x.html;

    location = /50x.html {
        root /usr/share/nginx/html;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Git Repository
&lt;/h3&gt;

&lt;p&gt;Check out the git repository for this project or download the code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ZakiMohammed/ng-docker-mark-3/archive/master.zip"&gt;Download Code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ZakiMohammed/ng-docker-mark-3"&gt;Git Repository&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;Caching is some default feature that is available by default with HTTP standard. We simply need to press some keys on the keyboard to get this properly configured for our SPA application. We can introduce as many flavors to it as we want to depend upon our app requirements. Nginx syntax is really simple and straight forward. So, without much hassle we can enable some pre-built functionality that will help the loading speed. Although this won’t be helping on your initial load but surely relives the subsequent requests. The performance improvement battle is never ending, stay tuned!&lt;/p&gt;

&lt;p&gt;Hope this article helps.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at&lt;/em&gt; &lt;a href="https://codeomelet.com/posts/nginx-cache-config-for-dockerized-angular-app-ngdocker"&gt;&lt;em&gt;https://codeomelet.com&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>cache</category>
      <category>nginx</category>
      <category>cachecontrol</category>
    </item>
    <item>
      <title>Gzip Dockerized Angular App with Nginx — NgDocker</title>
      <dc:creator>Zaki Mohammed</dc:creator>
      <pubDate>Sat, 08 Apr 2023 09:17:28 +0000</pubDate>
      <link>https://dev.to/zakimohammed/gzip-dockerized-angular-app-with-nginx-ngdocker-3c2b</link>
      <guid>https://dev.to/zakimohammed/gzip-dockerized-angular-app-with-nginx-ngdocker-3c2b</guid>
      <description>&lt;h3&gt;
  
  
  Gzip Dockerized Angular App with Nginx — NgDocker
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DyGISz7y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/1%2AkqP4ScQ7VapPryRFuKEQcg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DyGISz7y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/1%2AkqP4ScQ7VapPryRFuKEQcg.jpeg" alt="" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Leveling up by taking performance improvement measures for our super friendly NgDocker app. In this article, we will explore how we can enable Gzip to improve performance at a hulking scale.&lt;/p&gt;

&lt;p&gt;This article is sort of continuation of the previous article &lt;a href="https://codeomelet.com/posts/dockerized-angular-app-with-nginx-ngdocker"&gt;Dockerized Angular App with Nginx — NgDocker&lt;/a&gt; with some added features and modules. The idea is to enable the Gzip compression at Nginx configuration in order to make application loading blazingly fast. The Gzip compression is must for any SPA/API developed on server in order to reduce the data transmission up to 80%. It is important to understand how to enable and control the configurations related to Gzip to make any SPA based application stand out. For this we are creating an Angular application on the same base line of NgDocker app, this will be Mark 2 of NgDocker app.&lt;/p&gt;

&lt;p&gt;We will head in this direction:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Setup Angular App&lt;/li&gt;
&lt;li&gt;Enable Gzip in Nginx Configuration&lt;/li&gt;
&lt;li&gt;Run Docker Container&lt;/li&gt;
&lt;li&gt;Celebrate the Impact of Gzip&lt;/li&gt;
&lt;li&gt;Analyzing the Bundle — Web Pack Bundle Analyzer&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Setup Angular App
&lt;/h3&gt;

&lt;p&gt;Let us first do the daily chores of any Angular app and as per the need of the hour we are considering v15 of Angular; just for fun we are using Cirrus UI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng new ng-docker-mark-2

npm i cirrus-ui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Below shows the skeleton of the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng-docker-mark-2
|-- nginx
| |-- nginx.conf
|-- src
| |-- app
| | |-- core
| | | |-- components
| | | | |-- footer
| | | | |-- header
| | | |-- pages
| | | | |-- home
| | | |-- services
| | | |-- core.service.ts
| | | |-- products.service.ts
| | |-- modules
| | | |-- posts
| | | |-- users
| | |-- app-routing.module.ts
| | |-- app.component.html
| | |-- app.component.ts
| | |-- app.module.ts
|-- angular.json
|-- Dockerfile
|-- package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This application respect minimalism and have less number of routes and components. For the sake of understanding how compression affects the lazy loaded modules we have added 2 feature standalone modules Posts and Users. Thanks to Angular v15 these are standalone components routed in a lazy loaded fashion. The data is coming from our beloved JSON Placeholder API.&lt;/p&gt;

&lt;p&gt;Below shows the business logic for Posts component, the Users component is also created in the similar manner:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;posts.component.html&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="section"&amp;gt;
  &amp;lt;div class="content"&amp;gt;
    &amp;lt;div class="row mb-3" *ngFor="let post of posts$ | async"&amp;gt;
      &amp;lt;div class="col-sm-4"&amp;gt;
        &amp;lt;img
          class="img-stretch u-round-md"
          [src]="getImage(post)"
          alt="img-stretch"
          height="400"
          (error)="getDefaultImage($event)" /&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;div class="col-lg-6 col-md-8"&amp;gt;
        &amp;lt;h3&amp;gt;{{ post.title | titlecase }}&amp;lt;/h3&amp;gt;
        &amp;lt;p&amp;gt;{{ post.body }}&amp;lt;/p&amp;gt;
        &amp;lt;div&amp;gt;
          &amp;lt;span class="icon"&amp;gt;
            &amp;lt;i class="far fa-wrapper fa-clock" aria-hidden="true"&amp;gt;&amp;lt;/i&amp;gt;
          &amp;lt;/span&amp;gt;
          {{randomDate | date: 'fullDate'}}
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;posts.component.ts&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;posts$?: Observable;

constructor(private coreService: CoreService) {
  this.posts$ = coreService.getPosts();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Below shows the post service, the user service is also created in similar manner:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;products.service.ts&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private url: string = environment.apiUrl;

constructor(private http: HttpClient) {}

get(name: string) {
  const url = `${this.url}${name}`;
  return this.http.get(url);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Below shows the routes of the application:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;app-routing.module.ts&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const routes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'posts', loadComponent: () =&amp;gt; import('./modules/posts/posts.component').then(x =&amp;gt; x.PostsComponent) },
  { path: 'users', loadComponent: () =&amp;gt; import('./modules/users/users.component').then(x =&amp;gt; x.UsersComponent) },
  { path: '**', component: HomeComponent },
];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Enable Gzip in Nginx Configuration
&lt;/h3&gt;

&lt;p&gt;The time has come to enable Gzip in Nginx configuration file which we have seen in the previous article for re-writing the route for SPA:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
    gzip on;
    gzip_static on;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
    gzip_proxied no-cache no-store private expired auth;
    gzip_min_length 1000;
    ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it, no need to check or look anywhere else, this much is required for speeding up your app performance! Also, these settings are self explanatory, the first 2 lines enables the Gzip and Gzip for static files, followed by mentioning of types for which the Gzip will be enabled (note: no need to compress already compressed images like JPG), then we are keeping the gzip_proxied to the default setting and then finally setting the min length for Gzip consideration, right now we have gone with 1000 means 1KB, files below 1KB won’t be compressed then, we can make it 256 too optionally to consider even smaller than 1KB file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Run Docker Container
&lt;/h3&gt;

&lt;p&gt;For this we require to have Docker Desktop, otherwise we are unworthy to proceed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build Docker Image&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# build image
docker build -t ng-docker:mark-2 .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Run Docker Container&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# run container
docker run -p 3300:80 --name ng-docker-mark-2-container-1 ng-docker:mark-2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, you can change to your favorite port, we are going with “3300” for a change. Once, executed then open &lt;code&gt;http://localhost:3300/&lt;/code&gt; to run the application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Celebrate the Impact of Gzip
&lt;/h3&gt;

&lt;p&gt;Its time for the celebration! Due to drastic improvement in the performance. Here, we will compare the time to load files with and without Gzip through below screen shots:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gzip Disabled (before)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eAYA4aHg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2AYXTWFbWvOQr3V6tj.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eAYA4aHg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2AYXTWFbWvOQr3V6tj.jpg" alt="" width="800" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gzip Enabled (after)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZnwAJdS7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2ATJqXjl2Nbsu9jxpx.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZnwAJdS7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2ATJqXjl2Nbsu9jxpx.jpg" alt="" width="800" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, we can clearly see the before and after effect on the file size, the biggest file (main.js) early which was around 256KB turns out to be of 95.5KB of size after enabling the Gzip. The snowball effect can clearly be seen in other files too. The “Content-Encoding” header usually doesn’t appear in the browser’s Network tab of Developer Tools, we can add it by simply right clicking on the table of Network tab and go to “Headers” option and then add the “Content-Encoding”.&lt;/p&gt;

&lt;p&gt;Similar effect can also be observed with lazy-loaded modules:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lazy Loaded Module — Gzip Disabled (before)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TG3Us1Ij--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2ARBXVkxKLP6e4Wxjs.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TG3Us1Ij--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2ARBXVkxKLP6e4Wxjs.jpg" alt="" width="800" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lazy Loaded Module — Gzip Enabled (after)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1dcME4TT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2AAniZSrIX-KeAXAh8.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1dcME4TT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2AAniZSrIX-KeAXAh8.jpg" alt="" width="800" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Analyzing the Bundle — Web Pack Bundle Analyzer
&lt;/h3&gt;

&lt;p&gt;We can analyze our modules and bundle size in a more better way with the help of &lt;a href="https://www.npmjs.com/package/webpack-bundle-analyzer"&gt;Web Pack Bundle Analyzer&lt;/a&gt;. This package helps to provide a breakdown of the entire Angular project and the size of the bundle in Stats, Parsed and Gzipped state. The Stats is the default build without production, the Parsed is the production build with minified JS and CSS and finally the Gzipped state is the production build with Gzip enabled. This will give a very clear and early picture of your project, how much it has grown, which modules are heavier, how to keep main.js minimal as much as we can. With the decision making becomes very easy peasy.&lt;/p&gt;

&lt;p&gt;We first have to install the Web Pack Bundle Analyzer, make sure to add it as Dev Dependency, since it just a tool to analyze the project bundle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i webpack-bundle-analyzer -D
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add scripts to package.json file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"scripts": {
  "build:prod-stats": "ng build --configuration production --stats-json",
  "analyze": "npm run build:prod-stats &amp;amp;&amp;amp; webpack-bundle-analyzer dist/ng-docker-mark-2/stats.json",
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we are adding one new “build:prod-stats” production build command with “stats-json” flag, this will create “stats.json” file once the build is completed. This file is then used by the next command of Web Pack Bundle Analyzer. The script “analyze” clubbed the build stats and analyzer commands.&lt;/p&gt;

&lt;p&gt;Run Web Pack Bundle Analyzer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run analyze
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will open a web page to show the project bundle graph as follows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Xh3GqxHu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2AF3TCx7ydhWvsD1w7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Xh3GqxHu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2AF3TCx7ydhWvsD1w7.jpg" alt="" width="800" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, we can see the entire project and its module and the feature modules with their respective sizes. If you notice the left side bar it shows the 3 states that we have discussed earlier.&lt;/p&gt;

&lt;p&gt;This will open a web page to show the project bundle graph as follows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UEWNM9HD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2AUfB6twS_TUO9Yb1P.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UEWNM9HD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/0%2AUfB6twS_TUO9Yb1P.jpg" alt="" width="800" height="347"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, we can see the 3 states, and finally the Gzipped version of the JS files. If we notice the sizes its not fully accurate what we finally got on the application while running it, like the main.js Parsed is 254KB and while the actual is 256KB and the Gzipped is 78.53KB while we got 95.5KB; so, its not pixel perfect but at least on a closer proximity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Git Repository
&lt;/h3&gt;

&lt;p&gt;Check out the git repository for this project or download the code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ZakiMohammed/ng-docker-mark-2/archive/master.zip"&gt;Download Code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ZakiMohammed/ng-docker-mark-2"&gt;Git Repository&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;Performance is a pain in project development if not considered prior. Some of the performance are handled direct at the configuration levels rather than code and business logic; Gzip is one of the configuration level change that brings performance we crave for in our application. If you are having SPA which doesn’t have Gzip enabled then it’s a huge loss on performance, and people will judge us, as someone form Dark Age. The configuration for Gzip is dead simple and can be tested and verified easily (bye-bye QA).&lt;/p&gt;

&lt;p&gt;Another, hidden gem which we have disclosed in this article is the Web Pack Bundle Analyzer. This dude helps us to monitor our project growth and bulky areas of project upfront and allow us to take prior actions before thing gets out of hand. So that being said, enable Gzip in your app and we will see more future articles on the same line, stay tuned!&lt;/p&gt;

&lt;p&gt;Hope this article helps.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at&lt;/em&gt; &lt;a href="https://codeomelet.com/posts/gzip-dockerized-angular-app-with-nginx-ngdocker"&gt;&lt;em&gt;https://codeomelet.com&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>gzip</category>
      <category>angular</category>
      <category>gzipcompression</category>
      <category>webpackbundleanalyze</category>
    </item>
    <item>
      <title>Dockerized Angular App with Nginx — NgDocker</title>
      <dc:creator>Zaki Mohammed</dc:creator>
      <pubDate>Sun, 26 Mar 2023 18:49:17 +0000</pubDate>
      <link>https://dev.to/zakimohammed/dockerized-angular-app-with-nginx-ngdocker-bl3</link>
      <guid>https://dev.to/zakimohammed/dockerized-angular-app-with-nginx-ngdocker-bl3</guid>
      <description>&lt;h3&gt;
  
  
  Dockerized Angular App with Nginx — NgDocker
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ghDf0Qsh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/0%2AsEKjAIxaj-jvZ_RS.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ghDf0Qsh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/0%2AsEKjAIxaj-jvZ_RS.jpg" alt="" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Meet super friendly NgDocker app, made with love and care using Angular and Cirrus UI, garnished with Docker and Nginx. In this article, we will explore how to make and bake a Dockerized Angular App with Nginx without much hassle.&lt;/p&gt;

&lt;p&gt;If we already know about wonders Docker brings to the table then we are good to go ahead with the article, but if we have any doubt related to Docker (what, why and how) then the pre-requisite here is to know the background details of Docker. Here, we will talk about how to setup a minimalist Dockerized Angular application along with Nginx configurations. The idea is to keep it dead simple and explore what things are needed to make Angular App as Dockerized. Enough said lets get started with the code already!&lt;/p&gt;

&lt;p&gt;We will head in this direction:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Setup Angular App&lt;/li&gt;
&lt;li&gt;Add Production Build Command&lt;/li&gt;
&lt;li&gt;Setup Docker and Nginx&lt;/li&gt;
&lt;li&gt;Build Docker Image and Run Docker Container&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Setup Angular App
&lt;/h3&gt;

&lt;p&gt;Let us first do the daily chores of any Angular app and as per the need of the hour we are considering v15 of Angular; just for fun we are using Cirrus UI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng new ng-docker-mark-1

npm i cirrus-ui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As already mentioned we will do bare minimum with the Angular project:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;app.component.html&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;section class="section"&amp;gt;
  &amp;lt;div class="hero fullscreen bg-gray-100"&amp;gt;
    &amp;lt;div class="hero-body"&amp;gt;
      &amp;lt;div class="content"&amp;gt;
        &amp;lt;div class="row"&amp;gt;
          &amp;lt;div class="col text-center"&amp;gt;
            &amp;lt;h1&amp;gt;?? Hello &amp;lt;span class="text-red-700"&amp;gt;Ng&amp;lt;/span&amp;gt;&amp;lt;span class="text-blue-500"&amp;gt;Docker&amp;lt;/span&amp;gt;!&amp;lt;/h1&amp;gt;
            &amp;lt;h6 class="font-alt font-light"&amp;gt;
              A webpage powered by
              &amp;lt;b&amp;gt;
                &amp;lt;i class="fab fa-angular text-red-700"&amp;gt;&amp;lt;/i&amp;gt; Angular + &amp;lt;i class="fab fa-docker text-blue-500"&amp;gt;&amp;lt;/i&amp;gt; Docker
                + &amp;lt;i class="fas fa-cloud text-pink-600"&amp;gt;&amp;lt;/i&amp;gt; Cirrus.
              &amp;lt;/b&amp;gt;
            &amp;lt;/h6&amp;gt;
            &amp;lt;div class="tag-container group-tags group-tags--rounded mt-2"&amp;gt;
              &amp;lt;div class="tag tag--dark"&amp;gt;Mark&amp;lt;/div&amp;gt;
              &amp;lt;div class="tag tag--info"&amp;gt;1.0.0&amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
          &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add Production Build Command
&lt;/h3&gt;

&lt;p&gt;Adding the build command for production in “package.json” file&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;package.json&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "scripts": {
    "build:prod": "ng build --configuration production",
  },
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setup Docker and Nginx
&lt;/h3&gt;

&lt;p&gt;Before we address the Docker whale in the project, we first create Nginx configuration file, later this configuration file will overrides the default Nginx configuration. Create a folder for this file named “nginx” and create a file named “nginx.conf” inside that folder.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;nginx/nginx.conf&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
    listen 80;

    root /usr/share/nginx/html;

    location / {
        index index.html index.htm;
        try_files $uri $uri/ /index.html;
    }

    error_page 500 502 503 504 /50x.html;

    location = /50x.html {
        root /usr/share/nginx/html;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Through this file we declare the re-write rule for the SPA application. The line “try_files $uri $uri/ /index.html;” makes the route to always be redirected to the “index.html”, since the SPA serves everything through the index page only. This file is very important to setup security headers, implement caching, enabling Gzip etc. Basically, this file is same as “web.config” file of ASP.NET project.&lt;/p&gt;

&lt;p&gt;The time has come to create our own “Dockerfile” and surprisingly the name of the file is itself “Dockerfile”. Create this file on your root of Angular project directory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dockerfile&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

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

ADD package.json .

RUN npm install

ADD . .

RUN npm run build:prod

FROM nginx:alpine

COPY --from=build-env /app/dist/ng-docker-mark-1 /usr/share/nginx/html
COPY nginx/nginx.conf /etc/nginx/conf.d/default.conf

EXPOSE 8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, when I first seen this file, I got little nervous, like what kind of Egyptian literature is this. But seems pretty simple to digest and get along with, let me break it down. This can be simply broken down into 2 major steps:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Installing Dependencies and Building Project&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM node:latest as build-env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells that the image will be for Node project, since most of the SPA are NPM and Node dependent, this line acknowledges the same.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Here, we are naming our working directory as “/app”.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Now, we are first adding only the “package.json” file to the “/app” directory using ADD command, this is to reduce the adding cost, and if the install command fails it fails early without adding the entire project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RUN npm install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we are installing the NPM packages based on the configured “package.json” file with the help of RUN command.&lt;/p&gt;

&lt;p&gt;Once the package installation is succeeded and after doing yay! we then add all of the project files to the working directory “/app”.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RUN npm run build:prod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Post that we run the build prod command, the same command which we have added to “package.json” script.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Setup Nginx and Nginx Configuration&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;COPY --from=build-env /app/dist/ng-docker-mark-1 /usr/share/nginx/html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we copy the built Angular project to Nginx HTML directory. This makes the project ready to be accessed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;COPY nginx/nginx.conf /etc/nginx/conf.d/default.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we will override the default Nginx configuration to the configuration that we have created in our Angular project “nginx/nginx.conf”.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Finally, exposing it to the default port 8080.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build Docker Image and Run Docker Container
&lt;/h3&gt;

&lt;p&gt;We can take further steps if we have installed Docker Desktop already, otherwise we are unworthy to proceed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build Docker Image&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# build image
docker build -t ng-docker:mark-1 .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once, executed then check in the image list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# list images
docker image ls
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Run Docker Container&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# run container
docker run -p 3300:80 --name ng-docker-container-1 ng-docker:mark-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, you can change to your favorite port, we are going with “3300” for a change. Once, executed then open &lt;code&gt;http://localhost:3300/&lt;/code&gt; to run the application.&lt;/p&gt;

&lt;p&gt;Here, have some additional commands if failed in between; to stop and remove the existing container to create another one with same name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# stop container
docker stop ng-docker-container-1

# remove container
docker rm ng-docker-container-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Git Repository
&lt;/h3&gt;

&lt;p&gt;Check out the git repository for this project or download the code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ng-docker-mark-1.azurewebsites.net/"&gt;Application&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ZakiMohammed/ng-docker-mark-1/archive/master.zip"&gt;Download Code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ZakiMohammed/ng-docker-mark-1"&gt;Git Repository&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;Doing bare minimum from Angular front but exploring the Docker and Nginx was a good experience overall. Dockerized apps are trending for many obvious reasons and understanding them is what today’s demand. This majorly helps in deployment, setting up CI/CD, configuring different environments. After few initial hiccups one can gain control over Docker and Nginx configurations and provide necessary support to the actual Angular project. We will see more future articles on the same line, stay tuned!&lt;/p&gt;

&lt;p&gt;Hope this article helps.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at&lt;/em&gt; &lt;a href="https://codeomelet.com/posts/dockerized-angular-app-with-nginx-ngdocker"&gt;&lt;em&gt;https://codeomelet.com&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cirrusui</category>
      <category>angular</category>
      <category>docker</category>
      <category>nginx</category>
    </item>
    <item>
      <title>JEST Started with Unit Testing</title>
      <dc:creator>Zaki Mohammed</dc:creator>
      <pubDate>Sun, 19 Mar 2023 19:37:27 +0000</pubDate>
      <link>https://dev.to/zakimohammed/jest-started-with-unit-testing-48o2</link>
      <guid>https://dev.to/zakimohammed/jest-started-with-unit-testing-48o2</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nk3eyMQi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/1%2A3b6QryeH2KE_xEGBQN8zcg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nk3eyMQi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/1%2A3b6QryeH2KE_xEGBQN8zcg.jpeg" alt="" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Either you are in a deep love with unit testing or hating it to the core, but the bottom line is to do it anyway. So just like many things in our life, let us deal with it; in this article we will start exploring the realm of unit testing along with JEST.&lt;/p&gt;

&lt;p&gt;If you ask me what I think about unit testing in general, then I would say it’s another kind of drug that is available in the market. Don’t get me wrong, nothing negative here; but at first you hate it and cry in the initial phases. You don’t, and never want to judge your own code; because your code is super awesome and will never going to break. As a developer we are no different, we share the same initial thought process in our early phases of our coding journey. But a mature developer knows he/she is human after all, and definitely going to make mistakes; but rather than denying it accept and learn from our mistakes. Unit testing comes from that learning and realization only. So, in order to make our code robust and reliable we need to make it go through some harsh and soft test cases that helps to prove worthiness of written code. Enough of prep talk! Now if we are all on boarded to explore some ABCD of unit testing will start with the super popular &lt;a href="https://jestjs.io/"&gt;JEST&lt;/a&gt; unit testing framework.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jestjs.io/"&gt;JEST&lt;/a&gt; is a delightful JavaScript unit test framework, which is quite popular among the dev squad. In this article our goal will be to get started with JEST and understand the ecosystem and write a dumb unit test case that actually runs. So, if you are already familiar with JEST then this article might not be a right fit for you since you are already a meta! But surely it will make you feel nostalgic. Without further ado will get our hands dirty.&lt;/p&gt;

&lt;p&gt;We will gonna use Node.js for running our JavaScript code and of course pre-requisite will be Node.js to be avail in your system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup and Dependencies
&lt;/h3&gt;

&lt;p&gt;Create a fresh directory and initialize using NPM initialize command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir jest-started
cd jest-started

npm init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After providing necessary detailing to your package.json file its time to add JEST dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i jest -D 
npm i @types/jest -D
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure to install them as dev-dependencies -D, since unit testing is something for dev and not for production. Otherwise, it will increase the bundle size of your project at the time of production.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add Test Command
&lt;/h3&gt;

&lt;p&gt;For starter we are just adding one command to at least run our test case in the script property of package.json file. Later, we will explore some more version of the below command:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;package.json&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"scripts": {
  "test": "jest"
},
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Write Code
&lt;/h3&gt;

&lt;p&gt;Let’s be very gentle and write super simple program that a school student can write and understand:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;index.js&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const sum = (num1, num2) =&amp;gt; num1 + num2;

module.exports = {
  sum,
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Write Unit Test Case
&lt;/h3&gt;

&lt;p&gt;Its time to write a test case/spec for the sum function, for this create a file with same name “index.test.js” but ends with test.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;index.test,js&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { sum } = require('./index');

// test suite
describe('Index', () =&amp;gt; {
  // test spec
  it('should add two numbers', () =&amp;gt; {
    // arrange
    const expected = 30;

    // act
    const received = sum(10, 20);

    // assert
    expect(received).toBe(expected);
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we are writing a test suite using the &lt;a href="https://jestjs.io/docs/api#describename-fn"&gt;describe()&lt;/a&gt; and a test spec using &lt;a href="https://jestjs.io/docs/api#testname-fn-timeout"&gt;it()&lt;/a&gt; methods of JEST. Inside of the it() method, just like any other test case we are doing some arraignment of data, acting upon the code and finally doing an assert operation on our expectation using &lt;a href="https://jestjs.io/docs/expect"&gt;expect()&lt;/a&gt; method of JEST, followed by a matcher function &lt;a href="https://jestjs.io/docs/expect#tobevalue"&gt;toBe()&lt;/a&gt; to prove our expectation.&lt;/p&gt;

&lt;p&gt;Congratulations! You have written your first JavaScript unit test case code without dropping a single sweat or blood. Keeping the code this much only let us now explore the surroundings.&lt;/p&gt;

&lt;h3&gt;
  
  
  Run Test Command
&lt;/h3&gt;

&lt;p&gt;Now its time for the actual show down, run your unit test with the beautiful npm script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm test

&amp;gt; jest-started@1.0.0 test
&amp;gt; jest

 PASS ./index.test.js
  Index
    v should add two numbers (2 ms)

Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 0.511 s, estimated 1 s
Ran all test suites.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, it runs your test for your index.test.js file and provide you the passed/fail status. It shows the total passed test suits and specs. You can try to tweak the code by making the expected value to be different, this will show result as below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm test

&amp;gt; jest-started@1.0.0 test
&amp;gt; jest

 FAIL ./index.test.js
  Index
    × should add two numbers (3 ms)

  ? Index › should add two numbers

    expect(received).toBe(expected) // Object.is equality

    Expected: 40
    Received: 30

      12 |
      13 | // assert
    &amp;gt; 14 | expect(received).toBe(expected);
         | ^
      15 | });
      16 | });
      17 |

      at Object.toBe (index.test.js:14:22)

Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 0.68 s, estimated 1 s
Ran all test suites.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, clearly mentions what it was expected and what actually received along with the line number it failed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add More Test Commands
&lt;/h3&gt;

&lt;p&gt;JEST has a couple of options that come along with the default JEST command. These options allow us to make a command that suites our project needs and requirements. Let us create some new custom commands inside package.json file using these options:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;package.json&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"scripts": {
  "test": "jest",
  "test-watch": "jest --watchAll",
  "test-verbose": "jest --verbose",
  "test-coverage": "jest --coverage",
  "test-fat": "jest --watchAll --verbose --coverage"
},
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we have added a command called “test-watch” with option named “ — watchAll”, this will make the jest runs in the background to continuously watch your folder for any change in your test/spec files and runs the result. This one is pretty handy if you are doing continuous changes. The second command provides detailing to individual test specs even if you have multiple test files, otherwise the default command just shows the pass/fail status of each file. The coverage command generates a coverage report the get displayed on the console and also creates an entire HTML report ( &lt;a href="https://istanbul.js.org/"&gt;Istanbul Coverage&lt;/a&gt;) that helps to explore all of the test cases present inside a folder at project level. The last and final command is just for fun to have all of these options combined together. Explore more such CLI &lt;a href="https://jestjs.io/docs/cli"&gt;options&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Coverage Report
&lt;/h3&gt;

&lt;p&gt;A coverage report provides you an overall picture of unit test case coverage project/folder wide. This helps to determine missed statements, lines, branches and functions. By default, 80% or above coverage is a green.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run test-coverage

&amp;gt; jest-started@1.0.0 test-coverage
&amp;gt; jest --coverage

 PASS ./index.test.js
  Index
    v should add two numbers (1 ms)

---------------|------------|----------------|---------------|------------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
---------------|------------|----------------|---------------|------------|-------------------
All files | 100 | 100 | 100 | 100 |                   
 index.js | 100 | 100 | 100 | 100 |                   
---------------|------------|-----------------|---------------|------------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 0.84 s
Ran all test suites.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the console report shows the files present in the folder, and percentage of the mentioned factors. At the end it shows total number of passed/failed test suites/cases. This even created the Istanbul report, generated at coverage\lcov-report\index.html folder; every time you runs the command it will re-generate the “coverage” folder.&lt;/p&gt;

&lt;h3&gt;
  
  
  Debug Unit Test
&lt;/h3&gt;

&lt;p&gt;There are couple of tools available for the debugging; but here our focus is on &lt;a href="https://code.visualstudio.com/"&gt;VS Code&lt;/a&gt; only; there is this awesome VS Code extension named &lt;a href="https://marketplace.visualstudio.com/items?itemName=Orta.vscode-jest"&gt;Jest&lt;/a&gt; available using which we can debug the written test cases. Once installed it automatically runs all the test cases, you can add breakpoints and press run/debug button on all or individual test cases to debug the test. Apart from this just to speed up the coding speed of unit test cases we can use &lt;a href="https://marketplace.visualstudio.com/items?itemName=andys8.jest-snippets"&gt;Jest Snippets&lt;/a&gt; VS Code extension to have short hands for JEST related methods.&lt;/p&gt;

&lt;h3&gt;
  
  
  Git Repository
&lt;/h3&gt;

&lt;p&gt;Check out the git repository for this project or download the code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ZakiMohammed/jest-started/archive/master.zip"&gt;Download Code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ZakiMohammed/jest-started"&gt;Git Repository&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;Writing unit test is an art and here we have just started to explore this art form. Moving forward, will deep dive into this topic and explore good amount of unit test case scenarios. Talking about &lt;a href="https://jestjs.io/"&gt;JEST&lt;/a&gt;, its super simple, straight forward and interesting framework that keeps things pretty handy to use and instead of feeling like struggling it helps to easy things out. We will learn more on &lt;a href="https://jestjs.io/"&gt;JEST&lt;/a&gt; in upcoming articles. Wish me strength on this one!&lt;/p&gt;

&lt;p&gt;Hope this article helps.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at&lt;/em&gt; &lt;a href="https://codeomelet.com/posts/jest-started-with-unit-testing"&gt;&lt;em&gt;https://codeomelet.com&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>jest</category>
      <category>node</category>
      <category>unittesting</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Workflow Core DSL for JSON and YAML</title>
      <dc:creator>Zaki Mohammed</dc:creator>
      <pubDate>Mon, 13 Mar 2023 19:33:21 +0000</pubDate>
      <link>https://dev.to/zakimohammed/workflow-core-dsl-for-json-and-yaml-3kn5</link>
      <guid>https://dev.to/zakimohammed/workflow-core-dsl-for-json-and-yaml-3kn5</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qovQbzNB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/0%2AGIeVhBHyKhE0LJSN.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qovQbzNB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/0%2AGIeVhBHyKhE0LJSN.jpg" alt="" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Workflow Core comes with many features one of which is its common DSL for JSON and YAML. In this article, we will explore JSON/YAML way of creating the process workflows.&lt;/p&gt;

&lt;p&gt;If you are following along, we have already seen how to get started with Workflow Core with .NET Core (6.0); you can check &lt;a href="https://codeomelet.com/posts/workflow-core-getting-started"&gt;Workflow Core Getting Started&lt;/a&gt; article. In continuation to that, we will understand how to use common DSL (JSON/YAML) provided by Workflow Core and re-create the same example with DSL instead of C# based workflow.&lt;/p&gt;

&lt;p&gt;Check out the &lt;a href="https://workflow-core.readthedocs.io/"&gt;Workflow Core&lt;/a&gt; documentation and also explore the &lt;a href="https://github.com/danielgerlag/workflow-core"&gt;Git Repository&lt;/a&gt;, thanks to &lt;a href="https://github.com/danielgerlag"&gt;Daniel Gerlag&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Initial Workflow Core Setup
&lt;/h3&gt;

&lt;p&gt;The initial setup of Workflow Core remains the same as explained in the &lt;a href="https://codeomelet.com/posts/workflow-core-getting-started"&gt;previous&lt;/a&gt; article; except we need to add one more package for DSL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dotnet add package WorkflowCore.DSL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The remaining steps will remain as is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create Steps&lt;/li&gt;
&lt;li&gt;Create Workflow&lt;/li&gt;
&lt;li&gt;Register and start the Workflow from the main program file&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The folder structure too remains the same:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;workflow-start
|-- Workflows
    |-- ProcessPayment
        |-- Steps
            |-- ApplyDiscount.cs
            |-- ApplyShipping.cs
            |-- Finalize.cs
            |-- Initialize.cs
        |-- ProcessPaymentWorkflow.cs
        |-- ProcessPaymentWorkflow.json
        |-- ProcessPaymentWorkflow.yml
|-- GlobalUsings.cs
|-- Program.cs
|-- workflow-start.csproj
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we have added JSON and YAML files for the workflow. But we need to revisit step 2 since our definition for the workflow will be written in DSL (JSON/YAML).&lt;/p&gt;

&lt;h3&gt;
  
  
  Define DSL for Workflow
&lt;/h3&gt;

&lt;p&gt;Consider the below workflow that we have created in C# &lt;code&gt;..\Workflows\ProcessPayment\ProcessPaymentWorkflow.cs&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workflows/ProcessPayment/ProcessPaymentWorkflow.cs&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class ProcessPaymentWorkflow : IWorkflow
{
    public string Id =&amp;gt; "ProcessPaymentWorkflow";

    public int Version =&amp;gt; 1;

    public void Build(IWorkflowBuilder&amp;lt;object&amp;gt; builder)
    {
        builder
            .UseDefaultErrorBehavior(WorkflowErrorHandling.Suspend)
            .StartWith&amp;lt;Initialize&amp;gt;()
            .Then&amp;lt;ApplyDiscount&amp;gt;()
            .Then&amp;lt;ApplyShipping&amp;gt;()
            .Then&amp;lt;Finalize&amp;gt;();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will comply with this and create the same definition in YAML.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Id: ProcessPaymentWorkflow
Version: 1
Steps:
  - Id: Initialize
    StepType: Initialize, workflow-start-dsl
    NextStepId: ApplyDiscount
  - Id: ApplyDiscount
    StepType: ApplyDiscount, workflow-start-dsl
    NextStepId: ApplyShipping
  - Id: ApplyShipping
    StepType: ApplyShipping, workflow-start-dsl
    NextStepId: Finalize
  - Id: Finalize
    StepType: Finalize, workflow-start-dsl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let us zoom in to individual step definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Id: Initialize
    StepType: Initialize, workflow-start-dsl
    NextStepId: ApplyDiscount
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It includes the Id of the step, this can be given anything within the YML definition file. Then we have StepType which needs to match the step class name as is along with the project assembly name (workflow-start-dsl). Then we have NextStepId which points to the next step within the YML definition file; this must match the Ids given to each step within the YML definition file.&lt;/p&gt;

&lt;p&gt;The same goes for JSON files too, even you can use simple YML to JSON conversion if you want to have both of the versions, as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "Id": "ProcessPaymentWorkflow",
    "Version": 1,
    "Steps": [
        {
            "Id": "Initialize",
            "StepType": "Initialize, workflow-start-dsl",
            "NextStepId": "ApplyDiscount"
        },
        {
            "Id": "ApplyDiscount",
            "StepType": "ApplyDiscount, workflow-start-dsl",
            "NextStepId": "ApplyShipping"
        },
        {
            "Id": "ApplyShipping",
            "StepType": "ApplyShipping, workflow-start-dsl",
            "NextStepId": "Finalize"
        },
        {
            "Id": "Finalize",
            "StepType": "Finalize, workflow-start-dsl"
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the same except for the ugly curly braces.&lt;/p&gt;

&lt;h3&gt;
  
  
  Load Definition
&lt;/h3&gt;

&lt;p&gt;For setting up the workflow we have to do the below steps in Program.cs file:&lt;/p&gt;

&lt;p&gt;Add the Workflow DSL service after the Workflow server in the ServiceCollection object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var serviceProvider = new ServiceCollection()
    .AddLogging()
    .AddWorkflow()
    .AddWorkflowDSL()
    .BuildServiceProvider();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a load definition method to load the workflow definition from DSL file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;static void LoadDefinition(IServiceProvider serviceProvider)
{
    var type = Deserializers.Yaml;
    var file = File.ReadAllText($"Workflows/ProcessPayment/ProcessPaymentWorkflow.{(type == Deserializers.Yaml ? "yml" : "json")}");

    var loader = serviceProvider.GetService();
    if (loader == null)
        throw new Exception("Loader not initialized");

    loader.LoadDefinition(file, type);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Call the LoadDefinition method instead of calling the RegisterWorkflow method of the host object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var host = serviceProvider.GetService();
if (host == null)
    throw new Exception("Host not initialized");

LoadDefinition(serviceProvider);

host.Start();

host.StartWorkflow("ProcessPaymentWorkflow");

Console.ReadLine();

host.Stop();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, when you run the project it will simply print these steps console text on you console window:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dotnet run
Initialize
ApplyDiscount
ApplyShipping
Finalize
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will continue to explore some of the other Workflow Core features in upcoming posts. Stay tuned!&lt;/p&gt;

&lt;h3&gt;
  
  
  Git Repository
&lt;/h3&gt;

&lt;p&gt;Check out the git repository for this project or download the code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ZakiMohammed/workflow-core-dsl/archive/master.zip"&gt;Download Code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ZakiMohammed/workflow-core-dsl"&gt;Git Repository&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;The Workflow Core’s common DSL provides a way to express your process workflow one step closer to the business understanding. Mainly YAML is considered a language of configuration and widely accepted by business analysts, automation, and DevOps professionals. This allows for defining workflows that can be easily used by both the hard-core developers and business bodies. One of the vital asks from any Workflow Core engine is to have a common language construct and Workflow Core provides it.&lt;/p&gt;

&lt;p&gt;Hope this article helps.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at&lt;/em&gt; &lt;a href="https://codeomelet.com/posts/workflow-core-dsl-for-json-and-yaml"&gt;&lt;em&gt;https://codeomelet.com&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>workflow</category>
      <category>workflowcore</category>
      <category>dotnetcore</category>
    </item>
    <item>
      <title>The Art of Destructuring in JavaScript</title>
      <dc:creator>Zaki Mohammed</dc:creator>
      <pubDate>Tue, 07 Mar 2023 18:49:45 +0000</pubDate>
      <link>https://dev.to/zakimohammed/the-art-of-destructuring-in-javascript-4fam</link>
      <guid>https://dev.to/zakimohammed/the-art-of-destructuring-in-javascript-4fam</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gxAjNoIM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/0%2A3U16AGboFbuXcN6u.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gxAjNoIM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/800/0%2A3U16AGboFbuXcN6u.jpg" alt="" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Poses the power of destructuring and conquers the world of JavaScript. Like literally, since it’s been available to devs, it is the coolest thing ever after the dark theme. To be relevant in the JavaScript gang, one has to understand the art of destructuring in JavaScript; in this article, we will adapt to the destructuring swag.&lt;/p&gt;

&lt;p&gt;Ask any beginner who has just started their coding journey, what kind of nightmare they have; out of many, one will be the concept of destructuring in JavaScript. But the same fella after understanding what it is will start bragging about it within their JavaScript circle. Happens to most of us, the point is why it’s been so popular nowadays and why we need to know it. The answer is the way it is handling some of the very tedious tasks we have to deal with objects and arrays in JavaScript. It offers lots of solutions and cases where it can help out in a way that a normal coding style would have cost us a lot.&lt;/p&gt;

&lt;p&gt;Let us directly jump into coding and take notes from there on what and how it is doing. For this we will consider 2 scenarios shown as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Destructuring Arrays&lt;/li&gt;
&lt;li&gt;Destructuring Objects&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Destructuring Arrays
&lt;/h3&gt;

&lt;p&gt;Arrays are awesome until you have to use the indexes to access the elements. Working with array indexes uglify your code and make it difficult for others to read and understand quickly. For this destructuring plays a vital role; we will cover the following array-based scenarios:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Basic One&lt;/li&gt;
&lt;li&gt;Skipping Values&lt;/li&gt;
&lt;li&gt;Index Position Values&lt;/li&gt;
&lt;li&gt;Default Values&lt;/li&gt;
&lt;li&gt;Swapping Values&lt;/li&gt;
&lt;li&gt;Returning Values From Function&lt;/li&gt;
&lt;li&gt;Passing Values To Function&lt;/li&gt;
&lt;li&gt;With Rest Parameter&lt;/li&gt;
&lt;li&gt;With Spread Operator&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Basic One
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const wizard = ['Harry', 'Potter']

const [firstName, lastName] = wizard

console.log(firstName, lastName) // Harry Potter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The wizard array is holding the name of the wizard (first and last name). In order to take the first name and last name individually, we need to use array indexes 0 and 1 respectively. That would be an ugly way to talk, but here we are giving names to the variable instead of talking in terms of indexes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Skipping Values
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const wizard = ['Harry', 'James', 'Potter']

const [firstName, , lastName] = wizard

console.log(firstName, lastName) // Harry Potter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we wanna skip the middle name which is appearing at the 1st index location; this can be done just by skipping the variable name after firstName and moving to the 2nd index location’s variable name which is lastName.&lt;/p&gt;

&lt;h3&gt;
  
  
  Index Position Values
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const developer = ['Rajneesh', 'Mithilesh', 'Chaubey']

const { [1]: middleName } = developer

console.log(middleName) // Mithilesh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can even pick values from the specific index location of an array as shown above. The square brackets contain the index followed by a colon and the name of the variable. Thanks to ma friend &lt;a href="https://www.linkedin.com/in/rajneeshchaubey/"&gt;Rajneesh Chaubey&lt;/a&gt; for helping me to add this one (#respect).&lt;/p&gt;

&lt;h3&gt;
  
  
  Default Values
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const address = ['Pittsburgh', 'Pennsylvania']

const [city, state, country = 'US'] = address

console.log(city, state, country) // Pittsburgh Pennsylvania US
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we are making the default value of the country variable to US, since our address array don’t have any country by default the country will be US.&lt;/p&gt;

&lt;h3&gt;
  
  
  Swapping Values
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let a = 10, b = 20;

console.log(a, b); // 10 20

[a, b] = [b, a]

console.log(a, b) // 20 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Swapping is fun with destructuring; what magically happens here is that the variable a and b are simply re-initialized with each other’s existing values.&lt;/p&gt;

&lt;h3&gt;
  
  
  Returning Values From Function
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const getLocation = () =&amp;gt; [18.97474, 72.82712]

const [lat, lng] = getLocation()

console.log(lat, lng) // 18.97474 72.82712
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing fancy here, a function is simply returning an array (latitude and longitude values), and then we are destructuring it for our own good.&lt;/p&gt;

&lt;h3&gt;
  
  
  Passing Values To Function
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const setLocation = ([lat, lng]) =&amp;gt; console.log('Stored:', lat, lng)

const location = [18.97474, 72.82712]

setLocation(location) // Stored: 18.97474 72.82712
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, a function named setLocation is accepting an array, but instead of naming the array we are destructuring it and creating variables from it inside the parameter tray itself. This looks pretty hardcore!&lt;/p&gt;

&lt;h3&gt;
  
  
  With Rest Parameter
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const languages = ['JavaScript', 'Node.js', 'C#', 'Java', 'C']

const [lang1, lang2, ...others] = languages

console.log(lang1, lang2, others)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This one is a throwback from &lt;a href="https://codeomelet.com/posts/rest-your-love-on-javascript"&gt;Rest Parameter&lt;/a&gt; article which we had covered already. Here, we are having a language array from which we are grabbing the first 2 values while keeping the rest as is within an array named others. For the “others” arrays, we are using the rest parameter’s three dots within our destructuring operation.&lt;/p&gt;

&lt;h3&gt;
  
  
  With Spread Operator
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const marks = [[54, 65, 93], [48, 58, 39], [85, 95, 73]]

const [[...marks1], marks2] = marks

console.log(marks[0] === marks1) // false
console.log(marks[1] === marks2) // true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we are having a marks array that has different sub-array storing marks data. We are destructuring first 2 arrays and also creating new reference of the first array using spread operator. We can confirm a new reference is created using the compare operator as shown. So the marks[0] is not same as marks1 array while marks[1] is same as marks2 array. Refer &lt;a href="https://codeomelet.com/posts/spread-love-with-javascript"&gt;Spread Operator&lt;/a&gt; article for spread operator.&lt;/p&gt;

&lt;h3&gt;
  
  
  Destructuring Objects
&lt;/h3&gt;

&lt;p&gt;Objects are the coolest in JavaScript, often with some buffy projects we receive tons of properties associated with them; then things get messy and difficult to manage, and required us to create new objects out of the main object every now and then. If it happens once or twice we can bare, but in the real-life scenario, such occurrences are a lot many. For this destructuring plays a vital role; we will cover the following object-based scenarios:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Basic One&lt;/li&gt;
&lt;li&gt;Property Alias&lt;/li&gt;
&lt;li&gt;Default Values&lt;/li&gt;
&lt;li&gt;Nested Objects&lt;/li&gt;
&lt;li&gt;Nested-Nested Objects&lt;/li&gt;
&lt;li&gt;Nested Arrays&lt;/li&gt;
&lt;li&gt;Nested Destructed Arrays&lt;/li&gt;
&lt;li&gt;Returning Objects From Function&lt;/li&gt;
&lt;li&gt;Passing Objects To Function&lt;/li&gt;
&lt;li&gt;With Rest Parameter&lt;/li&gt;
&lt;li&gt;With Spread Operator&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Basic One
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const person = { id: 1, name: 'John Doe', age: 21 }

const { name } = person

console.log(name) // John Doe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sweet! broken the person object and got only the name property out of it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Property Alias
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const person = { id: 1, name: 'John Doe', age: 21 }

const { name: personName } = person

console.log(personName) // John Doe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same as previous with the only difference is that we can have a new alias name instead of the actual property for the variable; for example, we have given the “name” property an alias that is “personName”. This is useful in cases where we already have an existing variable name with the same property name.&lt;/p&gt;

&lt;h3&gt;
  
  
  Default Values
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const person = { id: 1, name: 'John Doe', age: 21 }

const { name, age, salary = 0 } = person

console.log(name, age, salary) // John Doe 21 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This feature allows us to provide a default value to the newly destructured variable. For example, we are giving a default value of zero to the salary variable, but since we don’t have a salary property within the object it will create one for us. But if it already existed then the object’s property value would be considered.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nested Objects
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const address = {
    id: 1,
    street: 'Kulas Light',
    city: 'Gwenborough',
    zipcode: '92998-3874',
    geo: {
        lat: -37.3159,
        lng: 81.1496
    }
}

const { city, geo: { lat, lng } } = address

console.log(city, lat, lng)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are having a geo location object within our address object, but destructing allows us to further destructure the nested object as shown above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nested-Nested Objects
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const employee = {
    id: 1,
    name: 'Allen Green',
    job: 'Clerk',
    department: {
        id: 1,
        name: 'Accounts',
        geo: {
            lat: -54.4241,
            lng: 32.8973
        }
    }
}

const { name, department: { geo: { lat, lng } } } = employee

console.log(name, lat, lng)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Complicating it even further, we can go on with it at any level of nesting without hesitating. Here, we are getting the latitude and longitude variables out of the nested object geo within the nested object department.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nested Arrays
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const post = {
    id: 1,
    title: 'Voluptate occaecat',
    body: 'Elit eu ullamco sit elit magna id incididunt culpa.',
    comments: [
        {
            id: 1,
            body: 'Incididunt elit magna ipsum adipisicing nisi eu.'
        },
        {
            id: 2,
            body: 'Et voluptate occaecat esse esse nisi eu ullamco duis nisi sint aliquip.'
        },
        {
            id: 3,
            body: 'Deserunt ullamco aliquip veniam quis duis sunt.'
        },
    ]
}

const { title, comments } = post

console.log(title, comments)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works pretty normally with an array; here, “comments” is an array that can also be destructured out from the post object.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nested Destructed Arrays
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const post = {
    id: 1,
    title: 'Voluptate occaecat',
    body: 'Elit eu ullamco sit elit magna id incididunt culpa.',
    comments: [
        {
            id: 1,
            body: 'Incididunt elit magna ipsum adipisicing nisi eu.'
        },
        {
            id: 2,
            body: 'Et voluptate occaecat esse esse nisi eu ullamco duis nisi sint aliquip.'
        },
        {
            id: 3,
            body: 'Deserunt ullamco aliquip veniam quis duis sunt.'
        },
    ]
}

const { title, comments: [comment1, comment2] } = post

console.log(title, comment1.body, comment2.body)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we are destructuring arrays while we are destructuring the object. We are creating 2 variables from the array “comments” comment1 and comment2 while we are destructuring the post object.&lt;/p&gt;

&lt;h3&gt;
  
  
  Returning Objects From Function
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const getProduct = () =&amp;gt; ({
    id: 1,
    name: 'Wildcraft Bag',
    price: 1200,
    color: 'Red'
})

const { id, price, color } = getProduct()

console.log(id, price, color)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing to flex here, simply an object is returned from a function named getProduct() and we are destructuring it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Passing Objects To Function
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const setProduct = ({ id, name, price, color }) =&amp;gt; console.log('Stored:', id, name, price, color)

const product = {
    id: 1,
    name: 'Woodland Shoes',
    price: 3000,
    color: 'Brown'
}

setProduct(product)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, this is different! Here, we are breaking out the object which is supplied to the setProduct() function within the function’s parameter tray. This one looks cool and most of the framework uses this way, like in React we can see this in the component’s props. The setProduct() function is accepting all the values of the product object except the id property (actually in real-world scenarios id is mostly auto-generated by the backend).&lt;/p&gt;

&lt;h3&gt;
  
  
  With Rest Parameter
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const employee = {
    id: 1,
    name: 'Allen Green',
    job: 'Clerk',
    salary: 1200
}

const { id, name, ...others } = employee

console.log(id, name, others) // 1 Allen Green { job: 'Clerk', salary: 1200 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The formula of rest parameter can also be applied to object destructuring. Here, we are taking out only id and name properties while leaving all the remaining properties to “others” objects. For further reading refer &lt;a href="https://codeomelet.com/posts/rest-your-love-on-javascript"&gt;Rest Parameter&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  With Spread Operator
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const employee = {
    id: 1,
    name: 'Allen Green',
    job: 'Clerk',
    salary: 1200,
    department: {
        id: 1,
        name: 'Accounts'
    }
}

const { name, department: { ...department } } = employee

console.log(employee.department === department) // false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we are using the spread operator to create department object but with a new reference while we are destructuring the employee object. It actually worked and we can compare the references, it will result as false. For further reading refer &lt;a href="https://codeomelet.com/posts/spread-love-with-javascript"&gt;Spread Operator&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Git Repository
&lt;/h3&gt;

&lt;p&gt;Check out the git repository for this project or download the code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ZakiMohammed/javascript-destructuring/archive/master.zip"&gt;Download Code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ZakiMohammed/javascript-destructuring"&gt;Git Repository&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;If not the rest parameter or spread operator scared me initially, but destructuring had definitely. It is worth studying since day by day the dependency on JavaScript is increasing and we need to overcome ES6 hurdles. Most of the recent JS frameworks are using it within their coding syntax it is difficult to understand it along with the framework, so it is good to separate what are default JS features and what are framework-specific features.&lt;/p&gt;

&lt;p&gt;Hope this article helps.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at&lt;/em&gt; &lt;a href="https://codeomelet.com/posts/the-art-of-destructuring-in-javascript"&gt;&lt;em&gt;https://codeomelet.com&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>destructuring</category>
      <category>es6</category>
      <category>objectdestructuring</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
