<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Nishu Goel</title>
    <description>The latest articles on Forem by Nishu Goel (@nishugoel).</description>
    <link>https://forem.com/nishugoel</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%2F138709%2F9bf663d5-62da-4572-a75b-09813dd5f007.jpeg</url>
      <title>Forem: Nishu Goel</title>
      <link>https://forem.com/nishugoel</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/nishugoel"/>
    <language>en</language>
    <item>
      <title>The ü/ü Conundrum</title>
      <dc:creator>Nishu Goel</dc:creator>
      <pubDate>Mon, 12 Feb 2024 01:23:23 +0000</pubDate>
      <link>https://forem.com/epilot/the-uu-conundrum-2d75</link>
      <guid>https://forem.com/epilot/the-uu-conundrum-2d75</guid>
      <description>&lt;p&gt;I implemented search and filtering for entities on our product at epilot. The users were heavily using the feature, however, a unique issue surfaced: difficulties in filtering file names containing diacritical marks like umlauts (Äpfel, über, schön, etc.).&lt;/p&gt;

&lt;p&gt;Intrigued, I delved into the logic to investigate. Initially, everything seemed in order. For instance, a file named “blöb” was saved precisely as “blöb” — no bizarre encoding alterations were apparent.&lt;/p&gt;

&lt;p&gt;Can you spot any difference between “blöb” and “blöb”?&lt;/p&gt;

&lt;p&gt;This time instead of typing the word in the search bar, I copied it from the uploaded and saved filename, and and voilà, the search functioned perfectly! 🤯. This definitely meant the file name was subtly altered upon upload, hinting at a background encoding transformation.&lt;/p&gt;

&lt;p&gt;So, as anyone would, I employed the power of encodeURIComponent to see for myself what really was different.&lt;/p&gt;

&lt;p&gt;There it was! The different types of encoding forms that led to this. Now you might be wondering (I was wondering too!):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As if worrying about encoding wasn’t enough, now we’ve got to stress over its different types too 🤦‍♀ and wait, its 2024, and we are still grappling with Unicode character encoding problems?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs8bqoa1seb9nj96uufxw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs8bqoa1seb9nj96uufxw.png" alt="different encoding forms" width="536" height="260"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The culprits in this case were these two contrasting encoding types: &lt;code&gt;%C3%BC&lt;/code&gt; and &lt;code&gt;%CC%88&lt;/code&gt;, leading to the search malfunction. This is a well-known issue with NFD/NFC encoding forms.&lt;/p&gt;

&lt;p&gt;There is a detailed explanation &lt;a href="https://unicode.org/reports/tr15/#Norm_Forms"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiziylxqmjph9xw35fcpw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiziylxqmjph9xw35fcpw.png" alt="nfc and nfd" width="800" height="295"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now imagine explaining this technical jargon that to a user! 😃&lt;/p&gt;

&lt;p&gt;To fix it, I aligned the encoding type of the searched filename with that of the saved filename.&lt;br&gt;
On MacOS, the encoding was NFD (for ö: 111 + 776), whereas on Linux, it switched to NFC (ö: 246).&lt;/p&gt;

&lt;p&gt;The solution?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Post-upload, before saving the file as an entity, I applied .normalize() to the filename, ensuring it was uniformly searchable.&lt;/li&gt;
&lt;li&gt;Additionally as a next cleanup step — to write a migration script to normalize the names of all existing files with non-ASCII characters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simple string normalise proved effective in this case. ✅&lt;/p&gt;

&lt;p&gt;Read more about String.prototype.normalize() &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize#canonical_equivalence_normalization"&gt;here&lt;/a&gt;. There is also specific normalization option to pass to ensure canonically equivalent normalization — for example: string.normalize("NFC")&lt;/p&gt;

&lt;p&gt;Here’s a visual representation of the problem with the solution output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://drive.google.com/file/d/1Jkp8ye47iRfDoeTaMQkkskY_BTHegj_D/view?source=post_page-----1b628e8cf36a--------------------------------"&gt;files umlaut.mov&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hope it helps!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>programming</category>
      <category>encoding</category>
      <category>unicode</category>
    </item>
    <item>
      <title>How using Server-Timing API helped bring &gt; 70% perf improvement</title>
      <dc:creator>Nishu Goel</dc:creator>
      <pubDate>Tue, 23 Jan 2024 00:27:12 +0000</pubDate>
      <link>https://forem.com/epilot/how-using-server-timing-api-helped-bring-70-perf-improvement-44ln</link>
      <guid>https://forem.com/epilot/how-using-server-timing-api-helped-bring-70-perf-improvement-44ln</guid>
      <description>&lt;p&gt;&lt;em&gt;(Originally posted on &lt;a href="https://calendar.perfplanet.com/2023/server-timing-api-70-percent-perf-improvement/" rel="noopener noreferrer"&gt;Web performance Calendar)&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When working on the web, we all end up coming across situations where the experience for the user seems a bit janky. This happens either when you are dogfooding, or the users actually highlight facing that slowness, or you see that for yourself in your performance metrics or tools.&lt;/p&gt;

&lt;p&gt;What one’d do in such a situation is look at the obvious things first: are the images loaded lazily? Is the bundle size too big? Is the main thread blocked? How is the performance panel recording looking? Is the data coming from the backend taking more time than it should?&lt;/p&gt;

&lt;p&gt;If it’s one of the first problems, we’d fire up our favorite devtools and try to find out the root cause. If it’s the data being slow, we could see how long that is taking in the network requests duration. And, if it does end up being slow from the backend, the next step would be to dive into some logs and checking what piece of code in the request endpoint takes the time, or embedding some performance information in the response, or using other performance tools etc.&lt;/p&gt;

&lt;p&gt;We had a similar situation at &lt;a href="https://epilot.cloud/" rel="noopener noreferrer"&gt;epilot&lt;/a&gt; where we use Elasticsearch to look for all related products of an order for example, and also all the other entities the order itself might be a relation to.&lt;/p&gt;

&lt;p&gt;This meant, looking for direct relations of the order but also the reverse relations of that order.&lt;/p&gt;

&lt;p&gt;Now in the Elasticsearch world, this meant:&lt;/p&gt;

&lt;p&gt;Getting the list of related products of the order.&lt;br&gt;
Searching through the documents in elasticsearch for all those related IDs.&lt;br&gt;
Also, searching all other docs for having this one order as a relation to this (the reverse relation example, I shared above).&lt;br&gt;
Finally returning all the found relations.&lt;/p&gt;

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

&lt;p&gt;Showcasing two products as relations of an order&lt;/p&gt;

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

&lt;p&gt;This worked great for the first few years and got us exactly what we needed quickly. However, as we scaled, the amount of documents indexed in ES increased too, and that meant, the step 3 above started getting expensive and slow! The logic was to look for an entity ID in all the indexed documents and find if it is a relation or not. For orders with a lot of products, the endpoint would respond in seconds.&lt;/p&gt;

&lt;p&gt;We had to fix the logic/ update the design asap.&lt;/p&gt;

&lt;p&gt;The first solution was to update the ES query to make it more specific about what is being looked up, and then check if it improved with that query change and how the results looked for the entities with many relations. This meant looking into a crazy amount of Cloudwatch logs and looking at the total time taken and if it was any better.&lt;/p&gt;

&lt;p&gt;We made a change to search query to be an IDs specific query which would look through only the IDs in the indexed docs, and not through all the fields in those ES documents.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /_search
{
  "query": {
    "ids" : {
      "values" : ["1", "4", "100"]
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The expectation was this would completely solve the problem and be super quick. This wasn’t the case unfortunately.&lt;/p&gt;

&lt;p&gt;What we were missing was to see which part of the code was really adding to the time taken by the endpoint. We needed to observe the different steps in the process and see all the sections where the time taken could be reduced. We needed to see if it was really ES that was the culprit.&lt;/p&gt;

&lt;p&gt;This is where Server-Timing API came into picture. Something that would break down the request into pieces and give us an idea about what’s expensive. The API allowed to pass the timings specific to a request from the backend to the browser. This meant that we could now see in the network requests, how the response times looked like, which part of the function took more time and so on.&lt;/p&gt;

&lt;p&gt;To support this, we need to send a Server-Timing response header from the backend and time the methods that we needed. Something like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP/1.1 200 OK

Server-Timing: db;dur=53, app;dur=47.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The Server-Timing header can send three bits of information:&lt;/p&gt;

&lt;p&gt;metric name&lt;br&gt;
duration&lt;br&gt;
description of the metric&lt;br&gt;
In the above example:&lt;br&gt;
db — metric name&lt;br&gt;
dur=53 — the time in milliseconds the db metric took (This is for instance, the time to fetch some data from the Database)&lt;br&gt;
app — another metric name and dur=47.2 being the time taken for the same.&lt;/p&gt;

&lt;p&gt;The header can take multiple metrics separated by commas delivering great information and is super lightweight. Although it is recommended to keep the metric names as small as possible.&lt;/p&gt;

&lt;p&gt;An example of what the returned Server-Timing header looks like:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;db; dur=142.715967; desc="getRelationsForEntity",flt; 
dur=36.777609; desc="filterEntityListByAccess",pgn; 
dur=22.96549; desc="getPaginatedRelations",hyd; 
dur=0.64605; desc="hydrateRelations",ddp; 
dur=0.13311699999999999; desc="dedupeRelations",total; dur=721.388338

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

&lt;/div&gt;

&lt;p&gt;And this information then translates into a more visual and helpful view as a part of the network timings tab of the request OR available via the PerformanceServerTiming interface.&lt;/p&gt;

&lt;p&gt;To access the three available properties, they correspond in the PerformanceServerTiming as:&lt;/p&gt;

&lt;p&gt;“name” -&amp;gt; PerformanceServerTiming.name&lt;br&gt;
“dur” -&amp;gt; PerformanceServerTiming.duration&lt;br&gt;
“desc” -&amp;gt; PerformanceServerTiming.description&lt;/p&gt;

&lt;p&gt;Here’s the &lt;a href="https://www.w3.org/TR/server-timing/" rel="noopener noreferrer"&gt;working draft&lt;/a&gt; with more information.&lt;/p&gt;

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

&lt;p&gt;These are not available as a series of timings as such and just simple metrics and therefore, do not show in a waterfall pattern.&lt;/p&gt;

&lt;p&gt;This means we can now tackle both the frontend performance problems and the backend slowness problems all in one place. No jumping to the logs to find a response was slow or heading backing again to the devtools to find the frontend problem.&lt;/p&gt;

&lt;p&gt;We could also read the data now available using the navigation (PerformanceNavigationTiming) and resource timing (PerformanceResourceTiming) APIs and send it our analytic tools to create metrics/monitors.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const performanceObserver = new PerformanceObserver((list) =&amp;gt; {
  for (const entry of list.getEntries()) {
    log('Server Timing', entry.serverTiming);
  }
});


performanceObserver.observe({type: 'navigation', buffered: true});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Below is one of the examples from a method taking different amounts of time at different steps in the request. We will set up together the whole setting up and measuring the server response times later in this article.&lt;/p&gt;

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

&lt;p&gt;This was especially helpful for the Elasticsearch example that I shared earlier as we needed to know which change was improving what part of the implementation and if it was moving us closer to improved response times. The Server-Timing header does not directly improve the timings but helps find out what could be worked towards.&lt;/p&gt;

&lt;p&gt;The uniqueness of the API is that its super flexible and allows the server to communicate important information to the browser, even other than the server timings. It would be amazing to have this supported on Safari as well, which currently lacks the API support.&lt;/p&gt;

&lt;h2&gt;
  
  
  Coming back to how we came down to the API response in 400ms seconds as compared to 1.8s earlier
&lt;/h2&gt;

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

&lt;p&gt;These are the two versions of the API. When we measured the performance with server-timings on the ES implementation after switching to the IDs search query on Elasticsearch, the performance seemed to be taking all the time, especially on this function searchRelationsForEntity.&lt;/p&gt;

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

&lt;p&gt;Now we knew this was to do with ES but where exactly. We timed specifics inside this method.&lt;/p&gt;

&lt;p&gt;On measuring further, it was entitySearch.searchEntities that was taking up the most time so we needed to find a solution that would not go through all the ES documents at all to get the relation entities.&lt;/p&gt;

&lt;p&gt;This is where changing the design made sense, and we moved to a graph table design with Dynamo, which would store all the relationships as mappings, and return all the entity IDs whether as direct relations or reverse.&lt;/p&gt;

&lt;p&gt;Now we could choose if we wanted to search ES with all the IDs that we have or just batch get the entities from the db, and support pagination (this was an added advantage with ES) etc. separately.&lt;/p&gt;

&lt;p&gt;Now, this complete dynamo approach saved us looking through all the indexed documents for the IDs that we need, and also cut some slack on Elasticsearch which would have this unnecessary load, eventually affecting other straightforward searches as well.&lt;/p&gt;

&lt;p&gt;This is how the server timings looked with the DynamoDB approach:&lt;/p&gt;

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

&lt;p&gt;This was an amazing improvement, clearly visible with this significant performance information right in the browser devtools for us. This also ended up helping improve the code in general, to gain some more milliseconds by refactoring, and using performant ways to filter/hydrate/paginate things etc.We love to see the output of what’s in the works, and this was a great example to look at and feel a sense of achievement for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation of Server-Timing as a middleware for AWS Lambda
&lt;/h2&gt;

&lt;p&gt;Now if you look up the existence of libraries supporting server-timing header, there are so many (&lt;em&gt;server-timing, server-timings etc.&lt;/em&gt;) but most of them for Express and NodeJS.&lt;/p&gt;

&lt;p&gt;We needed to create one for the serverless use case which could be used for AWS Lambda, lets say to use with middy for example.&lt;/p&gt;

&lt;p&gt;Step 1. Create a withServerTimings() middleware to use for AWS lambda&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import middy from '@middy/core';
import * as Lambda from 'aws-lambda';

export const withServerTimings = () =&amp;gt; {

  return {
    // add Server-Timing header to response
    after: (handler: middy.Request) =&amp;gt; {
      const response = handler.response as Lambda.APIGatewayProxyStructuredResultV2;
      try {
        const headers: unknown[] = [];
        /**
          This is where the headers with different metrics are be saved
          and retrieved from
        */
        const timings = getServerTimingHeader(headers);
        response.headers = {
          ...response.headers,
          'Server-Timing': timings,
        };
      } catch (e) {
        Log.debug(`Middleware Error: Could not record server timings`, e);
      }
    },
  };
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This creates a usable withServerTimings() middleware to then be used as:&lt;/p&gt;

&lt;p&gt;Step 2. Apply the middleware to the request handler&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;return middy(handler)
  .use(...)
  .use(withCors())
  .use(withServerTimings())
  .use(...)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This already ensures that your backend will now be sending a Server-Timing header in the request endpoints.&lt;/p&gt;

&lt;p&gt;Next step would be to actually time our operations and store them as a common timing variable which can then be sent to the getServerTimingHeader method for example.&lt;/p&gt;

&lt;p&gt;Step 3. Implement timing for your methods&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const times = new Map&amp;lt;string, Record&amp;lt;string, unknown&amp;gt;&amp;gt;();

// starting the timer on a method
const startTimer = (name: string, description?: string) =&amp;gt; {
  times.set(name, {
    name,
    description: description || '',
    startTime: process.hrtime(),
  });
};

// ending the timer and recording the duration of the method
const endTimer = (name: string, description?: string) =&amp;gt; {
  const timeObj = times.get(name);
  if (!timeObj) {
    return console.warn(`No such name ${name}`);
  }

  const duration = process.hrtime(timeObj.startTime as [number, number]);
  const value = duration[0] * 1e3 + duration[1] * 1e-6;

  timeObj.value = value;
  times.delete(name);
  return timeObj;
};

// finally setting the metric value received from endTimer object
const metric =
  typeof description !== 'string' || !description
    ? `${name}; dur=${dur ?? 0}`
    : `${name}; dur=${dur}; desc="${description}"`;

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

&lt;/div&gt;

&lt;p&gt;This would then be used as:&lt;/p&gt;

&lt;p&gt;Step 4. Usage of timers of the methods&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;startTime('db', 'getRelationsForEntity');
const relations = await getRelationsForEntity({
  ...
});
endTime('db', 'getRelationsForEntity');
  .
  .
  .
startTime('pgn', 'getPaginatedRelations');
const { relations: paginatedRelations, hits } = getPaginatedRelations({
  ...
});

endTime('pgn', 'getPaginatedRelations');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And there we go, we will have the timed methods and the server headers looking like:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;db; dur=139.99; desc="getRelationsForEntity",flt; dur=36.68; desc="filterEntityListByAccess",
pgn; dur=39.37; desc="getPaginatedRelations",hyd; dur=0.61;
desc="hydrateRelations",ddp; dur=0.130; desc="dedupeRelations",
total; dur=741.53
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

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

&lt;p&gt;All the timing implementation and setting up the middleware can be a hassle, and should be a one-time job. I created a package out of this implementation called lambda-server-timing:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/lambda-server-timing" rel="noopener noreferrer"&gt;lambda-server-timing (npmjs.com)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/NishuGoel/lambda-server-timing" rel="noopener noreferrer"&gt;GitHub – NishuGoel/lambda-server-timing (github.com)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;withServerTimings&lt;/code&gt; to use a middleware&lt;br&gt;
&lt;code&gt;startTime&lt;/code&gt; to start measuring a function/set of functions&lt;br&gt;
&lt;code&gt;endTime&lt;/code&gt; to end the timing of the function/set of functions and get the metric set.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import {withServerTimings, startTime, endTime } from 'lambda-server-timing';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let’s time our functions down!&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.w3.org/TR/server-timing/" rel="noopener noreferrer"&gt;Working draft&lt;/a&gt;&lt;br&gt;
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Server-Timing" rel="noopener noreferrer"&gt;MDN Server Timing&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.w3.org/TR/navigation-timing/" rel="noopener noreferrer"&gt;Navigation Timing&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.w3.org/TR/resource-timing/" rel="noopener noreferrer"&gt;Resource Timing&lt;/a&gt;&lt;/p&gt;

</description>
      <category>performance</category>
      <category>lambda</category>
      <category>serverless</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Spin up a new Elasticsearch cluster?</title>
      <dc:creator>Nishu Goel</dc:creator>
      <pubDate>Tue, 08 Aug 2023 12:53:24 +0000</pubDate>
      <link>https://forem.com/epilot/spin-up-a-new-elasticsearch-cluster-43m0</link>
      <guid>https://forem.com/epilot/spin-up-a-new-elasticsearch-cluster-43m0</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This post explains how to setup a new elasticsearch cluster from scratch and make it ready to be used for indexing data into, communicating to/from as a remote cluster, along with setting up monitoring for the new cluster. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Problem 1: Create a new elasticsearch cluster on elastic.co based on the required configuration for your use case.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To create a new cluster from scratch:&lt;br&gt;
Create a &lt;code&gt;main.tf&lt;/code&gt; file with following resources:&lt;/p&gt;

&lt;p&gt;a. &lt;code&gt;ec_deployment&lt;/code&gt; - The main cluster deployment resource&lt;br&gt;
b. &lt;code&gt;aws_secretsmanager_secret&lt;/code&gt;- The cluster secrets&lt;br&gt;
c. &lt;code&gt;aws_secretsmanager_secret_version&lt;/code&gt; - The cluster secret version needed for the secrets&lt;br&gt;
d. &lt;code&gt;elasticstack_elasticsearch_cluster_settings&lt;/code&gt; - To add default settings to the cluster like cluster nodes etc. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Example: ec_deployment&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "ec_deployment" "&amp;lt;cluster-name&amp;gt;" {
  name = "&amp;lt;cluster-name&amp;gt;"

  region                 = "aws-eu-central-1"
  version                = var.elasticsearch_version
  deployment_template_id = standard/arm-based (decide based on the requirement)

  elasticsearch {
    autoscale = "false"
    topology {
      id   = "hot_content"
      size = "15g" // size of the cluster
      zone_count = 3 // number of nodes 
    }
  }
  kibana {}

  // important to include this in the cluster config to ensure to NOT override any existing remote cluster related config
  # ignore changes to remote cluster config
  lifecycle{
    ignore_changes = [
      elasticsearch[0].remote_cluster
    ]
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Few important bits to note in this:&lt;/p&gt;

&lt;p&gt;Ensure the region and ES version are consistent for your cluster.&lt;/p&gt;

&lt;p&gt;By default, &lt;code&gt;deployment_template_id&lt;/code&gt; should be defined as standard &lt;code&gt;aws-storage-optimized-v3&lt;/code&gt; but if your are creating the cluster for memory-intensive work, consider creating it as arm-based &lt;code&gt;aws-general-purpose-arm-v6&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The size and number of nodes vary based on the need, so decide it accordingly from the Elastic Cloud UI.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hj-S-wgp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6xknyji4ievixf9xsy73.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hj-S-wgp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6xknyji4ievixf9xsy73.png" alt="Elastic Cloud UI" width="800" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An important bit to always add in your cluster deployment is to ignore changes to the remote cluster config. This ensures the remote communication that is configured among clusters doesn’t get overriden by this update.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ignore_changes = [
      elasticsearch[0].remote_cluster
    ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Example: Cluster Secrets&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Create the AWS Secret Manager secret
resource "aws_secretsmanager_secret" "&amp;lt;cluster-secret-name&amp;gt;" {
  name = "/elasticsearch/&amp;lt;cluster-name&amp;gt;/credentials"
  description = "Elasticsearch credentials for &amp;lt;cluster-name&amp;gt; cluster"
}

# Create the AWS Secret Manager secret version with JSON value
resource "aws_secretsmanager_secret_version" "&amp;lt;cluster-secret-name&amp;gt;" {
  secret_id     = aws_secretsmanager_secret.&amp;lt;cluster-secret-name&amp;gt;.id
  secret_string = &amp;lt;&amp;lt;JSON
{
  "endpoint": "${ec_deployment.&amp;lt;cluster-name&amp;gt;.elasticsearch.0.https_endpoint}",
  "username": "${ec_deployment.&amp;lt;cluster-name&amp;gt;.elasticsearch_username}",
  "password": "${ec_deployment.&amp;lt;cluster-name&amp;gt;.elasticsearch_password}"
}
JSON
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Example: cluster_settings&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "elasticstack_elasticsearch_cluster_settings" "&amp;lt;cluster-settings&amp;gt;" {
  elasticsearch_connection {
    username = ec_deployment.&amp;lt;cluster-name&amp;gt;.elasticsearch_username
    password = ec_deployment.&amp;lt;cluster-name&amp;gt;.elasticsearch_password
    endpoints = [ec_deployment.&amp;lt;cluster-name&amp;gt;.elasticsearch.0.https_endpoint]
  }

  persistent {
    setting {
      name  = "cluster.max_shards_per_node"
      value = "2500"
    }
    setting {
      name  = "cluster.routing.allocation.cluster_concurrent_rebalance"
      value = "30"
    }
    setting {
      name  = "cluster.routing.allocation.node_concurrent_recoveries"
      value = "30"
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three important settings that would make sense to add as default 👆&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;max_shards_per_node&lt;/code&gt; the value would vary based on the cluster size and requirement&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cluster_concurrent_rebalance&lt;/code&gt; To allow correct rebalancing of shards to all available nodes (20-30 is a good value for this)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;node_concurrent_recoveries&lt;/code&gt; To allow correct rebalancing of shards to all available nodes (20-30 is a good value for this)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Above steps will create a cluster with the defined config. ✅&lt;/p&gt;

&lt;p&gt;Always good to ensure and check what our changes are adding/updating/deleting, to be extra cautious to NOT end up deleting/updating some existing resource. 😨 &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem 2: Add the newly created elastic cluster to the POOL of clusters in the DynamoDB Cluster assignment table&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now that the new cluster is and available to view on elasticsearch as a healthy deployment, we can make the next move to include it in the list of available clusters, to be able to then use it for our ES needs.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;example:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1BId0Z8r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vjg9va1kn5zz5fc2xg51.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1BId0Z8r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vjg9va1kn5zz5fc2xg51.png" alt="healthy cluster deployment" width="800" height="51"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To add it to the pool, you can assign it to the table where cluster assignment is defined, in our case: &lt;code&gt;ClusterAssignmentTable&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To add your new cluster to this pool, use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;json=$(cat &amp;lt;&amp;lt;EOF
{
  "pk": {"S": "POOL"},
  "sk": {"S": "CLUSTER#${cluster_name}"},
  "cluster": {"S": "${cluster_name}"},
  "endpoint": {"S": "${endpoint}"},
  "username": {"S": "${username}"},
  "password": {"S": "${password}"}
}
EOF
)

echo "===&amp;gt; Storing cluster $cluster_name in table $table_name..."
aws dynamodb put-item \
    --table-name $table_name \
    --item "${json}"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a result of this, the new cluster should be found in the cluster assignment table.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem 3: Allow it the access to communicate to/from other existing ES clusters.&lt;/strong&gt;&lt;br&gt;
When dealing with multiple ES clusters, there is the requirement to query data from one cluster to another. In the current implementation, we already have use cases of doing multi-org queries where those orgs could belong to different clusters.&lt;/p&gt;

&lt;p&gt;Usage looks like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EkyYLXaV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rujnuj09bxgj165nifqj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EkyYLXaV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rujnuj09bxgj165nifqj.png" alt="multicluster communication" width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To allow this, we need to ensure, every time a new cluster is created, its also set up for the remote cluster communication.&lt;/p&gt;

&lt;p&gt;To do this, you could do something like for all your clusters in the pool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;payload=$(cat &amp;lt;&amp;lt;EOF
{
  "persistent": {
    "cluster": {
      "remote": {
        "${remote_cluster}": {
          "mode":"proxy",
          "server_name": "${remote_server_name}",
          "proxy_address": "${remote_proxy_address}",
          "skip_unavailable": true
        }
      }
    }
  }
}
EOF
)

curl -sS -X PUT "$endpoint/_cluster/settings" \
  -u "$username:$password" \
  -H 'Content-Type: application/json' \
  -d "$payload" | jq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will ensure all the clusters for that environment are setup for remote communication to one another. &lt;/p&gt;

&lt;p&gt;Also, remember we had that extra step to ignore changes to the remote cluster config? This is why! We DO NOT want to override any remote config changes when updating clusters.&lt;/p&gt;

&lt;p&gt;At this point, you have your cluster ready on elasticsearch, cluster allocation table, and also able to talk to other clusters 💪&lt;/p&gt;

&lt;p&gt;And then there are the Not-so-good times when clusters are not doing that great and healthy.&lt;/p&gt;

&lt;p&gt;With these many clusters (even more probably in the future), we want to know the whereabouts of each of them, to be better at managing these.&lt;br&gt;
So a cluster specific monitoring is a good idea. We do that using Datadog. Datadog provides a Elastic Cloud integration that can be used for the same.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--s36SEbcj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/agwyzp8ov3m6uy2rb0g1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--s36SEbcj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/agwyzp8ov3m6uy2rb0g1.png" alt="Datadog ES integration" width="800" height="778"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Something like above 👆&lt;/p&gt;

&lt;p&gt;Hope this helps!&lt;/p&gt;

</description>
      <category>elasticsearch</category>
      <category>architecture</category>
      <category>database</category>
      <category>development</category>
    </item>
    <item>
      <title>Matching faces to Twitter DPs #ngIndia</title>
      <dc:creator>Nishu Goel</dc:creator>
      <pubDate>Fri, 27 May 2022 05:23:06 +0000</pubDate>
      <link>https://forem.com/epilot/matching-faces-to-twitter-dps-ngindia-1j9j</link>
      <guid>https://forem.com/epilot/matching-faces-to-twitter-dps-ngindia-1j9j</guid>
      <description>&lt;p&gt;Guess what I am talking about?&lt;/p&gt;

&lt;p&gt;Year 2022, this was after more than 2 years that we got to be at an in-person conference, and gosh, the experience! I am forced to accept that we were simply compromising when being a part of the online conferences.&lt;/p&gt;

&lt;p&gt;I think conferences were anyway never about only consuming the content delivered from the sessions. It was majorly about the networking, the warmth from meeting people. So when we we went to ngIndia 2022 event this May, it was about meeting those people from online bonds from the past two years and connecting NOT from behind the screens.&lt;/p&gt;

&lt;p&gt;So, 21st May, I got the opportunity to speak at #ngIndia on the topic ‘Microfrontends’. Now I chose this topic because I am currently working with micro-frontends at &lt;a href="https://epilot.cloud/"&gt;@epilotGmbH&lt;/a&gt;, and what could be better than talking about the success and challenges of using micro-frontends for larges scale apps than this.&lt;/p&gt;

&lt;p&gt;So my preparation did include solving issues related to, for example, cross-micro-frontend communication AT WORK and coming back home and trying to explain the problem and its solution from the day through my slides.&lt;/p&gt;

&lt;p&gt;Finally I decided to do my talk in the form of a role-play with &lt;a href="http://twitter.com/srashtisj"&gt;Srashti Jain&lt;/a&gt; to help audience understand better.&lt;/p&gt;

&lt;p&gt;Me — I wish I could work for this company but I haven’t used X framework for a while now.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://twitter.com/srashtisj"&gt;Srashti Jain&lt;/a&gt; (as a recruiter) — oh that’s not a problem at all! Our product is based on a micro-frontend architecture. We’d love to hire you to work on your choice of framework and build our awesome product.&lt;/p&gt;

&lt;p&gt;Me — Wow that sounds interesting! So I don’t need to skill up in another framework (X framework) now to contribute to this product. Awesome!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jCM9c8Le--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/1400/1%2AO6b1VdM_WD0yFNbKc4N2PQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jCM9c8Le--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/1400/1%2AO6b1VdM_WD0yFNbKc4N2PQ.jpeg" alt="role play microfrontends" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So this is how I began my talk to explain how micro-frontends made scaling up teams and projects framework-agnostic. But thats not it, I am sharing my slides here to get a sense of what was discussed, however, we will soon have the recorded talk up soon and ready to be shared right here.&lt;br&gt;
&lt;/p&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://docs.google.com/presentation/d/1o9qS5VoguhcbuW1MvzC1nEluJ5VZJQ5ULeeDTUb_Rs8/edit#slide=id.p" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--pWBEu5lm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://lh7-us.googleusercontent.com/docs/AHkbwyKyvcfpA1qqDMAmyVB6rXewb4jYkqpGYBo_fmmB_qIv393YiMoEQrvj5ToZ8wDh5ML3jU91rSAMTHCK7DY7KdRXvyjN8_0Ruq2hsQLIBu-Rb0Skewpu%3Dw1200-h630-p" height="420" class="m-0" width="800"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://docs.google.com/presentation/d/1o9qS5VoguhcbuW1MvzC1nEluJ5VZJQ5ULeeDTUb_Rs8/edit#slide=id.p" rel="noopener noreferrer" class="c-link"&gt;
          mfe_ngIndia - Google Slides
        &lt;/a&gt;
      &lt;/h2&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--LIKqDDXx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://ssl.gstatic.com/docs/presentations/images/favicon-2023q4.ico" width="256" height="256"&gt;
        docs.google.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;My experience speaking about micro-frontends was beyond my expectations, and I had such important conversations with many engineers from the audience. Our discussions revolved around how developers could use single-spa framework to deal with their micro-frontend architecture as the top level router, around how developers faced routing issues in their current single-spa MFEs, how some developers desired to switch to micro frontends to migrate from AngularJS etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tJvQa8jh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/2404/1%2AnSnfM2BJxyVplGVnTqlBNA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tJvQa8jh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/2404/1%2AnSnfM2BJxyVplGVnTqlBNA.jpeg" alt="networking" width="800" height="1065"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kFcAGV8E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/3072/1%2AlsZhYbrdC1T8QEH96QFStQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kFcAGV8E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/3072/1%2AlsZhYbrdC1T8QEH96QFStQ.jpeg" alt="networking" width="800" height="1067"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2Jhf94n9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/6048/1%2A4GBbJVjPJm4sHtNVWdZ1hw.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2Jhf94n9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/6048/1%2A4GBbJVjPJm4sHtNVWdZ1hw.jpeg" alt="networking" width="800" height="1067"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NrZeBzPz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/6048/1%2AHQ1sKiFRRbR-4GTAqpbQ7w.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NrZeBzPz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/6048/1%2AHQ1sKiFRRbR-4GTAqpbQ7w.jpeg" alt="networking" width="800" height="1067"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ciclPpTT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/1400/1%2AvO0Lw66uIzLix_lA_YEsNw.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ciclPpTT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/1400/1%2AvO0Lw66uIzLix_lA_YEsNw.jpeg" alt="a happy picture with the volunteers and the attendees&amp;lt;br&amp;gt;
" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The conference had more than 500 people joining in the fun, with the right element of over-coffee networking, over-lunch chit-chat, after-talk questions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CfkHOCFO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/1400/1%2As3AfiVvB8S4-S0N8-HKbMw.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CfkHOCFO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/1400/1%2As3AfiVvB8S4-S0N8-HKbMw.jpeg" alt="500 attendees with the organiser" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Speakers from all over the world shared interesting findings during their talks. The conference was followed by an after party where the speakers and the volunteers partied together with food, drinks, music, dance and what not!&lt;br&gt;
During the pre-conference dinner, the speakers were felicitated with gifts and swags from ngIndia organiser, &lt;a href="http://twitter.com/debug_mode"&gt;Dhananjay Kumar&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9_AmL-eN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/5200/0%2AchlDq2wqnk6LyDvv" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9_AmL-eN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/5200/0%2AchlDq2wqnk6LyDvv" alt="Speakers felicitation" width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_nDO_Rnf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/3782/1%2A8NEKSMhHjZfdSAN5dcbHyg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_nDO_Rnf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/3782/1%2A8NEKSMhHjZfdSAN5dcbHyg.jpeg" alt="Speakers felicitation" width="800" height="1146"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I always love to speak at ngIndia and looking at the love from people who attend this event yearly, I am proud to be a home grown speaker of ngIndia, saying this since this is where my foray into speaking at events to share my learning started.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8_iL3Rky--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/5760/1%2AAxviKZXCCbDAQ9j9JPw84Q.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8_iL3Rky--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/5760/1%2AAxviKZXCCbDAQ9j9JPw84Q.jpeg" alt="My speaking moments at ngIndia" width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gRnkxXK---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/5760/1%2A0naRxhW_ns99Uk6XmYRxEg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gRnkxXK---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/max/5760/1%2A0naRxhW_ns99Uk6XmYRxEg.jpeg" alt="My speaking moments at ngIndia" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are some of the captured moments from the conference.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft1evqxrxulpu7318b3ve.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft1evqxrxulpu7318b3ve.jpg" alt="ngIndia moments" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fem5p56y3swhng7jo7v5k.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fem5p56y3swhng7jo7v5k.jpg" alt="ngIndia moments" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7qiiuh8lpff3az02dq6a.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7qiiuh8lpff3az02dq6a.jpg" alt="ngIndia moments" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffduz52hbfpktbyo4c40a.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffduz52hbfpktbyo4c40a.jpg" alt="ngIndia moments" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F32lwgqxiqawqi62bss7i.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F32lwgqxiqawqi62bss7i.jpg" alt="ngIndia moments" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fph5vltuc5otmmvmg4ayb.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fph5vltuc5otmmvmg4ayb.jpg" alt="ngIndia moments" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8gnty6bbqvce3uv7xhfb.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8gnty6bbqvce3uv7xhfb.jpg" alt="ngIndia moments" width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft85fz9q86zeihrooi2ha.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft85fz9q86zeihrooi2ha.jpg" alt="ngIndia moments" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9yr9bamxpqdkhuk9wa2b.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9yr9bamxpqdkhuk9wa2b.jpg" alt="ngIndia moments" width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fweh0f29goxqfr9vkokfs.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fweh0f29goxqfr9vkokfs.jpg" alt="ngIndia moments" width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8i3ktwawxdyozq277wn6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8i3ktwawxdyozq277wn6.jpg" alt="ngIndia moments" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7mnjwuiog4ddd2pmq4p6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7mnjwuiog4ddd2pmq4p6.jpg" alt="ngIndia moments" width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thank you!!!&lt;/p&gt;

&lt;p&gt;Stay tuned for the talk recordings :)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.ng-ind.com/"&gt;https://www.ng-ind.com/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>angular</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>svelte-i18next</title>
      <dc:creator>Nishu Goel</dc:creator>
      <pubDate>Fri, 08 Apr 2022 13:32:09 +0000</pubDate>
      <link>https://forem.com/nishugoel/svelte-i18next-1108</link>
      <guid>https://forem.com/nishugoel/svelte-i18next-1108</guid>
      <description>&lt;p&gt;Presenting the svelte wrapper for i18next.&lt;br&gt;
This library, based on i18next, wraps an i18next instance inside a svelte store and observes the i18next events like &lt;code&gt;languageChanged&lt;/code&gt;, &lt;code&gt;resource added&lt;/code&gt;, etc. to be able to render our svelte components.&lt;/p&gt;
&lt;h2&gt;
  
  
  Package:
&lt;/h2&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://www.npmjs.com/package/svelte-i18next" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--58xlbJJX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://static-production.npmjs.com/338e4905a2684ca96e08c7780fc68412.png" height="420" class="m-0" width="800"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://www.npmjs.com/package/svelte-i18next" rel="noopener noreferrer" class="c-link"&gt;
          svelte-i18next - npm
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          Svelte wrapper for i18next. Latest version: 2.0.0, last published: 3 months ago. Start using svelte-i18next in your project by running `npm i svelte-i18next`. There are no other projects in the npm registry using svelte-i18next.
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--lHsc1BmO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://static-production.npmjs.com/b0f1a8318363185cc2ea6a40ac23eeb2.png" width="32" height="32"&gt;
        npmjs.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  GitHub:
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/NishuGoel"&gt;
        NishuGoel
      &lt;/a&gt; / &lt;a href="https://github.com/NishuGoel/svelte-i18next"&gt;
        svelte-i18next
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Internationalization for svelte framework. Based on i18next ecosystem
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
svelte-i18next &lt;a href="https://twitter.com/intent/tweet?text=%40sveltejs%20wrapper%20for%20i18next%20%0Ahttps%3A%2F%2Fgithub.com%2FNishuGoel%2Fsvelte-i18next%0Avia%20%40TheNishuGoel%20%20&amp;amp;hashtags=i18next,sveltejs,svelte,javascript,webdev" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/90bc908826728c0e4261acfff5619fd732c7be2b2a00624fce6363c9a3623c90/68747470733a2f2f696d672e736869656c64732e696f2f747769747465722f75726c2f687474702f736869656c64732e696f2e7376673f7374796c653d736f6369616c" alt="Tweet"&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://github.com/NishuGoel/svelte-i18next/actions?query=workflow%3ACI"&gt;&lt;img src="https://github.com/NishuGoel/svelte-i18next/workflows/CI/badge.svg" alt="CI"&gt;&lt;/a&gt;
&lt;a href="https://www.npmjs.com/package/svelte-i18next" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/7fe16bdd53affc71e01bd55c72b30a0f713ee3c7c330f0993b23bd159581020e/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f7376656c74652d6931386e6578742e737667" alt="npm version"&gt;&lt;/a&gt;
&lt;a href="https://bundlephobia.com/package/svelte-i18next" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/9c33e45657306ae8658e536f0693110134f0534add281a13776b3dc33f8b7ae7/68747470733a2f2f696d672e736869656c64732e696f2f62756e646c6570686f6269612f6d696e7a69702f7376656c74652d6931386e6578743f6c6162656c3d677a697025323062756e646c65" alt="bundle size"&gt;&lt;/a&gt;
&lt;a href="https://github.com/NishuGoel/svelte-i18next/blob/master/LICENSE"&gt;&lt;img src="https://camo.githubusercontent.com/c68f415fb16d96a5bb7e6946e89f403e7e68f1ac6fe494718210c3b46347e4f6/687474703a2f2f696d672e736869656c64732e696f2f3a6c6963656e73652d6d69742d626c75652e737667" alt="License"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Svelte wrapper for &lt;a href="https://i18next.com/" rel="nofollow"&gt;i18next&lt;/a&gt;&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;npm i svelte-i18next i18next
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;
Implementation&lt;/h2&gt;
&lt;p&gt;This library wraps an i18next instance in a Svelte Store to observe &lt;a href="https://github.com/NishuGoel/svelte-i18next/blob/main/src/translation-store.ts#L23"&gt;i18next events&lt;/a&gt;
so your Svelte components re-render e.g. when language is changed or a new resource is loaded by i18next.&lt;/p&gt;
&lt;h2&gt;
Quick Start&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;i18n.js&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight highlight-source-ts notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-s1"&gt;i18next&lt;/span&gt; &lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s"&gt;"i18next"&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt; &lt;span class="pl-s1"&gt;createI18nStore&lt;/span&gt; &lt;span class="pl-kos"&gt;}&lt;/span&gt; &lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s"&gt;"svelte-i18next"&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;

&lt;span class="pl-s1"&gt;i18next&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;init&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt;
 &lt;span class="pl-c1"&gt;lng&lt;/span&gt;: &lt;span class="pl-s"&gt;'en'&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
 &lt;span class="pl-c1"&gt;resources&lt;/span&gt;: &lt;span class="pl-kos"&gt;{&lt;/span&gt;
    &lt;span class="pl-c1"&gt;en&lt;/span&gt;: &lt;span class="pl-kos"&gt;{&lt;/span&gt;
      &lt;span class="pl-c1"&gt;translation&lt;/span&gt;: &lt;span class="pl-kos"&gt;{&lt;/span&gt;
        &lt;span class="pl-s"&gt;"key"&lt;/span&gt;: &lt;span class="pl-s"&gt;"hello world"&lt;/span&gt;
      &lt;span class="pl-kos"&gt;}&lt;/span&gt;
    &lt;span class="pl-kos"&gt;}&lt;/span&gt;
  &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
  &lt;span class="pl-c1"&gt;interpolation&lt;/span&gt;: &lt;span class="pl-kos"&gt;{&lt;/span&gt;
    &lt;span class="pl-c1"&gt;escapeValue&lt;/span&gt;: &lt;span class="pl-c1"&gt;false&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c"&gt;// not needed for svelte as it escapes by default&lt;/span&gt;
  &lt;span class="pl-kos"&gt;}&lt;/span&gt;
&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;

&lt;span class="pl-k"&gt;export&lt;/span&gt; &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;i18n&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-en"&gt;createI18nStore&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;i18next&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;code&gt;App.svelte&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight highlight-source-svelte notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&amp;lt;&lt;span class="pl-ent"&gt;script&lt;/span&gt;&amp;gt;
&lt;span class="pl-s1"&gt;  &lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-smi"&gt;i18n&lt;/span&gt; &lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;./i18n.js&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;;&lt;/span&gt;
&amp;lt;/&lt;span class="pl-ent"&gt;script&lt;/span&gt;&amp;gt;

&amp;lt;&lt;span class="pl-ent"&gt;div&lt;/span&gt;&amp;gt;
    {&lt;span class="pl-smi"&gt;$i18n&lt;/span&gt;.&lt;span class="pl-en"&gt;t&lt;/span&gt;(&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;key&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;)}
&amp;lt;/&lt;span class="pl-ent"&gt;div&lt;/span&gt;&amp;gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;See full example project: &lt;a href="https://github.com/NishuGoel/svelte-i18next/blob/main/example"&gt;Svelte example&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/NishuGoel/svelte-i18next"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The implementation looks like this:&lt;br&gt;
The package creates a Svelte writable of i18next and listens to the events triggered by any updates on the i18next instance.&lt;br&gt;
For example, if an application loads a new namespace for their translations, the svelte_i18next package will trigger the&lt;br&gt;
&lt;code&gt;i18next.on(‘loaded’, function(loaded) {})&lt;/code&gt; event.&lt;br&gt;
Check the &lt;a href="https://www.i18next.com/overview/api#events"&gt;i18next&lt;/a&gt; events here.&lt;/p&gt;

&lt;p&gt;Usage:&lt;br&gt;
&lt;em&gt;i18n.js&lt;/em&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 i18next from "i18next";
import { createI18nStore } from "svelte-i18next";

i18next.init({
   lng: 'en',
   resources: {
     en: {
       translation: {
         "key": "hello world"
       }
     }
   }
});

export const i18n = createI18nStore(i18next);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;App.svelte&lt;/em&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;script&amp;gt;
  import i18n from './i18n.js';
&amp;lt;/script&amp;gt;

&amp;lt;div&amp;gt;
    {$i18n.t('key')}}
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See a example usage here: &lt;a href="https://github.com/NishuGoel/svelte-i18next/tree/main/example"&gt;Svelte example&lt;/a&gt;&lt;br&gt;
You could also use namespaces as you would using i18next and the svelte wrapper will re-render the translations based on the namespace provided.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { createI18nStore } from 'svelte-i18next'
const i18n = createI18nStore(i18n_instance) 
i18n.loadNamespaces(['example']) // this triggers the re-render and loads translations from the provided namespace.
&amp;lt;div&amp;gt;
    {$i18n.t(key)}
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Into svelte? Dealing with localisation?&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://twitter.com/intent/tweet?text=%40sveltejs%20wrapper%20for%20i18next%20%0Ahttps%3A%2F%2Fgithub.com%2FNishuGoel%2Fsvelte-i18next%0Avia%20%40TheNishuGoel%20%20&amp;amp;hashtags=i18next,sveltejs,svelte,javascript,webdev"&gt;Show some love @svelte-i18next&lt;/a&gt; ❤
&lt;/h2&gt;

</description>
      <category>svelte</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>dabbling @epilot-dev with ______</title>
      <dc:creator>Nishu Goel</dc:creator>
      <pubDate>Mon, 21 Mar 2022 08:59:12 +0000</pubDate>
      <link>https://forem.com/epilot/dabbling-epilot-dev-with--2b9j</link>
      <guid>https://forem.com/epilot/dabbling-epilot-dev-with--2b9j</guid>
      <description>&lt;p&gt;Year 2022 marked an amazing start for me with a new stint with epilot GmbH based out of Cologne, Germany. I joined the team to be working on their micro-frontend architecture, with my primary focus going to be on the performance aspects of the applications.&lt;br&gt;
Like the first recent addition we did after I started was to add &lt;a href="https://github.com/google/brotli"&gt;&lt;code&gt;brotli&lt;/code&gt; compression&lt;/a&gt; to each of our micro-frontend (MFEs) bundles.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--a5Sxs3ES--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/26349046/159230252-7ecd8335-f564-4180-ad15-6704127dad65.png" class="article-body-image-wrapper"&gt;&lt;img alt="gzip vs brotli" src="https://res.cloudinary.com/practicaldev/image/fetch/s--a5Sxs3ES--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/26349046/159230252-7ecd8335-f564-4180-ad15-6704127dad65.png" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s look at the tech stack involved.&lt;/p&gt;
&lt;h2&gt;
  
  
  Micro services
&lt;/h2&gt;

&lt;p&gt;The main &lt;code&gt;epilot&lt;/code&gt; app is built using &lt;code&gt;microfrontends&lt;/code&gt; with each entity as its own application. There is a &lt;code&gt;login&lt;/code&gt; application that takes care of the user login state (built using &lt;em&gt;React + TypeScript&lt;/em&gt;), a &lt;code&gt;topbar&lt;/code&gt; application that has features like Global search, notifications (&lt;em&gt;React + TypeScript&lt;/em&gt;), a &lt;code&gt;sidebar&lt;/code&gt; application that navigates the user to different entities based on user permissions (&lt;em&gt;Svelte + TypeScript + Tailwind&lt;/em&gt;), and so on.&lt;br&gt;
The component library of the product is based on material UI, built with React.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/@epilot/base-elements"&gt;https://www.npmjs.com/package/@epilot/base-elements&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Due to the open source experience of many engineers in the team, most of the non-confidential stuff is kept public on GitHub and npm.&lt;/p&gt;

&lt;p&gt;One of the very interesting things I got to do to ramp up was to build a part of the product as a Svelte MFE. With most of the existing MFEs built using react, this was quite interesting and challenging to do.&lt;/p&gt;

&lt;p&gt;We use &lt;a href="https://single-spa.js.org/"&gt;single-spa&lt;/a&gt; framework to build our &lt;code&gt;microfrontends&lt;/code&gt; and use different frameworks per the ease and knowledge of an engineer. To make the development journey easier for a new developer starting up with microfrontends, we have a &lt;code&gt;mfe-app-generator&lt;/code&gt; package that creates a boilerplate application with the required config.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/@epilot360/mfe-app-generator"&gt;https://www.npmjs.com/package/@epilot360/mfe-app-generator&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Due to performance reasons, one of the goals was to switch to &lt;code&gt;rollup&lt;/code&gt; for bundling all our applications, however, we use &lt;code&gt;webpack&lt;/code&gt; due to &lt;a href="https://github.com/aws/aws-sdk-js/issues/1769"&gt;rollup’s known issues with aws-sdk&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We use serverless microservices architecture exposed via APIs. Most of the backend services are written in TypeScript using AWS lambda functions, APIs, Cloudfront distribution etc. More on that &lt;a href="https://docs.epilot.io/docs/architecture/overview/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With Svelte added to our microfrontend stack, we plan to further diversify the frameworks to be used, ensuring to not lose focus on a performant application. &lt;br&gt;
&lt;a href="https://single-spa.js.org/docs/ecosystem/"&gt;Here’s a list of support frameworks by single-spa.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I also plan to share our journey from a high &lt;a href="https://web.dev/lcp/"&gt;&lt;code&gt;LCP&lt;/code&gt; (largest contentful paint)&lt;/a&gt; to a much reduced one, but I’d keep it for another blog post.&lt;/p&gt;

&lt;p&gt;When working with custom implementation and bundling, one of the challenging tasks is to configure your bundler and ensure it is consistent throughout all the applications (MFEs in this case). We have defined a &lt;code&gt;@epilot360/webpack-config-epilot360&lt;/code&gt; to base one’s webpack config on top of the existing basic config defined.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/@epilot360/webpack-config-epilot360"&gt;https://www.npmjs.com/package/@epilot360/webpack-config-epilot360&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I will share more on the webpack configuration for microfrontends and &lt;a href="https://webpack.js.org/guides/caching/"&gt;&lt;code&gt;cache invalidation&lt;/code&gt;&lt;/a&gt; strategies in another post but cache invalidation is definitely my major ongoing focus area.&lt;/p&gt;
&lt;h2&gt;
  
  
  Epilot SDK
&lt;/h2&gt;

&lt;p&gt;All public epilot services are available through an epilot-sdk (written in JS/TS) that allows developers to interact with epilot APIs. Internally as well, the recommended way to use the services or APIs using directly the SDK instead of going the granular way.&lt;br&gt;
For e.g.,&lt;/p&gt;

&lt;p&gt;&lt;code&gt;import { getClient } from 'epilot-sdk/entity-client'&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Want to scratch your itch? Find it here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/@epilot/epilot-sdk"&gt;https://www.npmjs.com/package/@epilot/epilot-sdk&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Communicating between microfrontends
&lt;/h2&gt;

&lt;p&gt;One of the interesting challenges I recently faced was to communicate between two applications without any shared library. One was the sidebar built using Svelte and another the topbar built with React. Now we usually tend to avoid any such communication between our MFEs but this was a state clear event to happen before a routing change in any of the two MFEs.&lt;/p&gt;

&lt;p&gt;I used single-spa’s custom events to do this. The idea was to clear the selected state of an icon in the topbar when an icon in the sidebar was selected, and vice versa.&lt;br&gt;
To do this, I used the &lt;code&gt;single-spa:before-routing-event&lt;/code&gt; custom event to check if the active URL in the current application was undefined, we could clear the state, if however, the URL existed, that becomes the active state.&lt;/p&gt;

&lt;p&gt;Checking the URL history without communicating between two applications was not possible due to two separate routers for each application. Svelte app using the &lt;code&gt;svelte-routing&lt;/code&gt; package where with React, &lt;code&gt;the react-router&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--avxxHzVu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/26349046/159228146-17669380-bf3a-4678-9d23-a964844f7a52.png" class="article-body-image-wrapper"&gt;&lt;img alt="svelte custom events" src="https://res.cloudinary.com/practicaldev/image/fetch/s--avxxHzVu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/26349046/159228146-17669380-bf3a-4678-9d23-a964844f7a52.png" width="800" height="669"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://single-spa.js.org/docs/api/#custom-events"&gt;Read more about the single spa custom events here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s a detailed view of the tech stack used at epilot:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NqW-Pf5l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/26349046/159228268-752aa37a-1974-4d88-b1e1-679efb8b4184.png" class="article-body-image-wrapper"&gt;&lt;img alt="tech stack used at epilot" src="https://res.cloudinary.com/practicaldev/image/fetch/s--NqW-Pf5l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/26349046/159228268-752aa37a-1974-4d88-b1e1-679efb8b4184.png" width="800" height="642"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.epilot.io/techradar"&gt;https://docs.epilot.io/techradar&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have shared more info on my journey to epilot as a full-stack developer in my Spotify Podcast here:&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://open.spotify.com/embed/episode/2C4KYDkRroA6Uc2QevdhLt" width="100%" height="232px"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Interested to know more about how we do things at epilot? Reach out to me here: &lt;a href="https://twitter.com/TheNishuGoel"&gt;https://twitter.com/TheNishuGoel&lt;/a&gt;&lt;/p&gt;

</description>
      <category>web</category>
      <category>programming</category>
      <category>career</category>
      <category>germany</category>
    </item>
    <item>
      <title>Creating framework-agnostic web components with Angular</title>
      <dc:creator>Nishu Goel</dc:creator>
      <pubDate>Thu, 12 Dec 2019 20:33:51 +0000</pubDate>
      <link>https://forem.com/nishugoel/creating-framework-agnostic-web-components-with-angular-ili</link>
      <guid>https://forem.com/nishugoel/creating-framework-agnostic-web-components-with-angular-ili</guid>
      <description>&lt;p&gt;Wait, framework-agnostic web components? What is that!&lt;br&gt;
Let us try to understand the meaning of this term by an example, apparently my own example.&lt;/p&gt;

&lt;p&gt;The setting in which I work at my company is based on a Garage concept. This translates to not having a single account which you work for, and instead getting to work on many short-duration, faster-delivery projects which keep flowing in and out of the garage. And the technology/framework for the deliverable is arrantly based on the requirement of the project.&lt;br&gt;
Therefore, this means that one can get to work on Angular for let’s say one 7-month project whereas React/Vue for another project for some amount of time.&lt;br&gt;
Now in this kind of setting where I have created lets say, a loader component for one of my projects in Angular, I wouldn’t want to redo the effort of creating a similar component for another project now that it is a react-based project let’s say. I would want something reusable, wouldn’t I?&lt;br&gt;
So this small story was to set the context of why would one want to create a Web Component instead of a regular component in any of the frameworks that they code in.&lt;/p&gt;

&lt;h1&gt;Web components though?&lt;/h1&gt;

&lt;p&gt;Does it mean components coming from the web?&lt;br&gt;
Well, the best way to explain web components would be to say that you can create a reusable custom element in any technology and use its functionality inside your web-apps by using the element.&lt;br&gt;
As per the MDN web docs, Web components are defined as:&lt;/p&gt;

&lt;h3&gt;Web Components is a suite of different technologies allowing you to create reusable custom elements — with their functionality encapsulated away from the rest of your code — and utilize them in your web apps.&lt;/h3&gt;

&lt;p&gt;The amazing thing about web components is that they only work on the web standards without the involvement of third-party libraries.&lt;br&gt;
Now to understand this term web component which has a bigger meaning in a simple way, lets try to understand what it actually contains.&lt;br&gt;
Like I mentioned before, web components follow certain web standards and work as per those. These web component specifications are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Custom Elements&lt;/li&gt;
&lt;li&gt;Shadow DOM&lt;/li&gt;
&lt;li&gt;HTML Template&lt;/li&gt;
&lt;li&gt;ES Module i.e. HTML Imports&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each one of these standards are independently capable of being used. To leverage Web Components, we combine these four web standards and use and re-use our created custom elements built with the functionality of the holding framework in any of the frameworks/libraries, vanilla JavaScript etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0Bzg7Q2f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/8h4lj2j3yu8srp7f9o8v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0Bzg7Q2f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/8h4lj2j3yu8srp7f9o8v.png" width="800" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let us look at each one of these one by one.&lt;/p&gt;

&lt;h2&gt;First Spec: Custom Elements&lt;/h2&gt;

&lt;p&gt;This is the first standard of Web component spec which allows us to create our own tags just like another HTML element but we decide which behavior should be contained inside that tag.&lt;br&gt;
A Custom Element can be created as:&lt;/p&gt;

&lt;pre&gt;class MyElement extends HTMLElement {
  […]
}
customElements.define("my-element", MyElement);&lt;/pre&gt;

&lt;p&gt;More about Custom Elements and the two types of custom elements that can be created can be read here.&lt;/p&gt;

&lt;h2&gt;Second Spec: Shadow DOM&lt;/h2&gt;

&lt;p&gt;The shadow DOM specification for web components basically means that the custom element would have supported style encapsulation and it wouldn’t conflict with the ID or class of your other elements in the DOM. It does so by creating a sub-DOM tree of the element.&lt;br&gt;
These elements enters as children of the element and have their own encapsulated styles.&lt;br&gt;
This works by using  element as a placeholder for the the custom element. &lt;/p&gt;

&lt;p&gt;More on the DOM API, Shadow DOM, slots etc. can be read here in this amazing article.&lt;/p&gt;

&lt;h2&gt;Third Spec: HTML Template&lt;/h2&gt;

&lt;p&gt;This third standard of the web component basically helps it to be have some chunk to the template to be loaded at runtime. This is achieved by using the  tag.&lt;br&gt;
Whatever is placed inside these template tags can be rendered at runtime by cloning and inserting them though JS.&lt;br&gt;
After retrieving the template tag in JS, you can activate it using:&lt;/p&gt;

&lt;h3&gt;var clonedObj = document.importNode(templateRef.content, true);&lt;/h3&gt;

&lt;p&gt;We can then append this using to the DOM as a sub-DOM tree.&lt;/p&gt;

&lt;h2&gt;Final Spec: HTML Import i.e. ES Module Specification&lt;/h2&gt;

&lt;p&gt;This specification helps you integrate your custom element with the different JS apps by defining the interface using the script.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;script type=”module”&amp;gt;&lt;br&gt;
import {example} from ‘@example/example’;&lt;br&gt;
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This will then allow us to use the custom tag inside our app like:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;example&amp;gt;…&amp;lt;/example&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;These four web component specifications help us understand how and why the components are to be used in which ever framework/ JS app as you wish to.&lt;br&gt;
Now that we have an understanding of Web components, in general, custom elements, Shadow DOM etc. We can proceed to create one custom element inside the Angular framework and be able to use it inside other library projects.&lt;/p&gt;

&lt;h1&gt;Creating a Web Component in Angular&lt;/h1&gt;

&lt;p&gt;Starting Angular v6, we can use regular Angular components as web components and load these components inside any of the frameworks/libraries or a JavaScript code.&lt;br&gt;
This is achieved using Angular Elements. Angular Elements is an Angular package that helps us create regular Angular components as Web components (custom elements) and use them in other technologies.&lt;/p&gt;

&lt;p&gt;As Rob Wormald mentioned in his talk,&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SuN21c_2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/qcnmc1famnv7eh4z3s9x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SuN21c_2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/qcnmc1famnv7eh4z3s9x.png" width="800" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To start with this, the first step would be to install the package @angular/elements .&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ng add @angular/elements&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iFgvp9P6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/ej6n44aif1arak712l8p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iFgvp9P6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/ej6n44aif1arak712l8p.png" width="800" height="305"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install --save @webcomponents/webcomponentsjs&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;import ‘@webcomponents/custom-elements/src/native-shim’;&lt;br&gt;
import ‘@webcomponents/custom-elements/custom-elements.min’;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The installation of @angular/elements also adds polyfills which are lighter in size but the support for some browsers is quite limited. Therefore, these two above-mentioned polyfills come to be of best use and thus we can avoid installing the polyfills of @angular/elements by simply doing:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install @angular/elements&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The next step is to create a traditional Angular component (to be used as a web component) which looks like this:&lt;/p&gt;

&lt;pre&gt;@Component({
  templateUrl: './custom-elements.component.html',
  styleUrls: ['./custom-elements.component.scss']
})
export class ComponentName {
  title = 'custom-elements-demo';
}&lt;/pre&gt;

&lt;p&gt;The selector name will be the one used while creating the web-component. Also, you would want to give it a generic name as this web component is going to be a re-usable one which you’d definitely want to use in your other projects as well.&lt;br&gt;
The next step then is to create the custom Element inside the module of your app. To do this, we first need to declare it inside the entryComponents array inside NgModule.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Note: Starting Angular v9, this would not be required as with Ivy, we do not need to explicitly specify a component as an entryComponent.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Inside the module class, define the custom element component now as:&lt;/p&gt;

&lt;pre&gt;//Inside NgModule  
  
  entryComponents: [CustomElementDemoComponent],

  })
  
  export class FeatureModule {
  constructor(private injector: Injector) {
  }

  ngDoBootstrap() {
      const customElement = createCustomElement(CustomElementDemoComponent, { injector: this.injector });
      customElements.define('custom-element', customElement);
  }
  }&lt;/pre&gt;

&lt;p&gt;Inside the module, We use the injector service to basically wrap this Angular component as a web component using the createCustomElement method from Angular Elements. customElements.define registers this component on to the browser. This is done inside the ngDoBootstrap method as you want to manually bootstrap the component instead of asking Angular to do it using the bootstrap array.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;custom-element&amp;gt;&amp;lt;/custom-element&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Once this is done, you can use it on an Angular component’s template as above and to make sure that the Angular component recognizes this custom Element, we add CUSTOM_ELEMENTS_SCHEMA in the schemas array in NgModule inside AppModule.&lt;br&gt;
You custom Element is now ready to be used and is accessible inside any of your Angular projects.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4R2mS-Zk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/x1fadz4lg9t9aul45ebs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4R2mS-Zk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/x1fadz4lg9t9aul45ebs.png" width="494" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;Wait, just Angular projects?&lt;/h2&gt;

&lt;p&gt;You now might be wondering that I mentioned about using these anywhere across apps created using different frameworks or in JavaScript. Well, officially Angular does not yet support stand-alone web components which can be used outside the Angular projects, however, you can still use these custom elements in other frameworks by a variety of ways. Let us look at that.&lt;/p&gt;

&lt;p&gt;The community project, ngx-build-plus by &lt;a href="https://twitter.com/ManfredSteyer"&gt;Manfred Steyer&lt;/a&gt;, allows us to create the required polyfills and the optional --single-bundle switch, helps create a single bundle which then exposes our web component.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;There is an amazing series of blog posts about Angular elements in detail by Manfred himself here.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To add it to your web component project,&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ng add ngx-build-plus&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This will update the build target in your angular.json to ngx-build-plus:build and update scripts related to polyfills.&lt;/p&gt;

&lt;p&gt;Also, remember we manually added polyfills to our web component project? We can avoid doing that as well with the help of ngx-build-plus!&lt;/p&gt;

&lt;p&gt;We can do this by using the command: &lt;code&gt;ng g ngx-build-plus:wc-polyfill&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now, to use the single-bundle flag, we would update our scripts in the package.json for building the web component.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;“build-my-comp”: “ng build --prod --single-bundle true --keep-polyfills”&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The single-bundle flag defaults to false so we set it to true explicitly in the build script. When you try to build your component now using npm run build-my-comp , you might get to see another error saying:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Schema validation failed with the following errors: Data path “.budgets[1].type” should be equal to one of the allowed values.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To solve this, remove the following lines of code from angular.json&lt;/p&gt;

&lt;pre&gt;{
“type”: “anyComponentStyle”,
“maximumWarning”: “6kb”,
“maximumError”: “10kb”
}&lt;/pre&gt;

&lt;p&gt;Finally, you will get a main bundle created now that can be exported to a Vanilla JS project, or any other framework and can be used as a web component, with, ofcourse, polyfills in place.&lt;/p&gt;

&lt;p&gt;To test this, in a basic project inside index.html, I included the file as:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;script src=”main.js”&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt; and used the custom element&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;custom-element&amp;gt;&amp;lt;/custom-element&amp;gt;&lt;/code&gt;&lt;br&gt;
To run this to be able to see the output now, you can install static-server from npm&lt;br&gt;
&lt;code&gt;npm i -g static-server&lt;/code&gt; , open the app on the port specified, and there you go with own web component in a non-Angular project (where the web component was created.)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wtEkR9fq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/mjfp9jbvz55hm6w8bvr3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wtEkR9fq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/mjfp9jbvz55hm6w8bvr3.png" width="684" height="464"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The web component can now be published to npm and used in other frameworks as well.&lt;br&gt;
For this blog post, I will be publishing this login form to the npm registry and using it in a React project.&lt;br&gt;
After building the web component using npm run build-my-comp , you get a single bundle generated for your web component, looking like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0xiiTLMt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/mmfgsxng2i4z5xjf0b8w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0xiiTLMt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/mmfgsxng2i4z5xjf0b8w.png" width="763" height="237"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can now publish it to npm by creating a package.json file in the dist directory. This needs only the name and version of the package that you will be publishing. To avoid the deletion of package.json every time you build the component, you can place it inside another folder.&lt;br&gt;
The package.json looks like:&lt;/p&gt;

&lt;pre&gt;{
    "name" : "&lt;a class="mentioned-user" href="https://dev.to/nishugoel"&gt;@nishugoel&lt;/a&gt;/prod-card",
    "version": "0.0.5"
}&lt;/pre&gt;

&lt;p&gt;We can now cd into the built package and pack it into a package to be published to npm using &lt;code&gt;npm pack&lt;/code&gt;. This will generate .tgzfile for you. To publish it to npm, make sure you are logged in to npm, if not, use npm login.&lt;/p&gt;

&lt;p&gt;Finally publish using,&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm publish packagedfile.tgz --access public&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Our web component is finally on npm and can be used in other frameworks now :)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mz1giWs6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/h9hdgblkj3eebbfoozhv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mz1giWs6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/h9hdgblkj3eebbfoozhv.png" width="800" height="255"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;Using my web component in React&lt;/h2&gt;

&lt;p&gt;Create a new react app and inside the index.html, use the reference to your main.js script from npm.&lt;/p&gt;

&lt;p&gt;//Inside index.html&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        &amp;lt;script src="https://unpkg.com/@nishugoel/prod-card@0.0.5/main.js"&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;//Use the custom element in the App.js file  &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        &amp;lt;prod-card username='Enter your name' password='Enter new password' btnname='Sign in'&amp;gt;&amp;lt;/prod-card&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;Install webcomponents.js&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;npm install --save @webcomponents/webcomponentsjs&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Put the required polyfills for &lt;code&gt;custom-elements-es5-adapter.js&lt;/code&gt; and &lt;code&gt;webcomponents-bundle.js&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And finally use the custom element in your app.js file with your own property values to the component.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lWl3BF7j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/hxl54xgah8w41lupznh9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lWl3BF7j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/hxl54xgah8w41lupznh9.png" width="800" height="684"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here we go!&lt;br&gt;
In the upcoming articles, I will be writing more on binding the data i.e. properties, events to the web components made in Angular.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>javascript</category>
      <category>webcomponents</category>
      <category>react</category>
    </item>
    <item>
      <title>Custom validation for your reactive forms?</title>
      <dc:creator>Nishu Goel</dc:creator>
      <pubDate>Sat, 27 Jul 2019 19:03:15 +0000</pubDate>
      <link>https://forem.com/nishugoel/custom-validation-for-your-reactive-forms-39l7</link>
      <guid>https://forem.com/nishugoel/custom-validation-for-your-reactive-forms-39l7</guid>
      <description>&lt;p&gt;When working with forms in Angular, we are provided with a choice. The choice is similar to the one between choosing tea or coffee. This choice is about choosing which types of forms we should use as per the requirement of our solution.&lt;br&gt;
And the two options that we get are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Template-driven forms&lt;/li&gt;
&lt;li&gt;Model-driven forms a.k.a Reactive Forms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now each of these have their own advantage over another and it totally depends on the developer that which one he prefers to use for this set of requirement. Is he looking to quickly get an input from the user without much interest in all the fancy validation and stuff OR is she wanting to leverage all the possibilities that a form is capable of providing.&lt;br&gt;
One of the reasons somebody would choose to work with template-driven forms can be to get the work done without really wanting to have a form model inside the component, leaving alone the unit testing bit which gets difficult to do against the DOM.&lt;/p&gt;

&lt;p&gt;One of the reasons somebody would choose to work with model-driven forms (Reactive forms) can be to do most of the task from the component class only putting the instances of the input fields on the template, making it easier to unit test and use up other great features of reactive forms, for example, Custom validation.&lt;/p&gt;

&lt;p&gt;In this blog post, we will see how we can use custom validation inside our forms and make the best use of reactive forms.&lt;br&gt;
Now a validator is nothing but a function. To create this validator fucntion, the syntax goes like this:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;function classValidator(control: AbstractControl) : {[key : string] : boolean} | null {&lt;br&gt;
return null;&lt;br&gt;
}&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Let us break this down now and understand.&lt;br&gt;
Since our custom validator is a function, we use the function keyword followed by the name of our validator that we want to create. As an argument comes the FormControl or FormGroup for which use the base class i.e., AbstractControl. The next part in it means what type of value will be returned by the function. So it there is something wrong with the input field, it returns an object in the form of a key-value pair, where the value is of type boolean and the key is of type string. If everything works fine with the input field, it returns null.&lt;br&gt;
So lets create a simple range validator for age here:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;function ageValidator (control: AbstractControl):{[key: string]: boolean} | null {&lt;br&gt;
if( control.value !==null &amp;amp;&amp;amp; (isNaN(control.value) || control.value &amp;lt;20  || control.value&amp;gt; 70)){&lt;br&gt;
    return {'ageValidator': true}&lt;br&gt;
  }&lt;br&gt;
  return null;&lt;br&gt;
};&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;To apply this to your input field, use the name of your custom validator on the form control name inside the form group,&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;this.customerForm = this.fb.group({&lt;br&gt;
      firstname: ['', [Validators.required, Validators.minLength(5)]],&lt;br&gt;
      email: ['', Validators.required],&lt;br&gt;
      age: [null, ageValidator]&lt;br&gt;
    })&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;On the template,&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;span *ngIf= “customerForm.get(‘age’).errors?.ageValidator”&amp;gt;Only for age group 20 to 70&amp;lt;/span&amp;gt;&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Let us look at the results in the browser now.&lt;/p&gt;

&lt;p&gt;Read more here: &lt;a href="https://nishugoel.wordpress.com/2019/07/27/custom-validation-for-your-reactive-forms/"&gt;https://nishugoel.wordpress.com/2019/07/27/custom-validation-for-your-reactive-forms/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>javascript</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Lazy Loading in Angular 8</title>
      <dc:creator>Nishu Goel</dc:creator>
      <pubDate>Sun, 09 Jun 2019 15:08:59 +0000</pubDate>
      <link>https://forem.com/nishugoel/lazy-loading-in-angular-8-5g54</link>
      <guid>https://forem.com/nishugoel/lazy-loading-in-angular-8-5g54</guid>
      <description>&lt;p&gt;We all know lazy loading is one of the most useful concepts of Angular Routing, and for those of us who have been working with Angular, we know how it brings down the size of large files. This is done by lazily loading the files that are required occasionally.&lt;/p&gt;

&lt;p&gt;To start with lazy loading by asynchronously loading the feature module for routing whenever required, we go to the route configuration and use the property loadChildren. Let us see what this property does.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    {path: ‘user’, loadChildren: ‘./users/user.module#UserModule’}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This property loadChildren is used for lazily loading the routes and is not related to child routes or such.&lt;/p&gt;

&lt;p&gt;Let us break down what the property’s value means. The loadChildren property accepts a string value which contains the route to the feature module followed by a hash symbol and then the name of the feature module.&lt;/p&gt;

&lt;p&gt;Now when the route gets activated, this loadChildren property will get activated and load the requested module. It will then load the requested component and display that component’s template.&lt;/p&gt;

&lt;p&gt;Once we have configured this property, we go to the console to see which files are generated.&lt;/p&gt;

&lt;p&gt;We will see an extra bundle file generated.&lt;br&gt;
Now if you go to the network tab in the console to see the files generated on routing to the UserModule, you will see one extra file created with some numeric value which might look something like this:&lt;/p&gt;

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

&lt;p&gt;This is how lazy loading gets implemented using the loadChildren feature in the route configuration for the specific feature module. And this creates another bundle file only when that route is navigated to and the data is requested.&lt;/p&gt;

&lt;p&gt;This is how we have been working with lazy loading till now, right?&lt;/p&gt;

&lt;p&gt;But…&lt;br&gt;
If we look at the route config again,&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  {path: ‘user’, loadChildren: ‘./users/user.module#UserModule’}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;the loadChildren property accepts a string which means that even if there is a wrong module name or a typo while writing the code, Angular would not know there is something wrong and accept whatever is there as a string until we try building it.&lt;/p&gt;

&lt;p&gt;So, let us say we write the config as :&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   {path: ‘user’, loadChildren: ‘./users/user.module#UserModulee’},
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;with an extra ‘e’, it will not throw any error considering it a part of the string.&lt;/p&gt;

&lt;p&gt;Therefore,&lt;br&gt;
Angular 8 comes up with support for dynamic imports in our router configuration. This means that we use the import statement for lazy loading the module and this will be understood by the IDEs, webpack, etc.&lt;/p&gt;

&lt;p&gt;So when you update to Angular 8, this will automatically accommodate the changes in your application.&lt;/p&gt;

&lt;p&gt;Now if you look at how lazy loading is done in this new route config, you will see:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; {path: ‘user’, loadChildren: () =&amp;gt; import(‘./users/user.module’).then(m =&amp;gt; m.UserModule)};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now your editor, let’s say VSCode understands what is this syntax and will recognize if there is some mistake and we won't have to wait till build time to realize about an error.&lt;/p&gt;

&lt;p&gt;This new syntax now means that loadChildren is a function which will execute when it tries to access the user module. This will asynchronously load the import statement and implement the module.&lt;/p&gt;

&lt;p&gt;This new lazy loading is explained by Stephen Fluin in this video: &lt;a href="https://www.youtube.com/watch?v=jPXl7sCPCOA" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=jPXl7sCPCOA&lt;/a&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Angular Library with ng-packagr</title>
      <dc:creator>Nishu Goel</dc:creator>
      <pubDate>Mon, 03 Jun 2019 06:31:58 +0000</pubDate>
      <link>https://forem.com/nishugoel/angular-library-with-ng-packagr-4a5m</link>
      <guid>https://forem.com/nishugoel/angular-library-with-ng-packagr-4a5m</guid>
      <description>&lt;p&gt;When you have a small application with three or more components, and they do not share a relationship among themselves when it comes to sharing data through component communication. For such components, we have seen the usage of services. Services come to be of great help when we have to use the same data in more than one components. This data is then used inside the component by using dependency injection.&lt;/p&gt;

&lt;p&gt;This is when we have the components inside one module which contains the service. However, if we want to use the data from the service in multiple modules, a better idea is to put this data inside the service somewhere, from where you can actually import it in whichever module and component you wish to.&lt;/p&gt;

&lt;p&gt;Now, this is about one application, what if you want maximum applications to use the same data and you definitely do not want to copy paste the code for each of your applications. Also, you do not want any of the applications to communicate with each other, but just share some data from an external source to all the applications.&lt;/p&gt;

&lt;p&gt;That is when Libraries and packages come into the picture. In this blog post, we will see how to create an Angular library and what are the requirements to create one. When researching more about Angular libraries, I came across this great talk Create and publish Angular libs like a Pro by Juri Strumpflohner wherein he explains the need for a library with this image.&lt;/p&gt;

&lt;p&gt;Read more here: &lt;a href="https://nishugoel.wordpress.com/2019/05/12/angular-library-with-ng-packagr/"&gt;https://nishugoel.wordpress.com/2019/05/12/angular-library-with-ng-packagr/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>library</category>
      <category>package</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Angular Library with ng-packagr</title>
      <dc:creator>Nishu Goel</dc:creator>
      <pubDate>Wed, 29 May 2019 05:50:17 +0000</pubDate>
      <link>https://forem.com/nishugoel/angular-library-with-ng-packagr-1o5c</link>
      <guid>https://forem.com/nishugoel/angular-library-with-ng-packagr-1o5c</guid>
      <description>&lt;p&gt;NOTE: The article is relevant to the earlier versions of Angular and with the version 7 and above, we can create a library in Angular using the command ng generate library  . For more on this, refer to the documentation here: &lt;a href="https://next.angular.io/guide/creating-libraries"&gt;https://next.angular.io/guide/creating-libraries&lt;/a&gt;&lt;br&gt;
When you have a small application with three or more components, and they do not share a relationship among themselves when it comes to sharing data through component communication. For such components, we have seen the usage of services. Services come to be of great help when we have to use the same data in more than one components. This data is then used inside the component by using dependency injection.&lt;br&gt;
This is when we have the components inside one module which contains the service. However, if we want to use the data from the service in multiple modules, a better idea is to put this data inside the service somewhere, from where you can actually import it in whichever module and component you wish to.&lt;br&gt;
Now, this is about one application, what if you want maximum applications to use the same data and you definitely do not want to copy paste the code for each of your applications. Also, you do not want any of the applications to communicate with each other, but just share some data from an external source to all the applications.&lt;br&gt;
That is when Libraries and packages come into the picture. In this blog post, we will see how to create an Angular library and what are the requirements to create one. When researching more about Angular libraries, I came across this great talk Create and publish Angular libs like a Pro by Juri Strumpflohner wherein he explains the need for a library with this image.&lt;br&gt;
It explains how our applications comprise of different javascript files and other external libraries and tools and how they are assembled together.&lt;br&gt;
To start with, let us look at the points to consider when creating a library.&lt;br&gt;
A library should be:&lt;br&gt;
Distributed&lt;br&gt;
Platform-independent&lt;br&gt;
Bundled&lt;br&gt;
Supporting Ahead-of-time compilation&lt;br&gt;
Work with TypeScript doing type checking, autocompletion, etc.&lt;/p&gt;

&lt;p&gt;All these requirements together make a perfect Angular library, and it has come up with a solution called Angular Package format to support building the Angular libraries. This format is a recommended way of building Angular libraries and distributing the packages. Libraries which are built using this format will be handled properly by the Angular applications, node packages, npm.&lt;br&gt;
It is a set of guidelines which Angular team uses to distribute packages and if we follow those guidelines while developing an Angular library, we put ourselves on a safe side because then our library is supported by a lot of tools that are used in the ng ecosystem and the packages then integrate easily with these tools. It will also have an optimized bundle size and a build time which are some of the very important factors.&lt;br&gt;
Steps to produce an Angular library&lt;br&gt;
Since the library should be bundled all in once place, the first step should be to inline all the external templates.&lt;br&gt;
The next step is to compile this with the ngc compiler which is sort of a wrapper around the tsc. The ngc will produce the javascript files out of the typescript ones and also produce the the d.ts files to do the Ahead-of-time compilation by creating the metadata for this library.&lt;br&gt;
Build different ESM2015, UMD formats.&lt;/p&gt;

&lt;p&gt;ng-packagr&lt;br&gt;
We can developer libraries using the ng-packagr, which is a project on nom and Github. These libraries can then be put inside the Angular Package Format. The libraries create all the bundles for us. It is consumed by different module loaders, inline the templates by itself, creates the type definitions (.d.ts files)&lt;br&gt;
To install ng-packagr using npm, so that it can then be used globally from the command line,&lt;/p&gt;

&lt;p&gt;Read more here: &lt;a href="https://medium.com/@nishu0505/angular-library-with-ng-packagr-13f20ed202bd"&gt;https://medium.com/@nishu0505/angular-library-with-ng-packagr-13f20ed202bd&lt;/a&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>packages</category>
      <category>library</category>
    </item>
    <item>
      <title>IWD 2019 — GDG Makurdi</title>
      <dc:creator>Nishu Goel</dc:creator>
      <pubDate>Fri, 05 Apr 2019 21:06:38 +0000</pubDate>
      <link>https://forem.com/nishugoel/iwd-2019-gdg-makurdi-13j</link>
      <guid>https://forem.com/nishugoel/iwd-2019-gdg-makurdi-13j</guid>
      <description>&lt;p&gt;In the March of 2019, Pius contacted me to share my knowledge about the basic concepts of Angular with the audience of Makurdi, Nigeria. It was organised as a celebration of International Women’s Day 2019.&lt;/p&gt;

&lt;p&gt;On the 31st of March 2019, I joined the audience in Makurdi via a Hangout session and discussed the topics starting from the basic set up required to work on an Angular application to component communication, directives etc.&lt;/p&gt;

&lt;p&gt;This was my first online session teaching an international audience and there was no less energy in the crowd than that in an offline/physical session.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://thepracticaldev.s3.amazonaws.com/i/igz0frk6oi7u5hx7929y.jpeg"&gt;https://thepracticaldev.s3.amazonaws.com/i/igz0frk6oi7u5hx7929y.jpeg&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://thepracticaldev.s3.amazonaws.com/i/8ho0soo6rbtgoebmrdei.jpeg"&gt;https://thepracticaldev.s3.amazonaws.com/i/8ho0soo6rbtgoebmrdei.jpeg&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, the session could not be recorded due to network issues.&lt;br&gt;
The topics taught in the session are covered in this Beginners course on Angular by me here: &lt;a href="https://www.udemy.com/angular-for-beginners-l/"&gt;https://www.udemy.com/angular-for-beginners-l/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Contact me at: &lt;a href="mailto:nishu0505@gmail.com"&gt;nishu0505@gmail.com&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
