<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Technology at upGrad - Medium]]></title>
        <description><![CDATA[Building careers of Tomorrow - Medium]]></description>
        <link>https://engineering.upgrad.com?source=rss----c08812b771e6---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>Technology at upGrad - Medium</title>
            <link>https://engineering.upgrad.com?source=rss----c08812b771e6---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Tue, 14 Apr 2026 09:34:34 GMT</lastBuildDate>
        <atom:link href="https://engineering.upgrad.com/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Web Performance and Related Stories — upgrad.com]]></title>
            <link>https://engineering.upgrad.com/web-performance-and-related-stories-upgrad-com-a9fb9c6bb766?source=rss----c08812b771e6---4</link>
            <guid isPermaLink="false">https://medium.com/p/a9fb9c6bb766</guid>
            <category><![CDATA[performance]]></category>
            <category><![CDATA[nuxtjs]]></category>
            <category><![CDATA[upgrad]]></category>
            <category><![CDATA[lighthouse]]></category>
            <category><![CDATA[web-performance]]></category>
            <dc:creator><![CDATA[Mohit Karekar]]></dc:creator>
            <pubDate>Fri, 11 Jun 2021 06:37:45 GMT</pubDate>
            <atom:updated>2021-06-15T16:46:26.837Z</atom:updated>
            <content:encoded><![CDATA[<h3>Web Performance and Related Stories — upgrad.com</h3><p><em>The story and learnings from improving upgrad.com’s (a Nuxt.js SSR application) Lighthouse performance scores from 17 to 65</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*QBnDE4-Mu_jnLSP8Z2lcQQ.png" /></figure><p>In the month of January 2021 we, at upGrad, looked at our website’s page speed scores and accepted the fact that they demanded considerable improvement. Lighthouse is a popular and extremely useful tool to measure web performance, and well, the scores for upgrad.com were barely crossing two-digit values. The situation wasn’t great, but personally it was exciting for me because it presented a live problem to be solved, and this post is about the journey through the rough seas of web performance.</p><blockquote><strong>tl;dr<br></strong><em>We were successful in improving our website performance in terms of the Lighthouse scores going from about </em><strong>5 and 15 to around 50 and 70</strong><em> on mobile and desktop respectively. I hope these numbers will encourage you to read the entire post, but I’ll also try to make it interesting in general to read. See you until the end!</em></blockquote><p>I consider this domain of web-development to carry a large importance for both — <em>the developer working on the website/application, and the business/person owning the product</em>. The situation’s better when you play both of these roles, let’s say when you launch your side project. Web performance is an area which is interesting to work on as a tech person, as well as has direct impact on the business side of things. If you’ve run your Lighthouse tests quite often (it is time to automate them), you might have seen these facts being shown.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kWQI5aVwIa717hUT1sBtUQ.png" /></figure><p><a href="https://web.dev/why-speed-matters/"><strong>Speed does matter</strong></a>. As users of the internet ourselves, we know how frustrating it is to wait for a page to load. I sometimes empathize web developers of certain websites if they load slowly, and wait for a few more seconds. Unfortunately, real world users won’t do that. As everything is becoming more and more instant, giving the best experience on the web is one of the core responsibilities of a web developer.</p><h3>The January of 2021</h3><p>I do not want to sound poetic, but let’s go back to the January of 2021. The growth team at upGrad had worked on a crucial project this month — the separation of authentication logic from the website, and integrating a mobile OTP-based login+signup flow. It was an interesting project and Akshay (my teammate) is soon coming up with an article about it. But given that, now the upgrad.com website consisted only of representational content. In an ideal world, the website <em>could</em> have been built with plain HTML &amp; CSS, but having a framework like Nuxt.js gives us a lot of benefits in terms of operability and dynamism, given that we have an in-house CMS and editors.</p><p>Anyway, the Lighthouse scores for the website were as follows (for desktop):</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*okRn0e4T_SrYXqFHgeB8-w.png" /></figure><p>A 17 for desktop usually means your mobile scores sit somewhere lower, around 8–10. Things to note in the above screenshot are the values for the web vitals, <strong>most of them in red</strong>. Lighthouse considers these values to be important denoting factors about how your website loads and how it is perceived by the users. Let’s take a short detour into how a browser loads a page, the web vitals then would be much clearer on the way.</p><h3>Journey of a page</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*cfQpu6Xvb7e9IiH4CCuiCg.png" /><figcaption>Source: <a href="https://www.html5rocks.com/en/tutorials/internals/howbrowserswork/">https://www.html5rocks.com/en/tutorials/internals/howbrowserswork/</a></figcaption></figure><p>A browser usually goes through the above flow whenever it receives its first chunk of HTML from the server. HTML is a special language, <strong><em>it is forgiving</em></strong>. And hence its parsing is special too. To go a bit into details, HTML does not have a Context-Free Grammar, as most of the languages would have. This grammar tells what is valid and what is invalid in a language. In HTML, the browser usually corrects if some parts are erroneous. In addition to this, HTML can change while parsing is still happening! JavaScript can inject HTML into the page using document.write() and because of this HTML parsing is called <em>re-entrant</em>.</p><p>If we consider a short example page’s HTML</p><h4>Scenario 1</h4><pre>&lt;html&gt;<br>    &lt;body&gt;<br>        &lt;h1&gt;Hello&lt;/h1&gt;<br>    &lt;/body&gt;<br>&lt;/html&gt;</pre><p>Here, the parser will start from the top and go on identifying corresponding nodes. It will find open html -&gt; open body -&gt; open h1 -&gt; text Hello -&gt; close h1 -&gt; close body -&gt; close html. At the end of the parsing you’d generally have a syntax tree which will mirror a corresponding DOM tree. The object would be the one which will be exposed to JavaScript inside document.</p><p>Essentially, the DOM tree would be constructed, laying out would be done and then the bitmap would be painted on the screen. Super optimal.</p><h4>Scenario 2</h4><pre>&lt;html&gt;<br>    &lt;head&gt;<br>        &lt;link <em>rel</em>=&quot;stylesheet&quot; <em>href</em>=&quot;/main.css&quot; /&gt;<br>    &lt;/head&gt;<br>    &lt;body&gt;<br>        &lt;h1&gt;Hello&lt;/h1&gt;<br>    &lt;/body&gt;<br>&lt;/html&gt;</pre><p>Let’s consider this example which adds a remote CSS stylesheet to the page. Any form of <strong>CSS on the page is render-blocking</strong>. It means that until all the CSS isn’t parsed, the renderer won’t paint the page — remember from the above flow diagram that the render tree construction requires the CSS tree to be ready.</p><p>Hence in this example, the DOM tree would be constructed, but the rendering would not happen until the CSS file is loaded and parsed completely. A bit less optimal.</p><h4>Scenario 3</h4><pre>&lt;html&gt;<br>    &lt;head&gt;<br>        &lt;link <em>rel</em>=&quot;stylesheet&quot; <em>href</em>=&quot;/main.css&quot; /&gt;<br>        &lt;script&gt;console.log(&#39;Hello from JavaScript!&#39;);&lt;/script<br>    &lt;/head&gt;<br>    &lt;body&gt;<br>        &lt;h1&gt;Hello&lt;/h1&gt;<br>    &lt;/body&gt;<br>&lt;/html&gt;</pre><p>Let’s add a &lt;script&gt; tag to the head. Any form of <strong>JavaScript on the page is usually parser-blocking</strong>. The HTML parsing is blocked until the script is executed — this is again because JS has the potential to modify the DOM, so the browser wouldn’t want to proceed and later get to know that the code it had parsed was stale. You can think how this might get affected if you have remote JavaScript files, which is usually the case. The files would first have to be downloaded and then executed for the parser to move ahead. Very un-optimal.</p><p>Mozilla Developer Network documentation as well as Google’s web.dev articles explain this in great detail. You might want to look into those.</p><p>The crux of the detour above was to discuss what could potentially hinder our website from being loaded/visible to the users. From the discussion we can take two key points:</p><ul><li>CSS is render-blocking</li><li>JavaScript is parser-blocking</li></ul><p>These two points form a base to a large number of optimizations that one can perform to speed up their website. The core web vitals also get affected due to these.</p><p>Sourced directly from <a href="https://web.dev:">https://web.dev:</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5V9pbAoFLzNJYA2uRK0bKw.png" /></figure><p>Anything render-blocking will worsen LCP. Anything process heavy on page load will worsen FID, e.g. synchronous JavaScript execution on main thread. Anything writing to the document and hence drastically changing the page layout will worsen CLS.</p><p>For the initial phases our target was reducing LCP. Additional reading:</p><p><a href="https://developers.google.com/web/fundamentals/performance/get-started">Overview | Web Fundamentals | Google Developers</a></p><h3>Identifying Areas of Improvement &amp; Budgets</h3><p>To tackle the problem of performance, usually one can look internally first (quite philosophical). Before performing a complex change in your system, one must try to find areas to improve in the existing setup. It is quite probable that there would be a lot to clean up and optimize. At least that was the case with us.</p><p>But before beginning any work, how would you know what to pick and how that change would impact the performance? Enter performance analysis tools. There are numerous tools available online that help you know exactly where the bottleneck is. Lighthouse is one of such tools but I believe it should be used to benchmark the final score. There is another online tool called <a href="https://webpagetest.org/"><strong>WebPageTest</strong></a> that gives you detailed report of your website’s performance under various scenarios. I personally found is very useful to identify individual problems.</p><p>Following is a screenshot from webpagetest.org:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*y5MJRobxWlYjTqITVKp2EQ.png" /></figure><p>The above report gives a detailed view of what is happening on the page — from the vitals to the waterfall view and the vitals marked on it. It was particularly helpful to map what was delaying certain metrics and then target those resources specifically.</p><h4>Budgeting</h4><p>This is a debatable topic but everyone can have their own preferred ways of budgeting your optimizations. This helps us maintain a threshold for either metrics or the experience that we want to provide to our users. I’ll keep this section short and link to useful resources online:</p><ul><li><a href="https://web.dev/defining-core-web-vitals-thresholds/">https://web.dev/defining-core-web-vitals-thresholds/</a></li><li><a href="https://csswizardry.com/2020/01/performance-budgets-pragmatically/">https://csswizardry.com/2020/01/performance-budgets-pragmatically/</a></li></ul><p>We began with some obvious areas that we could target — something that we knew already but could not find time to fix. These included some unused scripts and some modules that were being loaded globally when we could load them locally. In Nuxt, you can define global plugins that get included in the main bundle — often a bad practice if not monitored properly. So in short, here are a few key areas that can be targeted:</p><h4>Lazy-load Everything Unnecessary</h4><p><strong><em>Impact — Time To Interactive (TTI), First Contentful Paint (FCP)</em></strong></p><p>You <em>must</em> defer/lazily-load everything that is not crucial for your first load and does not contribute to SEO. This includes modals, non-critical scripts, images, stylesheets and chunks of your application.</p><p>For our website, we saw a considerable difference when we lazy-loaded all our modal content. Since it was a separated module in itself, we could do this easily. Most of the frameworks have special syntax for lazy-loading components, e.g. React.lazy() in React and the import() syntax in Vue. Bundlers provide automatic code-splitting based on this syntax so that your chunks are separate and are only loaded when requested for.</p><p>Since Nuxt uses webpack, we could use <strong>webpack’s magic comments</strong> that allow you to define loading behaviours on chunks. /* webpackPrefetch: true */ is one such useful comment that pre-fetches the chunk so that the experience is similar to loading it synchronously.</p><p><strong>Image lazy-loading</strong> is core to faster websites and usually is already implemented. But if it isn’t then you should do it right away. There are popular libraries that allow this and now there’s also a native way of doing it in HTML, though it isn’t supported in all browsers.</p><p>The standard way of telling the browsers to defer loading a script, or load them asynchronously is by using the defer and async keywords correctly. <strong>defer moves the processing of a script to the end of parsing step</strong>. The script is fetched in the background and in parallel if many. This solves the issue mentioned in ‘Scenario 3’ above. That is also why usually it is recommended to keep your script tags at the end of the body. This helps in two ways — it allows the script to ‘see’ the entire body of the page at it was built before the parser reached the script tag, and second that the parses did not get blocked on the script somewhere at the beginning of the page.</p><p>async helps in a similar way but instead of loading the scripts in the end, <strong>they are loaded and even processed asynchronously</strong>. These won’t wait for the DOM tree being built, but get executed as soon as they complete loading. A key difference between async and defer is that all scripts loaded with defer will block the DOMContentLoaded event while those loaded with async would not.</p><p>Another useful thing that can be leveraged off of frameworks is the <strong>dynamic loading of scripts</strong>. Keeping the core idea in mind — <em>load only those resources that are required to build the page </em>— we can dynamically push &lt;script&gt; tags in our website’s head. This allows, e.g. loading the YouTube player script only on pages that actually display a video, ensuring that does not slow down a page that does not have a video at all.</p><h4>Limit Critical Resources — Shorten the Critical Rendering Path</h4><p><strong><em>Impact — First Contentful Paint (FCP), Largest Contentful Paint (LCP)</em></strong></p><p><strong>Critical rendering path</strong> (CRP) is the journey of a page being rendered in a browser from the point when the browser receives the first bytes of the response (HTML) to when the page starts becoming visible. Our aim should be to keep this as short as possible. As resources get added to this path, the time to start painting goes on increasing, hampering the page load experience.</p><p>CRP depends on the way your HTML document is parsed. If it is only static HTML, you would have a great LCP score. But practically, there are a lot of resources that usually get added on a HTML page, that are at times crucial for the website to behave properly. If these resources are required for your page to render, then they contribute to CRP. The parser has to do the work of loading and executing them and then continue to parse the page and then finally the renderer to take over.</p><p>Good resources:</p><p><a href="https://developers.google.com/web/fundamentals/performance/critical-rendering-path/">Critical Rendering Path | Web Fundamentals | Google Developers</a></p><p>For us, it was about looking at the waterfall snapshot from webpagetest and see what was delaying the first render. Upon checking, we could find that a particular CSS file was being loaded critically and to worsen the problem, it wasn’t being served via a CDN.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3iaZ8Cc33EkYklqpqqiXkQ.png" /></figure><p>Finding such resources and unblocking them helped us improve our start render times by a good number —<strong><em> old one being at 4.2 seconds, improved to 0.4 seconds.</em></strong></p><p>The initial HTML response of your page must contain only the critical CSS — CSS required for your page to be properly styled. All others can be lazily loaded. One popular way to <strong>asynchronously load CSS</strong> is using the media attribute of link tag.</p><pre>&lt;link<br>    <em>rel</em>=&quot;stylesheet&quot;<br>    <em>href</em>=&quot;/link/to/file.css&quot;<br>    <em>media</em>=&quot;print&quot;<br>    <em>onload</em>=&#39;<em>this</em>.media=&quot;all&quot;,<em>this</em>.onload=null&#39;<br>/&gt;</pre><p>Here, since the media value is ‘print’, the HTML parser in a browser would skip waiting on this stylesheet and move ahead. The stylesheet will still load in the background, and whenever it does, it will get parsed. This prevents blocking of the parser at the CSS link tag and is good for stylesheets that aren’t required at the time of first load.</p><p>Even with the above solution, we must ensure that the critical CSS is lean. Tools like <a href="https://purgecss.com/"><strong>PurgeCSS</strong></a> help in automating this by cleaning out the styles that are unused on the page by going over the HTML of the finally generated page.</p><h4>Preloading Critical Assets</h4><p><strong><em>Impact — Largest Contentful Paint (LCP)</em></strong></p><p>One other important practice is to <strong>preload all your critical resources</strong>. If your website has an image in the first fold, you must preload it so that it is visually available as soon as possible.</p><p>One can use the preload keyword to do this and many bundlers do this automatically for you.</p><pre>&lt;link <em>rel</em>=&quot;preload&quot; <em>as</em>=&quot;image&quot; <em>href</em>=&quot;banner-image.webp&quot; /&gt;</pre><p>More on preload here:</p><p><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload">Link types: preload - HTML: HyperText Markup Language | MDN</a></p><p>Applying these practices brought down our LCP largely — <strong><em>from 3.9 seconds to 0.9 seconds.</em></strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*as8e_vFbraac5kzl3WcIEw.png" /></figure><p>Google’s web.dev sums up the above points in a very good way — it introduces the <strong>PRPL pattern</strong>.</p><ul><li>P — Push (or preload) the most important resources.</li><li>R — Render the initial route as soon as possible.</li><li>P — Pre-cache remaining assets.</li><li>L — Lazy load other routes and non-critical assets.</li></ul><p>Apart from pre-caching, I think I talked about the rest of the points. Pre-caching helps in improving the performance of the subsequent loads by caching assets that have a higher time-to-live.</p><h3>Overall Improvements in upgrad.com’s Performance</h3><p>Since the website runs on a server-side rendered Nuxt, it poses some special conditions to approach performance. Any SSR framework, accompanied with a CDN cache, guarantees a great TTFB and LCP, but suffers in time to interactivity as there’s a re-hydration process that happens on the front-end.</p><p>A short description of how SSR works in Nuxt (from <a href="https://nuxtjs.org/docs/2.x/concepts/server-side-rendering">nuxtjs.org</a>):</p><blockquote>Server-side rendering (SSR), is the ability of an application to contribute by displaying the web-page on the server instead of rendering it in the browser. Server-side sends a fully rendered page to the client; the client’s JavaScript bundle takes over which then allows the Vue.js app to hydrate.</blockquote><p><a href="https://nuxtjs.org/docs/2.x/concepts/server-side-rendering">Server Side Rendering</a></p><p>Considering this, we only targeted the rendering and preloading bit of the entire process. We have plans to move to complete static rendering in the future which should solve the problem of hydration.</p><p>Anyway, we did this exercise over a period of <strong>4 months</strong>, and occasionally worked on performance specific tasks. I believe most of the work in this field has to be slow and steady. There’s a fair amount of trial and error, research and reading required when you begin. But consistent efforts towards improving and then maintaining performance do pay off well. This is how the LCP (in a desktop setup) improvement for us looked over time:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AVWLPIod04llfLLmoY0MHQ.png" /></figure><p>Our Lighthouse scores went up as well, as mentioned in the beginning of the article. Current score:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BS0gm24ASh0wxTOREMBYQA.png" /></figure><p>I have thoroughly enjoyed working on this and have learnt quite a lot about how pages get rendered and how to make them fast. We are targetting to a even better score as there is still a room for improvement, but honestly the current score is a good one to take a break and look back at it with a fresh mind. As next steps, this is what we are looking at:</p><ol><li><strong>Performance Benchmarking</strong> — We’ve improved the scores, but it’s our job now to maintain them there. Continuous monitoring is a crucial part of maintaining performance. There are tools available out there, or you can easily make one on your own (I did that :P, will link it later).</li><li><strong>Performance-optimised components</strong> — We’re looking towards replacing certain shared UI components with lighter-weight alternatives.</li><li><strong>Brotli Compression &amp; Infra-level changes</strong> — Brotli is enabled but needs some improvement in terms of the implementation. Also, infra-wise we look to reduce the number of hops required before reaching our origin server.</li></ol><p>This entire feat wouldn’t have been possible without my cooperative team, my manager Maitrey and Rohan. Me, Rohan and Maitrey have had numerous meetings to discuss this and have always worked towards building a performant and efficient platform. Things have worked like magic! Also, kudos to product leaders — Rohit &amp; Shahir, to look at this as a direct business impact (and constantly pinging us about the website being slow :D). And finally, thanks to engineering leaders Vishal and Puneet for constantly supporting this entire exercise.</p><p>I’ve already linked relevant resources as and when required, but for all others, here’s a collated list:</p><ul><li><a href="https://www.smashingmagazine.com/2021/01/front-end-performance-2021-free-pdf-checklist/">https://www.smashingmagazine.com/2021/01/front-end-performance-2021-free-pdf-checklist/</a></li><li><a href="https://www.smashingmagazine.com/2021/01/smashingmag-performance-case-study/">https://www.smashingmagazine.com/2021/01/smashingmag-performance-case-study/</a></li><li><a href="https://web.dev/optimize-lcp/">https://web.dev/optimize-lcp/</a></li><li><a href="https://web.dev/vitals/">https://web.dev/vitals/</a></li><li><a href="https://csswizardry.com/2020/01/performance-budgets-pragmatically/">https://csswizardry.com/2020/01/performance-budgets-pragmatically/</a></li></ul><p>This story was originally published on my personal blog. You can find my other writings there as well.</p><p><a href="https://mohitkarekar.com/posts/2021/web-performance-upgrad/">Web Performance and Related Stories - upgrad.com</a></p><p>Do you have any similar experience, or a suggestion? I’d love to discuss! You can reach out to me on <a href="mailto:karekar.mohit@gmail.com">email</a>, or <a href="https://twitter.com/MohitKarekar">Twitter</a>, or comment below. Do visit <a href="https://www.upgrad.com/"><strong>upGrad.com</strong></a> to check out our programs that are completely online! If you wish to work with our ever-enthusiastic team, check out <a href="https://www.upgrad.com/open-positions"><strong>our careers page</strong></a><strong>.</strong> We are always on the lookout for ambitious, talented folks!</p><p><a href="https://www.upgrad.com/careers/?utm_source=medium_blog&amp;utm_medium=web_perf_upgrad">upGrad Careers</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a9fb9c6bb766" width="1" height="1" alt=""><hr><p><a href="https://engineering.upgrad.com/web-performance-and-related-stories-upgrad-com-a9fb9c6bb766">Web Performance and Related Stories — upgrad.com</a> was originally published in <a href="https://engineering.upgrad.com">Technology at upGrad</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[upGrad’s Virtual Hackathon — 2020]]></title>
            <link>https://engineering.upgrad.com/upgrads-virtual-hackathon-2020-182ef579fc96?source=rss----c08812b771e6---4</link>
            <guid isPermaLink="false">https://medium.com/p/182ef579fc96</guid>
            <category><![CDATA[hackathons]]></category>
            <category><![CDATA[technology]]></category>
            <category><![CDATA[mvp]]></category>
            <category><![CDATA[ideas]]></category>
            <category><![CDATA[upgrad]]></category>
            <dc:creator><![CDATA[upGrad]]></dc:creator>
            <pubDate>Thu, 29 Apr 2021 06:07:08 GMT</pubDate>
            <atom:updated>2021-06-11T09:52:45.306Z</atom:updated>
            <content:encoded><![CDATA[<h3>upGrad’s Virtual Hackathon — 2020</h3><h4>An Event Where Innovations Came to Life!</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*gDbncWd7lPUusT8qORttvw.jpeg" /></figure><p>Over a year of being struck by the worldwide pandemic and the resulting work from home situation, we have realised the importance of constant communication and working together to solve real-world challenges. We are now accustomed to remote working and are coordinating and creating solely on virtual platforms. Hackathons are the events where the unthinkable is achieved, and innovations are born. They also encourage us to put our brains to work and develop something that can solve genuine challenges. Hackathons are also hubs for networking. This year’s hackathon was a bit different at upGrad — with everyone working remotely, it was a challenging feat by the organizing committee to bring everyone together, online, and execute each step of the event with perfection.</p><p>We conducted a 36-hour long <em>virtual hackathon</em> in December 2020. It was upGrad’s first-of-its-kind remote event that was a massive success.</p><h3>Pre-event Excitement</h3><p>It all started with the organizing committee floating out forms to accept the innovative ideas a few weeks before the hackathon.<strong> </strong>We received over 60 interesting ideas, out of which a total of 15 ideas were selected on the hackathon day. The 15 selected ideas were pitched, and the final voting was conducted, out of which the top 8 ideas were selected. The participants who had pitched the selected ideas were declared as captains of the teams.</p><h3><strong>The Hack Begins</strong></h3><p>The teams were formed, and they rigorously worked to develop the solutions pitched earlier. Team formation was one of the most fun and essential parts of the whole hackathon. An IPL-style auction was held for team formation where captains placed bids for people to include them in their team. Separate rooms were set up for each team to chat and brainstorm on their solution.</p><p>To create a synonymous environment as that of an office, the teams had 15–20 minute chai breaks between work. We would meet up in an open meeting link and chat about random things. Regular checkpoints were organized for the captains to connect and share their progress and concerns. One checkpoint was at the beginning of the day, and the next was at the end of the day. The teams were developing their products at a rapid pace, and many of them completed their MVPs. True enough, <strong>hackathons</strong> are the fastest way to bring<strong> ideas</strong> to life.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*L_FephzgvVxs9LHw7racUw.jpeg" /><figcaption>Stills from the Hackathon Zoom Call</figcaption></figure><p>The teams continuously worked hard from research to building an MVP of their solutions. The best thing was that even after the hackathon being a competition, all the teams were helping out each other and networking. A team plays an indispensable role in developing an idea to reality. It was astonishing how teams consisting of participants from different competencies came together and built solutions to solve real-world challenges.</p><p>The teams very well leveraged each other’s strengths. They identified their problem statements, developers, designers, and business minds worked out to develop the perfect solution. The outcomes were indeed impressive.</p><h3>The Final Demo</h3><p>It all boiled down to the 5-minute presentation of the final day. After <em>36 hours</em> of non-stop development and fun, at 5 PM on day 2, the captains presented their final MVPs before the judging panel in innovative ways. A quick question–answer round was held after each presentation for judges to understand more about the<strong> </strong>ideas and clarify their doubts. Out of 8 teams, five winners were chosen instead of three. There was a tie between the 2nd and 3rd positions. The judges were highly impressed. They appreciated their hard work and gave constructive feedback to the teams.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*6yWvRs9Wvf1wq486Q8tn9w.jpeg" /><figcaption>Stills from the Final Demo Call</figcaption></figure><h3>The Judges and Judgement Criteria</h3><p>The hackathon was judged by genius minds Rohit Dhar, President — Products at upGrad, and Puneet Tanwar, President — Technology at upGrad.</p><p>Rohit leads the entire Product, Data Science, Analytics, and Design teams. With his vast experience, he was able to critically analyze the innovative products developed by the teams.</p><p>Puneet, who leads the technology vertical at upGrad, has over 20 years of software development expertise. With his expertise in software and experience in leadership, he analysed the technical and practical feasibility of the MVPs and ideas.</p><p>The judgment was made on five parameters, including clarity of vision and roadmap of further development, user experience and ease, the complexity of implementation, completeness of the MVP, and the overall presentation.</p><p>The hackathon was a fun-filled, exciting event for the entire product and technology team at upGrad. The annual hackathon provided the team a platform to make their ideas a reality, <em>experiment</em>, and innovate in the field of ed-tech which is a promising domain right now. Even after the hackathon fatigue, the participants were still full of adrenaline and discussed ideas and further development.</p><p>Over the last year, upGrad has increased its team size and is looking forward to expanding even more. upGrad believes that online learning can transform one’s career. Online education is especially advantageous to those who don’t have access to in-person formal education. upGrad is bridging the gap and empowering individuals to learn affordably and from wherever they are.</p><h3><strong>Interested in working at upGrad?</strong></h3><p>By working with <strong>upGrad</strong>, you support thousands of dedicated learners who empower themselves with education and make progress in their careers. At upGrad, you change lives and contribute to the bigger mission of transforming thousands of students and working professionals’ careers. At upGrad, you learn and grow. This hackathon is only one out of the several tech and informal events we organize. Follow this blog to get future updates about technology and culture at upGrad.</p><p>If you are aspiring to be a part of our team, visit our careers page:</p><p><a href="https://www.upgrad.com/careers/?utm_source=medium_blog&amp;utm_medium=upgrad_hackathon_2020">upGrad Careers</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=182ef579fc96" width="1" height="1" alt=""><hr><p><a href="https://engineering.upgrad.com/upgrads-virtual-hackathon-2020-182ef579fc96">upGrad’s Virtual Hackathon — 2020</a> was originally published in <a href="https://engineering.upgrad.com">Technology at upGrad</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Revamping the Job Search Experience on Career Centre]]></title>
            <link>https://engineering.upgrad.com/revamping-the-job-search-experience-on-career-centre-8c4797c0df45?source=rss----c08812b771e6---4</link>
            <guid isPermaLink="false">https://medium.com/p/8c4797c0df45</guid>
            <category><![CDATA[typescript]]></category>
            <category><![CDATA[vuejs]]></category>
            <category><![CDATA[upgrad]]></category>
            <category><![CDATA[frontend]]></category>
            <category><![CDATA[quasarframework]]></category>
            <dc:creator><![CDATA[upGrad]]></dc:creator>
            <pubDate>Tue, 06 Apr 2021 10:10:17 GMT</pubDate>
            <atom:updated>2021-04-06T10:10:17.508Z</atom:updated>
            <content:encoded><![CDATA[<h4>How we rapidly built and shipped Career Centre’s brand new mobile-friendly job search experience</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0LFmbkkFzHac9K2fF8Sdtw.jpeg" /></figure><p>A frontend developer’s mildly terrifying nightmare is hearing “yes” to the daunting question of “Are we releasing this for the mobile web”? You first wonder how that’s going to impact estimates.. then the testing.. font sizes.. performance.. Your head starts to spin as you suddenly notice how your vision is blacking out..</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/968/1*by3DEtpZagsRP9RmlcUtPw.png" /></figure><p>A few developers working on the Career Centre at upGrad had a comparable incident some time back. We were tasked with bringing the slick, revamped jobs experience to the mobile web.</p><h3>mWeb — why it’s not dead.</h3><p>Across the internet, conversion for prompts to install a native app convert less than 4% of the time. Allowing a native app real estate in what is your most personal gadget, is still seen as a long term commitment — you need to first hook the user and then incentivise them to to install it. So unless your app does something that is not technically feasible on a mobile browser, addresses a high frequency use case or needs heightened security — your key focus should still be on mobile first web-apps!</p><p>mWeb also allows us to bring key features to users much quicker than native apps! It’s an easier platform to A/B test our features on and we routinely use mWeb to gather important insights into user behaviour or to gauge the success of a feature before introducing it to the native apps.</p><p>With increasingly larger strides in how mobile browsers are tapping into processing power and how new frontend technologies promise an app-like experience, modernising our jobs experience on mobile was a no-brainer.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/794/1*PDfENsA6YMpMLa51VBVIFA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/426/1*ejlnLN3sv214bvOgF5qMIg.gif" /><figcaption>The old vs the new job experience</figcaption></figure><h3>The technical challenges</h3><p>The Career Centre is built on Vue.JS, which happens to have a rich ecosystem of libraries. We went into the jobs project knowing that going forward, we should be targeting the entirety of the Career Centre to be served through a cohesive, responsive web experience.</p><h3>UI library</h3><p>We needed a scalable and flexible component library that could accommodate upGrad’s growing design system — Hygge. Our top priority when choosing the ideal library was customisability, performance and community support. After deep-diving into the availability of event hooks, template slots and general stylability, we narrowed down on Vuetify and Quasar.</p><p>These libraries had very similar underlying philosophies and component coverage. With that in mind, we tried out a real-world PoC showdown, trying to implement the screens provided by design in two very real-world tests. After pitting the two against a selected set of metrics which included ease of development, production package sizes, responsive design coverage and more, we narrowed down on Quasar!</p><p>Another “first” we tried out for this release is using theme-neutral components and iteratively evolving it to make it our own. This allows us to bring the product to the users much quicker, evolve our component libraries and setup the processes to share it between teams, while also making sure that other teams can shift to the new design system.</p><h3>Why a UI library?</h3><p>UI libraries allow rapid development of consistently styled UI components, that can be later shared across teams. Besides being responsive, these components are built to be compatible across browsers, while allowing developers to not worry about accessibility. Components also unify the way we feed and store data going to and from the components.</p><p>The immediately visible caveat is that the initial learning curve is a bit steep, but that time lost is easily made up in the additional time it takes to QA any custom UI component. Diving a bit deeper, there might be one possible argument against component libraries in the fact that a custom UI library would be leaner alternative to bloated and overly customised off-the-shelf components. While this concern is valid, in the real world, we were able to customise components globally to fit our design needs with a couple dozen or so lines of CSS.</p><p>After narrowing our list down to just 2 libraries, we chose <a href="http://quasar.dev">Quasar</a> over <a href="https://vuetifyjs.com/">Vuetify</a> and here’s how we evaluated it objectively.</p><h3>Vue 3 support</h3><p>Vue 3 brings with it some major features centered around performance and we definitely do not want to miss out. While both <a href="https://www.notion.so/Vuetify-Next-edf8fdb074eb4643a7196aaf8e5d5cad">Vuetify</a> and <a href="https://github.com/quasarframework/quasar/issues/7836">Quasar</a> have a detailed roadmap to support vue3, we felt that the jump would be marginally easier to make with Quasar, since it promises reverse compatibility. That said, we really did like how Vuetify has a highly organised <a href="https://www.notion.so/Vuetify-Next-edf8fdb074eb4643a7196aaf8e5d5cad">roadmap on Notion.</a></p><h3>Emphasis on Security</h3><p>When you first look at the sections on Quasar’s page, what really stands out is the <a href="https://quasar.dev/security/dos-and-donts">security tab!</a> It consists of a surprisingly exhaustive set of security DO’s, DONT’s and limitations for each of the most commonly vulnerable components. To top it off, a few components come with in-built sanitisation that are really handy!</p><h3>Core Philosophy</h3><p>Quasar, before it was officially released, started off as a mobile first framework that focussed primarily on building hybrid and electron apps. Then as it expanded to include desktop web-apps, it gained traction soon.</p><h3>Dev Experience</h3><h4>Documentation and ease of use</h4><p>When developing large scale web experiences, the documentation aspect of any library plays a huge role in shipping quality code on time. At first glance, Vuetify’s well presented documentation, coupled with a relatively lower barrier for entry made it a clear favourite. However, once we started delving into the more complex use cases, Quasar really started to shine.</p><h4>Styling</h4><p>The component styling are equally easy in both the libraries. Quasar has a custom class with a —inner prefix that can be used as a hook to create custom styles. It also has modifiers to changer styles based on element states. (This is especially useful in cases like the one illustrated below where the input element is much lower down the DOM tree than the element controlling the style)</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcodepen.io%2Fadarshsosale%2Fembed%2Fpreview%2FdypNgge%3Fheight%3D600%26slug-hash%3DdypNgge%26default-tabs%3Dcss%2Cresult%26host%3Dhttps%3A%2F%2Fcodepen.io&amp;display_name=CodePen&amp;url=https%3A%2F%2Fcodepen.io%2Fadarshsosale%2Fpen%2FdypNgge&amp;image=https%3A%2F%2Fassets.codepen.io%2F672105%2Finternal%2Fscreenshots%2Fpens%2FdypNgge.default.png%3Ffit%3Dcover%26format%3Dauto%26ha%3Dfalse%26height%3D540%26quality%3D75%26v%3D2%26version%3D1607932742%26width%3D960&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=codepen" width="800" height="600" frameborder="0" scrolling="no"><a href="https://medium.com/media/f1fd2fe1343e68b6ef239210fa8f8972/href">https://medium.com/media/f1fd2fe1343e68b6ef239210fa8f8972/href</a></iframe><h4>Flexibility</h4><p>The core component coverage of both the libraries is quite identical. However, Quasar has a slight edge in the extended components (stepper, timeline, etc). So then we started to analyze the number of customizable props and slots, etc for all of the core components, we found Quasar to be significantly more customizable than Vuetify.</p><p>The flexibility of the component library was very important to us since it’d enable us to write code and test faster, and bring the feature to production sooner. Moreover, if all the Vue repos at upGrad to shift to a single library, it would be easier to share off-the-shelf components bundled with the settings, rather than a custom written component with deeply-linked dependencies.</p><p>Let’s have a look how we assessed flexibility -</p><p>When we first had a look at the search bar’s functionality, it seemed like we’d have to build a component from scratch to account for the unique use cases.</p><p>The search bar has 3 states — <strong>recent searches</strong>, <strong>search suggestions</strong> and <strong>custom search</strong>. While the recent searches is straightforward to implement, the latter 2 cases operate on all the tags present in the search bar as well as the untagged input.</p><p>The closest components that resemble the functionality of the searchbar in Vuetify and Quasar are the <a href="https://vuetifyjs.com/en/components/combobox/#usage">v-combobox</a> and <a href="https://quasar.dev/vue-components/select">q-select</a>, respectively. Looking at the APIs for v-combobox, there’s no built-in way to access the the untagged input value.</p><p>The example below gives an idea of how easy it would be to customise the behaviour of a component to suite a complex use case.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcodepen.io%2Fadarshsosale%2Fembed%2Fpreview%2FwvzzzgQ%3Fheight%3D600%26slug-hash%3DwvzzzgQ%26default-tabs%3Djs%2Cresult%26host%3Dhttps%3A%2F%2Fcodepen.io&amp;display_name=CodePen&amp;url=https%3A%2F%2Fcodepen.io%2Fadarshsosale%2Fpen%2FwvzzzgQ&amp;image=https%3A%2F%2Fassets.codepen.io%2F672105%2Finternal%2Fscreenshots%2Fpens%2FwvzzzgQ.default.png%3Ffit%3Dcover%26format%3Dauto%26ha%3Dfalse%26height%3D540%26quality%3D75%26v%3D2%26version%3D1607945466%26width%3D960&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=codepen" width="800" height="600" frameborder="0" scrolling="no"><a href="https://medium.com/media/aa5d393c565297552da3bb47a63b3587/href">https://medium.com/media/aa5d393c565297552da3bb47a63b3587/href</a></iframe><h3>Typescript</h3><p>Another aspect of the release was a shift towards TypeScript. The immediate advantages are IDE autocomplete and static type checking. The well defined types makes it easier for newer developers to understand code (this is especially useful when sharing components between teams). The biggest tradeoff for us was an increased learning curve. In the longer term, we were expecting slight dips in productivity.</p><h3>The Release</h3><p>We split the releases into two, one for desktop and a subsequent one for mobile-web. We started by laying out the foundations for responsiveness in the desktop release. However, we used a conditional flag to hide the mobile version in production. The second release that brought mobile responsiveness was written in a mindful way that made sure that the previously tested code was mostly untouched</p><p>Once the desktop release went live, we quickly got back to work (after a quick break to celebrate, of course 🍻). We re-looked at key factors like interactions, touch targets and spacing using a mobile-first lens and after using BrowserStack to do extensive dev-testing, handed it over to QA.</p><h3>Future scope</h3><p>We have a series of features that would improve the general ease of use for our learners who are accessing the Career Centre on mobile. We’re focusing on improving the page speed performance and bringing more of the desktop functionality to mobile. Going full-PWA is next up on our list of TODO’s. PWAs will open the doors for background processing and push notifications, and that will take us a step forward in bringing a world-class learning experience to everyone.</p><p>Do visit <a href="https://www.upgrad.com/"><strong>upGrad.com</strong></a> to check out our programs that are completely online! If you wish to work with our ever-enthusiastic team, check out <a href="https://www.upgrad.com/open-positions"><strong>our careers page</strong></a><strong>.</strong> We are always on the lookout for ambitious, talented folks!</p><p><a href="https://www.upgrad.com/careers/">upGrad Careers</a></p><p>Contributors: <a href="https://medium.com/u/3201f86d0b1e">Adarsh Sosale</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8c4797c0df45" width="1" height="1" alt=""><hr><p><a href="https://engineering.upgrad.com/revamping-the-job-search-experience-on-career-centre-8c4797c0df45">Revamping the Job Search Experience on Career Centre</a> was originally published in <a href="https://engineering.upgrad.com">Technology at upGrad</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Taking upGrad.com International]]></title>
            <link>https://engineering.upgrad.com/taking-upgrad-com-international-bf0e6e328295?source=rss----c08812b771e6---4</link>
            <guid isPermaLink="false">https://medium.com/p/bf0e6e328295</guid>
            <category><![CDATA[lambda-edge]]></category>
            <category><![CDATA[cloudfront]]></category>
            <category><![CDATA[i18n]]></category>
            <category><![CDATA[nuxtjs]]></category>
            <category><![CDATA[upgrad]]></category>
            <dc:creator><![CDATA[Mohit Karekar]]></dc:creator>
            <pubDate>Tue, 02 Mar 2021 18:49:45 GMT</pubDate>
            <atom:updated>2021-03-03T04:50:55.947Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*LmhcGaVDLuq6iM5LxUvHCg.jpeg" /></figure><p>A few months back, upGrad decided to expand into international markets and thus came the need to support this expansion within the various client-facing technology products. The first one that required a substantial change was the course listing website — <a href="https://upgrad.com">upgrad.com</a>. In this post I talk about how we approached and implemented this, and the things we learnt on the way.</p><p>For the first phase of internationalization, the expectation from the platform was that the internal teams should be able to market upGrad’s courses independently in different countries, i.e. the content displayed in each country could be different and it should be possible to maintain it separately. In addition to this, visitors should be auto-redirected to their country-specific URLs when they try opening any page on the <a href="http://upgrad.com">upgrad.com</a> website. This is a general behavior across many websites where content is distinguished using certain codes that are appended to the base URL.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*pUtkIR1YBvcvZD4g010mpg.jpeg" /></figure><p>An additional requirement that might not seem obvious is maintaining this appended code throughout the visitor session. If someone lands on upgrad.com/us, then all the subsequent routing should be handled keeping the initial location code in context. E.g. if the viewer clicks on a hyperlink or a button that causes routing to /data-science-pgd-iiitb, then the resultant URL should be upgrad.com/us/data-science-pgd-iiitb.</p><p>So in all, we had three problems to solve:</p><ol><li>Enable saving location-specific pages in DB and allow respective editing</li><li>Handle client-side routing</li><li>Auto-redirect to these specific pages according to user location</li></ol><h3>Updating CMS to handle international pages</h3><p>We have an in-house content management system (CMS), named <strong>Apollo</strong>, to manage content on upgrad.com. <em>‘Pages’</em> can be described as documents in a database mapped to a unique <strong><em>slug</em></strong>. Previously, it was only possible to have single-level slugs which directly mapped to the URLs on upgrad.com. E.g. data-science-pgd-iiitb mapped to upgrad.com/data-science-pgd-iiitb. To allow pages to have a two-level URL like upgrad.com/us/data-science-pgd-iiitb, we introduced a new property in the page document called i18n. This is an object which holds the value for locale which is a <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements">unique two-digit code</a> assigned to each country. Locales are good textual identifiers for countries and can be directly mapped to URLs.</p><p>While creating pages, editors could specify which country page they wished to create and the value would get attached to the page document once created. We broke the previously used slug identifier for the page into two parts viz. name and i18n.locale.</p><pre>// Old<br>{<br>  &quot;slug&quot;: &quot;data-science-pgd-iiitb&quot;,<br>  ...<br>}</pre><pre>// New<br>{<br>  &quot;slug&quot;: &quot;us/data-science-pgd-iiitb&quot;,<br>  &quot;name&quot;: &quot;data-science-pgd-iiitb&quot;,<br>  &quot;i18n&quot;: {<br>    &quot;locale&quot;: &quot;us&quot;<br>  }<br>}</pre><p>The name property would contain the more logical value of the <em>program</em> for which the page belonged to. This could be used to later group all pages falling under the same name. As mentioned in the above snippet, the slug property now included the complete URL that the document would signify, i.e. if requested for the United States page for /data-science-pgd-iiitb, I can expect that I’d get data from the above document. Yet, the page data loading logic isn’t so straight forward, I’ll talk about it ahead.</p><p>You might question here why we are maintaining duplicate information in name, slug, and i18n.locale. The answer would be simple — to simplify various fetching scenarios. During this project, we wanted to keep breaking changes as minimum as possible and provide fallback mechanisms wherever possible. The CMS, which we call <strong>Apollo</strong> internally, was previously only being used by the web clients. But after the introduction of upGrad’s mobile applications, it was extended to Android and iOS mobile clients. Hence, keeping CMS API endpoints and response schema unchanged was of utmost importance.</p><h3>Fallback Mechanisms</h3><p>To be able to reach as many locations as possible and simultaneously to keep a low data footprint, international marketing pages followed a region-wise fallback mechanism. If thought in a straight-forward manner, creating a page for each country per program would mean an <strong>m * n</strong> rise in our data. This would also mean that content maintainers would have to update several pages in order to make changes across all country pages of a specific program. This wasn’t a happy scenario.</p><p>To solve this problem the idea was to maintain a minimum number of international pages and serve relevant content from these handful pages to all locations worldwide. This meant that we would have to maintain only a few pages and could update which page had to be served for a particular location on the fly.</p><p>Generalizing this, we introduced a configuration object in our APIs. This was a JSON structure which held region wise grouping of locations. The logic was simple, the values at the leaf nodes of this tree denoted individual countries/locations which could potentially have a page associated with them. If they did not, the page fetching API would go up one level and try to fetch a page linked with that locale <em>recursively</em>.</p><p>For e.g. if the client requested a page for locale=ae, we would check if ae page existed, if not then check if asia page existed, again if not then return the global page for the requested entity.</p><pre>// Configuration<br>{<br>  &quot;global&quot;: {<br>    &quot;asia&quot;: {<br>      &quot;sg&quot;: {},<br>      &quot;ae&quot;: {}<br>    },<br>    &quot;europe&quot;: {<br>      &quot;gb&quot;: {}<br>    }<br>  }<br>}</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Qq-RyIrjBX05W3ADbAM2tw.jpeg" /><figcaption>Fallback regions. Indian pages still run independently, global pages have fallback defined.</figcaption></figure><p>Introducing this allowed us to associate countries to specific regions so that we could serve the region data in every country which fell under it. To avoid making this configuration huge, we ensured on our level that a global page variant existed for every page. This ensured that if a page was requested for a country that did not exist in our configuration, it would always fall back to the <strong><em>global</em></strong> page.</p><p>The page endpoint now accepted a locale parameter through which the clients could specify which country page was required. This primarily covered most of the compatibility issues, mostly with gradually updating mobile clients, as the endpoints remained the same, and even if no locale query parameter was sent, the API assumed that it was in (India) by default, which was the case before internationalization.</p><h3>Handling client-side routing</h3><p>We use <a href="https://nuxtjs.org/"><strong>Nuxt</strong></a> in the universal mode to serve our marketing pages. Fortunately, Nuxt (and the Vue community in general) has a set of official modules for popular use cases, including internationalization. Thanks to the <a href="https://i18n.nuxtjs.org/"><strong>nuxt/i18n</strong></a><strong> </strong>module, a lot of our work was simplified. This provided auto-generation of static routes on startup, client-side routing and also provided various ways to configure them.</p><p><a href="https://github.com/nuxt-community/i18n-module">nuxt-community/i18n-module</a></p><p>At a lot of places, we were redirecting users to URLs in or outside the app. With <em>i18n</em>, this had to be pushed through a transformer function that attached the current locale value to the new URL. nuxt/i18n exposes a function called localePath for this. We found this to be a good opportunity to clean up our existing redirection logic and bring it to a single place by writing a wrapper function to redirect to URLs that internally optionally used localePath if the URL was an internal one.</p><p>When the app boots up, nuxt/i18n saves the data related to the current locale from the URL. E.g. upgrad.com/us/data-science-pgd-iiitb sets a value of locale us to the store, with other related information. Using this, the Nuxt app fires a call to the CMS API with the locale query parameter equal to the one set by the module. Post this, the API takes care of returning the exact matching data or applies some fallback mechanism to retrieve the nearest matching one. Once the data is returned, the Nuxt app renders according to the layouts specified in the data.</p><h3>Auto-redirection to country-specific URLs</h3><p>This was the most aesthetic feature of the entire project. No matter how much DB and API changes were made behind the scenes, one would look at the URL <em>magically</em> changing to their location code and be amazed. But we as developers knew how much effort went into making this possible along with ensuring that the rest of the platform worked fine as before.</p><p>Our existing marketing website is served via CloudFront to achieve low latency by enabling edge-caching. We decided to leverage this setup to add an extra step of routing to the location URLs. Just to restate the problem at hand:</p><blockquote>When a viewer opens <a href="http://upgrad.com/data-science-pgd-iiitb">upgrad.com/data-science-pgd-iiitb</a> from, say, the United States, the request should be redirected to <a href="http://upgrad.com/us/data-science-pgd-iiitb">upgrad.com/us/data-science-pgd-iiitb</a>.</blockquote><p>This was a perfect use case for on-demand executing functions — AWS Lambda. AWS Lambda has a CloudFront variant called <a href="https://aws.amazon.com/lambda/edge/"><strong>Lambda@Edge</strong></a>, which creates copies of a function and deploys them across the CloudFront distribution network. This can then be configured to execute on certain triggers.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/720/1*XiL0wRVVooYsi2wQYk_mfQ.jpeg" /><figcaption>Various triggers available for Lambda@Edge. <a href="https://aws.amazon.com/blogs/networking-and-content-delivery/handling-redirectsedge-part1/">Source</a>.</figcaption></figure><p>As mentioned in the diagram above, we placed the Lambda function on the <em>origin request</em> trigger. This event fires when CloudFront requests content from the origin server — the function sits between CloudFront and the origin. It is Lambda’s duty to create a redirection or pass on the URL to the origin server. To detect user location, CloudFront’s country headers came in handy. In addition to this, we also used our internal location resolving API powered by <a href="https://www.maxmind.com/en/geoip2-services-and-databases">MaxMind</a>’s IP database.</p><p>To access CloudFront’s country headers, they are required to be added to the whitelisted headers in the behavior configuration. You can find it by the name CloudFront-Viewer-Country .</p><p>The Lambda function, implemented in <strong>Node.js</strong>, roughly looks as follows:</p><pre>exports.handler = async (event, context, callback) =&gt; {<br>  let request = event.Records[0].cf.request;<br>  const uri = request.uri;<br>  const querystring = request.querystring;<br>  const countryCode = request.headers[&#39;cloudfront-viewer-country&#39;][0].value;</pre><pre>  // Skip some URLs which you might not want to redirect<br>  if(shouldSkipRedirection(uri)) return callback(null, request);<br>  <br>  // Resolve location from headers or IP<br>  let { locale } = await resolveLocation(countryCode, request.clientIp);<br>  <br>  // Create a 302 redirect<br>  let response = {<br>    status: 302,<br>    statusDescription: &#39;Found&#39;,<br>    headers: {<br>      location: [{<br>        key: &#39;Location&#39;,<br>        value: &#39;/&#39; + locale + uri + (querystring ? `?${querystring}` : &#39;&#39;)<br>      }]<br>    }<br>  }<br>  return callback(null, response);<br>}</pre><p>Once this Lambda was published over to the edge locations, origin requests started to flow through the lambda function and users viewing the website from locations worldwide saw different content. This was the last step (first in the user journey) in the entire flow and if we retract one point at a time, all things fall into place.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AEj4vD5RBu9sj7T6VtHBUg.jpeg" /><figcaption>The flow of a request feat. Lambda@Edge</figcaption></figure><p>When a user opens the <a href="http://upgrad.com">upgrad.com</a> website from the US, the lambda detects the location and assigns locale value of us, and redirects to /us before hitting the origin server — the Nuxt app. The nuxt/i18n module takes over and sets the locale value as us in the Nuxt <em>context</em> which leads to an API being fired with the respective query parameter attached. The API server tries looking for the US page in the database and returns the nearest matching document to the client. On receiving this data, the client successfully renders the page and returns it back to CloudFront. It then returns this result back to the viewer and also caches it against the original request URL as well as the redirected URL. And that’s all folks!</p><h3>Future Scope</h3><p>This was the first phase of internationalization across the marketing website and has enabled marketing our courses in multiple countries now, and we are able to drive content independently everywhere. The next steps would include adding local language support and allowing manually changing regions. In addition to this, we think there could be improvements in the Lambda approach. Currently, the logic of skipping certain URLs that do not fall under this criteria is a bit scattered. We are thinking of ways to make it more centralized so that it can be configured via a single source of truth. Let us know in the comments if you have implemented something similar to this in your applications, would love to have different perspectives.</p><p>This project wouldn’t have been possible without the entire team that made it a success. Kudos to Maitrey, Jitesh, Shahir, Abhishek, Harshita, Omkar, Akshay, Vishal, Yuvaraj &amp; team, and all others who contributed. Do visit <a href="https://www.upgrad.com/"><strong>upGrad.com</strong></a> to check out our programs that are completely online! If you wish to work with our ever-enthusiastic team, check out <a href="https://www.upgrad.com/open-positions"><strong>our careers page</strong></a><strong>.</strong> We are always on the lookout for ambitious, talented folks!</p><p><a href="https://upgrad.com/open-positions">upGrad Careers</a></p><h3>References &amp; Further Reading</h3><ul><li><a href="https://aws.amazon.com/blogs/networking-and-content-delivery/handling-redirectsedge-part1/">Handling Redirects@Edge Part 1 &amp; 2</a></li><li><a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-examples.html#lambda-examples-redirecting-examples">Personalize Content by Country or Device Type Headers — Examples</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bf0e6e328295" width="1" height="1" alt=""><hr><p><a href="https://engineering.upgrad.com/taking-upgrad-com-international-bf0e6e328295">Taking upGrad.com International</a> was originally published in <a href="https://engineering.upgrad.com">Technology at upGrad</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Introducing Versioning in a Content Management System]]></title>
            <link>https://engineering.upgrad.com/introducing-versioning-in-a-content-management-system-1b167c949cf3?source=rss----c08812b771e6---4</link>
            <guid isPermaLink="false">https://medium.com/p/1b167c949cf3</guid>
            <category><![CDATA[content-management-system]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[versioning]]></category>
            <category><![CDATA[nodejs]]></category>
            <category><![CDATA[cms]]></category>
            <dc:creator><![CDATA[upGrad]]></dc:creator>
            <pubDate>Thu, 11 Feb 2021 08:37:21 GMT</pubDate>
            <atom:updated>2021-02-11T08:34:39.611Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wvv5E_ivVI4X-4_Ko5CtLw.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/@annietheby?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Annie Theby</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><p>With tons of minor to major edits daily, keeping track of all content changes becomes challenging for content and website administrators, and that’s where content versioning comes into the picture. It is the concept of tracking different versions of content updates to answer simple day-to-day operations, such as:</p><ul><li>What edits were made?</li><li>When was content changed?</li><li>Who made the changes?</li><li>What is the difference between previous and updated content?</li></ul><p>Content versioning answers all these questions to make everyday operations efficient and seamless. The best example to understand content versioning is Google Docs. You can view version history, real-time changes and roll back to any version of the document without any hassle.</p><p>Since we are a swiftly scaling online higher education providing platform, our website <a href="https://www.upgrad.com/">upGrad.com</a> requires constant changes. Almost until a couple of years ago, the content edits on the website were still being done by the technology team. This included information about our programs, and how they add value to an individual’s career. Even though most of this data was in the form of JSON, it required constant code changes to update the website. To solve this, we built <strong>Apollo</strong>, our in-house content management system (CMS). Apollo allowed us to add, alter, and delete content on the fly. Over these two years, content editing has streamlined and individual program owners now own their respective content. But as it is true in software engineering —<strong><em> you never solve a problem completely, you only get mature enough to solve greater ones</em></strong>. Along with immense flexibility, Apollo also brought in some complications, which eventually raised the need for introducing versioning.</p><h3>The Why</h3><p>As mentioned, Apollo CMS allowed our internal programmers and administrators to edit website content hassle-free. But over time, managing a track of every edit became hectic. When we realized that editing content with Apollo was creating problems, we started looking for a solution, and we found that versioning can be a potential fit. But what were the issues that kept us looking for a solution like versioning?</p><p>What we used to do is have separate collections for components, layouts, and pages on Apollo. The CMS is backed by a Nodejs server using a MongoDB database. The initial design was more like a relational DB. To represent a page’s data, we included references for components into layouts and then layouts’ references into the page. This led to a mesh topology where every document was connected to several components, layouts, and pages from.</p><p>As the size of the data and frequency of edits increased, keeping track of every edit with such infrastructure was challenging. Also, it was troubling to delete or rollback to any previous point in the website. For instance, if we wanted to delete any particular document, we had to go through the entire mesh topology and look for references of all the components, layouts, and pages associated with that collection. Hence, it was hectic for everyone, right from content managers to us, developers, to delete or revert to any previously updated webpage.</p><p>Each query’s response time was also increasing as the program had to go through the entire database. Slow response time affected the performance of the whole website, which was not acceptable by us. All these problems led to an urgency for introducing versioning in Apollo.</p><h3>The How</h3><p>We introduced versioning in Apollo by eliminating the step of instance references; instead, we started storing the actual instances in each collection. This reduced mesh topology’s complexity, and now everything, with actual instances, was held in a single collection. Hence, everything: tracking, altering, reverting, and deleting content became more straightforward.</p><p>Our CMS was now storing data about who was changing what content at what particular time. Even a minor edit was creating a new version of the entire collection. The administrator and internal programmers now had the option to compare the changes side by side to see how they will affect the website’s whole layout.</p><p>We created two stacks of pages, one for storing only the latest page version and the other for keeping the latest and older versions. Whenever a query was called to retrieve the latest version or when someone visited the website, the program would take the code from the stack with the newest version and give the output to reduce response time and enhance performance. On the other hand, if the administrator wanted to compare two versions, he can merely head to the versioning interface and select any two versions to see the differences between them.</p><h3>The Seamless Transition to Content Versioning</h3><p>Let’s look at an elementary example to understand what’s the difference between the older Apollo and the newer one with versioning. The below table showcases how the versions are made, and the collection is changed with each edit.</p><pre>+----------------+------------------+----------+-------------+<br>| Global Version | Internal Version                          |<br>+----------------+------------------+----------+-------------+<br>| 1              | Page             | Layout   | Component   |<br>+----------------+------------------+----------+-------------+<br>| 2              | Page             | Layout   | Component 1 |<br>+----------------+------------------+----------+-------------+<br>| 3              | Page             | Layout   | Component 2 |<br>+----------------+------------------+----------+-------------+<br>| 4              | Page             | Layout 1 | Component 2 |<br>+----------------+------------------+----------+-------------+<br>| 5              | Page             | Layout 1 | Component 3 |<br>+----------------+------------------+----------+-------------+<br>| 6              | Page             | Layout 2 | Component 3 |<br>+----------------+------------------+----------+-------------+<br>| 7              | Page             | Layout 2 | Component 4 |<br>+----------------+------------------+----------+-------------+<br>| 8              | Page             | Layout 2 | Component 5 |<br>+----------------+------------------+----------+-------------+</pre><p>Without versioning, the administrator had to pass queries to search through the entire website code to add, alter, rollback, or delete any content. With versioning in place, the administrator only has to select a particular global version number to find out all the details about that version. It became possible to track changes, rollback, or delete content with just a few clicks without worrying about the associated instance references.</p><h3>How Versioning of CMS Helped Us?</h3><p>This initiative of introducing content versioning in CMS provided us with numerous benefits, the most significant ones are:</p><p><strong>Easy traceability</strong>: With versioning, we could track even minute changes and details such as who made the changes, what changes were made, and when they were made. It also allows us to compare the data on two versions by highlighting the changes made.</p><p><strong>Enhanced performance</strong>: The speed and overall performance of the website were improved. For instance, to navigate a page with 3 layouts and 15 components, the program had to run 1 x 3 x 15 = 45 queries to provide the results as each collection of stored instance references. But now, with versioning, since everything is in a single collection, the program only has to run a single query for getting the output. This led to enhanced performance.</p><p><strong>Efficient content management</strong>: Versioning allowed our internal programmers, content managers, and administrators to manage the entire website efficiently. Everything right from adding, updating, altering, reverting, and deleting content on the website became hassle-free and easy.</p><p>Introducing versioning in Apollo has helped us overcome the hurdles in efficiently managing our website. This has not only increased traceability but also has made reverting to older versions easy and has improved content stability. As engineers, this was an interesting problem to solve, deriving inspiration from versioning systems like git . If you are interested in solving similar and other interesting problems, do check out <a href="https://www.upgrad.com/open-positions"><strong>our careers page</strong></a><strong>.</strong> We are always on the lookout for ambitious, talented folks!</p><p>Do visit <a href="https://www.upgrad.com/"><strong>upGrad.com</strong></a> to check out our programs that are completely online!</p><p>Contributors: <a href="https://medium.com/u/9d00d158eebe">Jawad Sonalkar</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1b167c949cf3" width="1" height="1" alt=""><hr><p><a href="https://engineering.upgrad.com/introducing-versioning-in-a-content-management-system-1b167c949cf3">Introducing Versioning in a Content Management System</a> was originally published in <a href="https://engineering.upgrad.com">Technology at upGrad</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How we improved user experience by building a lite version of the upGrad learner platform]]></title>
            <link>https://engineering.upgrad.com/how-we-improved-user-experience-by-building-a-lite-version-of-learner-experience-ui-2df5ee521a5f?source=rss----c08812b771e6---4</link>
            <guid isPermaLink="false">https://medium.com/p/2df5ee521a5f</guid>
            <category><![CDATA[react]]></category>
            <category><![CDATA[lighthouse]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[performance]]></category>
            <category><![CDATA[web]]></category>
            <dc:creator><![CDATA[Arvind Narayan]]></dc:creator>
            <pubDate>Thu, 07 Jan 2021 05:43:53 GMT</pubDate>
            <atom:updated>2021-05-03T08:14:14.112Z</atom:updated>
            <content:encoded><![CDATA[<h3>How we improved user experience with a lite version of the upGrad’s learning platform</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5Y-dPR3IkBOOjVpb7Nkngw.png" /></figure><h3>Preface</h3><p><strong>Problem Statement: <br></strong>Many of upGrad’s users in India do not have access to powerful devices or high bandwidth internet, which affects their browsing experience. upGrad learners with poor or unstable bandwidth connections or with low-end devices had reported issues with screen loading times and general responsiveness of the User Interface (UI).</p><p>This was an important problem for us to resolve as providing a smooth and seamless learning experience to all our users is a key goal for upGrad.</p><p>The upGrad <strong>learner platform </strong>UI was written in <a href="https://backbonejs.org/">Backbone.js</a> a few years ago, was heavy in size, lacked re-render performance, and was difficult to maintain and enhance. In order to solve the problem, we decided to revamp our architecture with a modern rewrite and build a lighter version of the platform — <a href="https://lite.upgrad.com/">upGrad Learn Lite</a> — which is much more forgiving of poor bandwidth and devices.</p><h3>The outcome</h3><ol><li><strong>Lighthouse Scores:</strong></li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FvddA0zP7zitgHqDfZw4eg.png" /><figcaption>Lighthouse comparison of old and new platform</figcaption></figure><p>As a result of the exercise, we increased the <strong>Lighthouse</strong> score from a marginal <strong>1</strong> to a staggering <strong>92</strong> on the home page of the application. In addition to improving the load times, we also converted the app to an easily extensible Progressive Web App (PWA).</p><p>2.<strong> Load time improvements</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cuuxc5OlX0OOnG-rsY-z9A.png" /><figcaption>Load time comparisons</figcaption></figure><p>As you can see from the above image the <strong>legacy</strong> <strong>learner platform </strong>was heavy with 13.4 MB as the size of requests and takes 6.6s to load. The <strong>Learn Lite platform </strong>is 3.4 MB in size and takes 3s to load entirely. The best part is once the New App is loaded all the bundles and assets are cached the request size is just 1.7MB!</p><p>Where this improvement shines is in low bandwidth situations. The application provides faster initial loads and mimics the experience of an app where most static assets such as bundled CSS and JS files along with fonts, images, and icons are cashed and only API calls are required to fetch data.</p><p>3. <strong>Interactive Performance</strong>:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VgCRT2o0klLnPbNWldvztw.png" /><figcaption>Interactive performance comparisons</figcaption></figure><p>As you can see there is a significant drop in <strong>scripting</strong> and <strong>loading</strong> times (by a factor of 5). The user experience of the new app is noticeably better; every interaction is slick and smooth and re-rendering is low due to usage of <a href="https://reactjs.org/">Reactjs</a> and thanks to <a href="https://reactjs.org/docs/reconciliation.html">React’s Virtual DOM</a> Reconciliation.</p><p>The rest of this blog describes technical details of the revamp (project codename Ares) of the legacy platform.</p><h3>How we did it</h3><p>First and foremost, the technology behind the entire Revamp (project code name — Ares)</p><p><strong>Frontend:</strong></p><ul><li><a href="https://github.com/facebook/react">React</a></li><li><a href="https://github.com/ReactTraining/react-router">React router</a> in conjunction with <a href="https://github.com/supasate/connected-react-router">Connected React Router</a></li><li><a href="https://react-redux.js.org/">React Redux</a> along with <a href="https://redux-toolkit.js.org/">Redux Toolkit</a></li><li><a href="https://reactjs.org/docs/create-a-new-react-app.html">CRA (Create React App)</a></li><li><a href="https://github.com/axios/axios">Axios</a> for HTTP AJAX</li><li><a href="https://github.com/GoogleChrome/sw-toolbox">sw-toolbox</a> (comes with CRA)</li></ul><p><strong>Tooling</strong></p><ul><li><a href="https://github.com/postcss/postcss">Postcss</a> (+ autoprefixer, cssnext).</li><li><a href="http://babeljs.io/">Babel</a></li><li><a href="https://webpack.github.io/">Webpack</a></li><li><a href="http://www.html5rocks.com/en/tutorials/service-worker/introduction/">Service Workers</a></li></ul><p><strong>Infrastructure</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*s2jnULRf2RgDWDtTsV5ZJA.png" /><figcaption>Infrastructure based on Client View</figcaption></figure><p>Our architecture is plain simple and gets the job done. The application gets compiled and uploaded to <a href="https://aws.amazon.com/s3/">AWS S3</a> after deleting the old build via our CI/CD Pipeline (<a href="https://www.jenkins.io/">Jenkins</a>) and is delivered across geographies with the help of <a href="https://aws.amazon.com/cloudfront">AWS Cloudfront</a> CDN. For every release old cache gets purged.</p><p><em>The index.html file is never cached. This is intentional as any new updates are routed via new index.html</em></p><p>We use <a href="https://imagekit.io/">Imagekit</a> (Image CDN with automatic optimization) for delivery of Images</p><blockquote>Having no intermittent server reduced the Infrastructure Complexity and Increased the scalability whilst also drastically improving the Web App Delivery. 🚚</blockquote><h3><strong>The Code</strong></h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*2afuvL4rV_VIdrta.png" /></figure><p>Now, let us deep-dive into React and general frontend principles talking about the changes we have done. Usage of React and its ecosystem by itself was a huge factor in boosting the app&#39;s performance.</p><h4><strong>Lazy Loading</strong></h4><p>Lazy Loading is a feature that enables you to load certain other bundles after some user interaction like changing the route. This means that you can chop your huge application into segments with some performance budgeting to meet your needs.<br>This will drastically reduce the initial load size of the App and load the application as per the User&#39;s usage of modules.</p><pre>import React, { Suspense } from &#39;react&#39;;</pre><pre>Import FirstComponent from &#39;./FirstComponent&#39;;<br>const OtherComponent = React.lazy(() =&gt; import(&#39;./OtherComponent&#39;));<br><br>function MyComponent() {<br>  return (<br>    &lt;div&gt;<br>      &lt;Suspense fallback={&lt;div&gt;Loading...&lt;/div&gt;}&gt;<br>        &lt;Switch&gt;<br>         &lt;Route to=&quot;/&quot;&gt; &lt;FirstComponent /&gt; &lt;/Route&gt;<br>         &lt;Route to=&quot;/lazy-route&quot;&gt; &lt;OtherComponent /&gt; &lt;/Route&gt;<br>        &lt;/Switch&gt;<br>      &lt;/Suspense&gt;<br>    &lt;/div&gt;<br>  );<br>}</pre><p>With help of React.lazy you can lazy load certain components We’ve had to lazy load the main module routes to fit into our performance budgets (less than 500kb). <strong>Suspense</strong> is a React component that would render some placeholder until the lazy-loaded component is fetched. You can have a placeholder component(loader, image and etc.,) in the fallback prop until the new bundle is loaded.</p><pre>import React from &#39;react&#39;;<br>import { somefunction } from &#39;someLib&#39;;</pre><pre>function MyComponent() {<br>  <br>  const lazyFun = () =&gt; {<br>    import(&#39;lazy-lib&#39;).then((lazyLibraryFunction) =&gt; {<br>     lazyLibraryFunction();<br>   });<br>  };<br>  <br>  <br>  return (<br>    &lt;div&gt;<br>     &lt;button onClick={() =&gt; lazyFun()}&gt; Click to lazy execute   <br>     &lt;/button&gt;<br>    &lt;/div&gt;<br>  );<br>}</pre><p>Not only did we lazy load components and routes but we have gone a step further to lazy load certain libraries that are only used conditionally.</p><pre>export const changeLanguage = (locale) =&gt; (dispatch) =&gt; {</pre><pre> import(`../locale/${locale}.json`).then(({ default: language }) =&gt;{ <br>    dispatch({<br>             type: &#39;SET_LANGUAGE&#39;,<br>             language,<br>             locale,<br>             });<br>  }).catch(() =&gt; {});</pre><pre>};<br></pre><p>We are also lazy-loaded certain static JSON optionally. Like the example above where we lazy load the language JSON as per locale selected. This way the language files that were loaded were of the user’s choosing.</p><h4><strong>Usage of React Hooks:</strong></h4><p><strong><em>React</em> <em>Hooks</em> </strong>are a new addition in React 16.8 and they are certainly for the best. You can write pure <strong>Functional components</strong> and still manage to use <strong>State </strong>and<strong> Lifecycle </strong>via hooks courtesy of <strong>useState</strong> and <strong>useEffect</strong>. You might be pondering upon how the usage of hooks in functional components adds benefit with respect to performance.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/0*0EFTNseQ_f0zpWK4.gif" /><figcaption>Class-based component to JS (ES 2015)</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/0*_GfZ4gZ3STL8go_5.gif" /><figcaption>Functional component to JS (ES 2015)</figcaption></figure><p>As you can see from the above Gifs functional components have nearly half the footprint of Class-based components simply due to the complexity of emulating classes and it’s a feature in JS as functions. This end of the day results in much smaller bundles.</p><p>This also means using Hooks from other libraries like <a href="https://reactrouter.com/web/api/Hooks">React Router </a>and <a href="https://react-redux.js.org/api/hooks">React Redux</a> wherever support is provided. At this point, everybody has moved to hooks or at least support it.</p><h4><strong>React memo:</strong></h4><p>When deciding to update DOM, React first renders your component, then compares the result with the previous render result. If the render results are different, React updates the DOM.</p><p>Current vs previous render results comparison is fast. But you can <em>speed up</em> the process under some circumstances.</p><p>When a component is wrapped in React.memo(), React renders the component and memoizes the result. Before the next render, if the new props are the same, React reuses the memoized result <em>skipping the next render</em>.</p><pre>export function MemoComp({ prop1, prop2 }) {<br>  return (<br>    &lt;div&gt;<br>      &lt;div&gt;Property 1 : {prop1}&lt;/div&gt;<br>      &lt;div&gt;Property 2: {prop2}&lt;/div&gt;<br>    &lt;/div&gt;<br>  );<br>}<br><br>export const MemoizedComp = React.memo(MemoComp);</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GjFaeLCbv-QIQq31SbarPQ.png" /><figcaption>Courtesy of <a href="https://dmitripavlutin.com/"><strong>Dmitri Pavlutin</strong></a></figcaption></figure><h4><strong>Avoiding Reconciliation:</strong></h4><p>This basically means to avoid unwanted re-renders. Here are a few things you might have to give a thought about.</p><ol><li>Usage of React.memo as shown above, dramatically reduces re-renders.</li><li>Subscribe to only used sections of the global store. Subscribing to a larger section opens up possibilities of unwanted re-renders caused by other state changes.</li><li>Dispatch One Action for One Reducer that is if you are changing the Redux State at any given point maintain 1–1 ratio of action to dispatch. The below example dispatching two actions to the same reducer triggers one extra unwanted Rerender. This is for one simple API Action but as and when the application grows a lot of these things go unnoticed.</li></ol><pre>const ActionGenerator = () =&gt; dispatch =&gt; {<br> dispatch(ON_LOADER);<br> api().then((data) =&gt; { <br>  dispatch(OFF_LOADER)<br>  dispatch(SET_DATA, data)<br> })</pre><pre>}</pre><pre>// This causes 2 updates to store and 2*componenets times Rerenders </pre><p>What if you have to dispatch multiple actions or you have to update two sub-states. You can use Redux Batch to speed to do this. batch API allows any React updates in an event loop tick to be batched together into a single render pass.</p><pre>import { batch } from &#39;react-redux&#39;</pre><pre>const myThunk = () =&gt; (dispatch, getState) =&gt; {<br> // should only result in one combined re-render, not two<br> batch(() =&gt; {<br>  dispatch(increment())<br>  dispatch(increment())<br> })<br>}</pre><h4>Avoiding Usage of Large Libraries:</h4><p>There are certain libraries like <a href="https://momentjs.com/">Moment</a>, <a href="https://lodash.com/">Loadash</a>, etc., that huge packages without the support of <a href="https://webpack.js.org/guides/tree-shaking/">Tree Shaking</a> (<em>Tree shaking</em> is a term commonly used in the JavaScript context for unused code elimination). These libraries increase the time for the initial load. Take a look below.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lDWiqFxAfUhMehQ4mwM94Q.png" /><figcaption>Moment JS bundle stats</figcaption></figure><p>A good alternative to Moment would be <a href="https://github.com/date-fns/date-fns">date-fns</a> (18kb)or <a href="https://github.com/iamkun/dayjs">DayJS</a> (2kb) day js has fewer features in comparison with date-fns. For lodash we started writing our own utils inspired by lodash.</p><p>We wrote most of our components from scratch without using any UI Framework like <a href="https://ant.design/"><strong>antd</strong></a><strong> </strong>thus reaping the benefits of performance.</p><p><em>📝On a side note, Ant Design is one of the most feature-packed React Libraries out there. Solves most of your problems with less development time and effort.</em></p><h4>Optimizing CSS and Assets</h4><p>As mentioned in the above point we build our own UI Components likewise we refrained from using popular CSS libraries like <a href="https://getbootstrap.com/">Bootstrap</a>, <a href="https://bulma.io/">Bulma</a>, etc., this means CSS contains only styles we used and not any other fluff.</p><p><a href="https://purgecss.com/"><strong>Purging CSS</strong></a><strong>: </strong>This is a tool that removes unused CSS. This is especially useful if you are using any CSS Library (we did not have to) it just removes all unwanted styles.</p><p><strong>SVG Components for all our assets: </strong>All the Design assets like Icons, Illustrations, Badges, etc., apart from Few Images were exported as SVG’s and used as React Components show below.</p><pre>// Component <br>import React from &#39;react&#39;;<br>import SvgWrapper from &#39;.&#39;;</pre><pre>const AssetsExample = props =&gt; (<br> &lt;SvgWrapper {...props}&gt;<br>  &lt;path&gt;&lt;/path&gt;<br>  // other SVG markup  <br> &lt;/SvgWrapper&gt;<br>);</pre><pre>// Usage</pre><pre>...<br>&lt;AssetsExample fill=&quot;#555555&quot; width={x} /&gt;<br>...</pre><p>The benefit of this is that all our assets are treated as JS files. They are compiled and minified along with other JS which makes it much lighter. Elsewise you would have to load SVG’s as Images even via a CDN.</p><h3>Lighthouse specific enhancements</h3><p><a href="https://developers.google.com/web/tools/lighthouse">Lighthouse</a> is an Industry standard for web performance. The criteria for scoring in Performance as of lighthouse 6.0 by order of weightage is <a href="https://web.dev/lcp/">Largest Contentful Paint</a>, <a href="https://web.dev/lighthouse-total-blocking-time/">Total Blocking Time</a>, <a href="https://web.dev/first-contentful-paint/">First Contentful Paint</a>, <a href="https://web.dev/speed-index/">Speed Index</a>, <a href="https://web.dev/interactive/">Time to Interactive</a> and <a href="https://web.dev/cls/">Cumulative Layout Shift</a>.</p><p>We targeted to maximize the top four criteria which contribute to 80% of the lighthouse score. As that is where most of our business impact is seen.</p><p><strong>Largest Contentful Paint — LCP [25%]</strong> LCP essentially means time taken to render out the largest image or text field visible within the viewport.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/890/1*GKEkTtA9RJzAhiDgHJrGtw.png" /></figure><p>elements that are considered <em>&lt;img&gt;, &lt;image&gt;, &lt;video&gt;</em>,An element with a background image loaded via the <em>url()</em> and Block elements with text or inline elements.</p><p>The size of the element reported for Largest Contentful Paint is typically the size that’s visible to the user within the viewport. If the element extends outside of the viewport, or if any of the element is clipped or has non-visible <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/overflow">overflow</a>, those portions do not count toward the element’s size.</p><p>For image elements that have been resized from their <a href="https://developer.mozilla.org/en-US/docs/Glossary/Intrinsic_Size">intrinsic size</a>, the size that gets reported is either the visible size or the intrinsic size, whichever is smaller. For example, images that are shrunk down to a much smaller than their intrinsic size will only report the size they’re displayed at, whereas images that are stretched or expanded to a larger size will only report their intrinsic sizes.</p><p>For text elements, only the size of their text nodes is considered (the smallest rectangle that encompasses all text nodes).</p><p>For all elements, any margin, padding, or border applied via CSS is not considered.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sNfHIWjS0370XKIY_4ZPYw.png" /><figcaption>credits: web.dev by google</figcaption></figure><p><strong>What we did</strong> was to introduce the use of Skeleton Loader with Shimmer Animation via `background-image: URL(&lt;imageUrl&gt;)` before even making the API call to fetch the data. Once the data is fetched we replace the skeleton loader with the actual UI.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*R-8AtDMq6VDPLkzdWdl_yw.png" /><figcaption>Old vs New app loading</figcaption></figure><p>This is a better way of communicating to the user what interface to expect as early as possible in the loading process. As they outline the structure itself, they can be hardcoded to appear while the server is still collecting and delivering all the page-specific content. Additionally, the user is provided with a sense of progression as the blank screen fills in with the skeleton and then the content, instead of just staring at a loading spinner or blank screen until the content is rendered.</p><p><strong>Total Blocking Time-TBT [25%]: </strong>TBT is the minimum wait time required by the user to use the application as in trigger events, such as mouse clicks, screen taps, or keyboard presses. The sum is calculated by adding the <em>blocking portion</em> of all <a href="https://web.dev/long-tasks-devtools">long tasks</a> between <a href="https://web.dev/first-contentful-paint/">First Contentful Paint</a> and <a href="https://web.dev/interactive/">Time to Interactive</a>. Any task that executes for more than 50 ms is a long task. The amount of time after 50 ms is the blocking portion. For example, if Lighthouse detects a 70 ms long task, the blocking portion would be 20 ms.</p><p>We did not actually do anything specifically to boost the TBT score. But I shall explain things that might push down TBT scores.</p><p>Loading, parsing, or execution js(javascript) impacts the time required for users to start interacting hence reducing bundle sizes by methods mentioned above like Lazy-Loading, Usage of Hooks and Nonusage of huge libraries reduces our bundle size which in turn reduced amount of js that has to be processed.</p><p><strong>FCP: </strong>The First Contentful Paint (FCP) metric measures the time from when the page starts loading to when any part of the page’s content is rendered on the screen. These contents might be Images, SVG elements, Text elements, and white canvas.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*FvqBbuRTWGRI7H_u.png" /><figcaption>credits: web.dev by google</figcaption></figure><p><strong>What we did:</strong> In a SPA(Single Page application) you would have a <strong><em>Root </em></strong>div element and your entire app would mount on inside that element. like the snippet below.</p><pre>&lt;body&gt;</pre><pre> &lt;div <em>id</em>=&quot;app&quot;&gt;&lt;/div&gt;</pre><pre>&lt;/body&gt;<br>...</pre><p>Without the loader in the html file.</p><pre>...<br>&lt;body&gt;</pre><pre>&lt;div <em>id</em>=&quot;app&quot;&gt;</pre><pre> &lt;div <em>id</em>=&quot;pageLoaderRoot&quot; <em>class</em>=&quot;loaderRoot&quot;&gt;<br>  &lt;img <em>class</em>=&quot;spinnerImage&quot; <em>src</em>=&quot;/path/to/loaderImg&quot;<br>   <em>alt</em>=&quot;Loading...&quot; /&gt;<br>  &lt;div <em>class</em>=&quot;barSpinner&quot;&gt;&lt;/div&gt;<br> &lt;/div&gt;</pre><pre>&lt;/div&gt;</pre><pre>&lt;/body&gt;<br>...</pre><p>With the loader inside the HTML file</p><p>Here we have placed a loader in form of an image inside <strong>Root Div</strong> with this the HTML file will load the loader first and later our JS bundles would override contents inside the div with our Application.</p><p>By adding the loader like this as soon as the index.html file is loaded the loader spinner is rendered ASAP. This by principle would boost FCP scores by a good margin.</p><p><strong>Speed Index:</strong> Speed Index measures how quickly content is visually displayed during page load. Lighthouse basically captures a video of the web page loading and calculates the visual difference between each frame.</p><p><strong>What we did:</strong></p><ol><li>Font Visibility: The easiest way to avoid showing invisible text while custom fonts load is to temporarily show a system font. By including font-display: swap in your @font-face style, you can avoid FOIT in most modern browsers</li><li>Preloading Key Requests: We preloaded all our fonts, CSS, and certain JS files via <a href="https://github.com/webpack/webpack">Webpack</a> and use of <a href="https://github.com/GoogleChromeLabs/preload-webpack-plugin">preload-webpack-plugin</a>. By doing this all our crucial resources required throughout the app are pre-loaded in a non-blocking manner.</li><li>Pre connecting to CDN and other tools: The preconnect directive allows the browser to <strong>setup early connections before an HTTP request is actually sent to the server</strong>. This includes DNS lookups, TLS negotiations, TCP handshakes. This in turn eliminates roundtrip latency and saves time for resolving used resources.</li><li>Purge CSS: <a href="https://purgecss.com/">PurgeCSS</a> is a tool to remove unused CSS. If you are using any third-party library or using your library for styling changes are that they are huge and you are not really using all the selectors. Purge CSS would scan all your selectors and remove all unused CSS that does not have a selector used.</li></ol><h3><strong>Epilogue</strong></h3><p>This blog is our story of how we achieved good web performance for a React JS based Single Page Application using standard SCSS. There are certain applications and processes like usage of CSS in JS or using Server Side Rendered(SSR) application that might differ in certain cases from the above.</p><p>The need to build fast applications came in as a necessity but the knowledge was my <strong>Life Long Learning</strong> from past experiences. Likewise, my work at <a href="https://www.upgrad.com/about/"><strong>upGrad</strong></a><strong> </strong>is now only about building applications but also to build a platform that <strong>empowers</strong> a ton of folks and helps build the <strong>Careers of Tomorrow</strong>. Do visit <a href="https://www.upgrad.com/"><strong>upGrad.com</strong></a> to check out our programs that are completely online! If you wish to work with our ever-enthusiastic team, check out <a href="https://www.upgrad.com/open-positions"><strong>our careers page</strong></a><strong>.</strong> We are always on the lookout for ambitious, talented folks!</p><p>If this blog was helpful do clap as much as possible and let us know in the comments how it helped you. If you feel anything amiss and there is more to it, Please drop in your comments we are more than happy to engage with your discussion.</p><p><strong>Resources</strong></p><p><a href="https://reactjs.org/docs/optimizing-performance.html">https://reactjs.org/docs/optimizing-performance.html</a> — React Specific optimizations.</p><p><a href="https://ui.dev/why-react-hooks/">https://ui.dev/why-react-hooks/</a> — React hooks why ?</p><p><a href="https://web.dev/performance-scoring/">https://web.dev/performance-scoring/</a> — Lighthouse scoring deep dive.</p><p><a href="https://dmitripavlutin.com/use-react-memo-wisely/">https://dmitripavlutin.com/use-react-memo-wisely/</a> — React Memo deep dive</p><p><a href="https://developers.google.com/web/tools/workbox/guides/configure-workbox">https://developers.google.com/web/tools/workbox/guides/configure-workbox</a> — workbox configuration deep dive</p><p><a href="https://medium.com/@joecrobak/production-deploy-of-a-single-page-app-using-s3-and-cloudfront-d4aa2d170aa3">https://medium.com/@joecrobak/production-deploy-of-a-single-page-app-using-s3-and-cloudfront-d4aa2d170aa3</a> — S3 deployment made easy</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2df5ee521a5f" width="1" height="1" alt=""><hr><p><a href="https://engineering.upgrad.com/how-we-improved-user-experience-by-building-a-lite-version-of-learner-experience-ui-2df5ee521a5f">How we improved user experience by building a lite version of the upGrad learner platform</a> was originally published in <a href="https://engineering.upgrad.com">Technology at upGrad</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to Role Based Access Control (RBAC) ? ‍♂️⚠️]]></title>
            <link>https://engineering.upgrad.com/how-to-role-based-access-control-9294146dc6e3?source=rss----c08812b771e6---4</link>
            <guid isPermaLink="false">https://medium.com/p/9294146dc6e3</guid>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[rbac]]></category>
            <category><![CDATA[node]]></category>
            <category><![CDATA[upgrad]]></category>
            <dc:creator><![CDATA[Arvind Narayan]]></dc:creator>
            <pubDate>Fri, 12 Jun 2020 14:21:23 GMT</pubDate>
            <atom:updated>2020-06-04T10:51:03.682Z</atom:updated>
            <content:encoded><![CDATA[<h3>How to Role Based Access Control (RBAC) ? 👮‍♂️⚠️</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/620/1*FODCuSopkLYNBCFcFf6t_g.jpeg" /><figcaption>like seriously some people ask me this</figcaption></figure><blockquote><strong>NOTE: </strong>For the sake of simplicity this article is going to be based on <a href="http://react.com/">React JS</a> along with <a href="https://github.com/ReactTraining/react-router">React Router</a> and <a href="https://nodejs.org">Node JS </a>on <a href="https://expressjs.com/">Express JS</a> for backend, all of which are awesome and do check them out.</blockquote><p>Have you ever wondered how in the world certain large scale applications like <a href="http://airbnb.com">airbnb</a>, <a href="http://facebook.com">facebook</a>, <a href="http://youtube.com">youtube</a> etc., manages to block a certain part of their application for certain people or release features based on certain conditions ? They all use a tried and tested methodology called RBAC this solution gained traction during the 90’s and still is widely used in a lot of forms. RBAC stands for Role Based Access Control as the title says and this article helps you fathom the RBAC game.</p><h3>Parameters to considers for RBAC :-</h3><h3><strong>1. Route Guarding </strong>🛣️:</h3><p>This is a process of authenticating 🔒 and authorising routes or part of a url in your web app. Typically you have a router switch with route paths couple. Here you can see that the routes stay in a different file to act as s single source of truth. In the switch instead of routes we wrap it with a wrapper and pass roles required path and component to be rendered as props.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcarbon.now.sh%2Fembed%3Fbg%3Drgba%28248%252C231%252C28%252C1%29%26t%3Dsynthwave-84%26wt%3Dnone%26l%3Dauto%26ds%3Dtrue%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D56px%26ph%3D56px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D133%2525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3Dmodule.exports%252520%25253D%252520%25257B%25250A%252509homePage%25253A%252520%25257B%25250A%252509%252509route%25253A%252520%2527%25252F%2527%25252C%25250A%252509%252509roles%25253A%252520%25255B%2527ROLE_TEACHER%2527%25252C%252520%2527ROLE_ADMIN%2527%25252C%252520%2527ROLE_EXTERNAL_SOURCER%2527%25255D%25250A%252509%25257D%25252C%25250A%252509createQuestion%25253A%252520%25257B%25250A%252509%252509route%25253A%252520%2527%25252Fquestion-bank%25252Fcreate%25252F%25253Atype%25252F%2527%25252C%25250A%252509%252509roles%25253A%252520%25255B%2527ROLE_TEACHER%2527%25252C%252520%2527ROLE_ADMIN%2527%25252C%252520%2527ROLE_EXTERNAL_SOURCER%2527%25255D%25250A%252509%25257D%25252C%25250A%252509testAttempt%25253A%252520%25257B%25250A%252509%252509route%25253A%252520%2527%25252Fstudent%25252Ftest%25252F%25253Agid%2527%25252C%25250A%252509%252509roles%25253A%252520%25255B%2527ROLE_TEACHER%2527%25252C%252520%2527ROLE_ADMIN%2527%25252C%252520%2527ROLE_STUDENT%2527%25255D%25250A%252509%25257D%25250A%25257D%25253B&amp;display_name=Carbon&amp;url=https%3A%2F%2Fcarbon.now.sh%2F%3Fbg%3Drgba%28248%25252C231%25252C28%25252C1%29%26t%3Dsynthwave-84%26wt%3Dnone%26l%3Dauto%26ds%3Dtrue%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D56px%26ph%3D56px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D133%252525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3Dmodule.exports%25252520%2525253D%25252520%2525257B%2525250A%25252509homePage%2525253A%25252520%2525257B%2525250A%25252509%25252509route%2525253A%25252520%27%2525252F%27%2525252C%2525250A%25252509%25252509roles%2525253A%25252520%2525255B%27ROLE_TEACHER%27%2525252C%25252520%27ROLE_ADMIN%27%2525252C%25252520%27ROLE_EXTERNAL_SOURCER%27%2525255D%2525250A%25252509%2525257D%2525252C%2525250A%25252509createQuestion%2525253A%25252520%2525257B%2525250A%25252509%25252509route%2525253A%25252520%27%2525252Fquestion-bank%2525252Fcreate%2525252F%2525253Atype%2525252F%27%2525252C%2525250A%25252509%25252509roles%2525253A%25252520%2525255B%27ROLE_TEACHER%27%2525252C%25252520%27ROLE_ADMIN%27%2525252C%25252520%27ROLE_EXTERNAL_SOURCER%27%2525255D%2525250A%25252509%2525257D%2525252C%2525250A%25252509testAttempt%2525253A%25252520%2525257B%2525250A%25252509%25252509route%2525253A%25252520%27%2525252Fstudent%2525252Ftest%2525252F%2525253Agid%27%2525252C%2525250A%25252509%25252509roles%2525253A%25252520%2525255B%27ROLE_TEACHER%27%2525252C%25252520%27ROLE_ADMIN%27%2525252C%25252520%27ROLE_STUDENT%27%2525255D%2525250A%25252509%2525257D%2525250A%2525257D%2525253B&amp;image=https%3A%2F%2Fcarbon.now.sh%2Fstatic%2Fbrand%2Fbanner.png&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;scroll=auto&amp;schema=carbon" width="1024" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/087d1d9433292ee3d4be959bb76eb4dd/href">https://medium.com/media/087d1d9433292ee3d4be959bb76eb4dd/href</a></iframe><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcarbon.now.sh%2Fembed%3Fbg%3Drgba%2874%252C144%252C226%252C1%29%26t%3Dmaterial%26wt%3Dnone%26l%3Dhtmlmixed%26ds%3Dfalse%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D56px%26ph%3D56px%26ln%3Dfalse%26fl%3D1%26fm%3DFira%2520Code%26fs%3D14px%26lh%3D152%2525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3D%25253CSwitch%25253E%25250A%252520%252520%25253CRouteWrapper%25250A%252520%252520%252520%252520exact%25250A%252520%252520%252520%252520path%25253D%25257BroutePaths.homePage.route%25257D%25250A%252520%252520%252520%252520roles%25253D%25257BroutePaths.homePage.roles%25257D%25250A%252520%252520%252520%252520Component%25253D%25257BQuestionListPage%25257D%25250A%252520%252520%25252F%25253E%25250A%252520%252520%25253CRouteWrapper%25250A%252520%252520%252520%252520exact%25250A%252520%252520%252520%252520path%25253D%25257BroutePaths.createQuestion.route%25257D%25250A%252520%252520%252520%252520roles%25253D%25257BroutePaths.createQuestion.roles%25257D%25250A%252520%252520%252520%252520Component%25253D%25257BCreateQuestions%25257D%25250A%252520%252520%25252F%25253E%25250A%252520%252520%25253CRouteWrapper%25250A%252520%252520%252520%252520exact%25250A%252520%252520%252520%252520path%25253D%25257BroutePaths.testListPage.route%25257D%25250A%252520%252520%252520%252520roles%25253D%25257BroutePaths.testListPage.roles%25257D%25250A%252520%252520%252520%252520Component%25253D%25257BTestListPage%25257D%25250A%252520%252520%25252F%25253E%25250A%252520%252520%25253CRoute%252520path%25253D%252522%25252Ferror%25252F%25253Acode%252522%25253E%25250A%252520%252520%252520%252520%25253CErrorComponent%252520%25252F%25253E%25250A%252520%252520%25253C%25252FRoute%25253E%25250A%252520%252520%25253CRoute%252520path%25253D%252522%2A%252522%25253E%25250A%252520%252520%252520%252520%25253CRedirect%252520to%25253D%252522%25252Ferror%25252F404%252522%252520%25252F%25253E%25250A%252520%252520%25253C%25252FRoute%25253E%25250A%25253C%25252FSwitch%25253E%25250A&amp;display_name=Carbon&amp;url=https%3A%2F%2Fcarbon.now.sh%2F%3Fbg%3Drgba%2874%25252C144%25252C226%25252C1%29%26t%3Dmaterial%26wt%3Dnone%26l%3Dhtmlmixed%26ds%3Dfalse%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D56px%26ph%3D56px%26ln%3Dfalse%26fl%3D1%26fm%3DFira%252520Code%26fs%3D14px%26lh%3D152%252525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3D%2525253CSwitch%2525253E%2525250A%25252520%25252520%2525253CRouteWrapper%2525250A%25252520%25252520%25252520%25252520exact%2525250A%25252520%25252520%25252520%25252520path%2525253D%2525257BroutePaths.homePage.route%2525257D%2525250A%25252520%25252520%25252520%25252520roles%2525253D%2525257BroutePaths.homePage.roles%2525257D%2525250A%25252520%25252520%25252520%25252520Component%2525253D%2525257BQuestionListPage%2525257D%2525250A%25252520%25252520%2525252F%2525253E%2525250A%25252520%25252520%2525253CRouteWrapper%2525250A%25252520%25252520%25252520%25252520exact%2525250A%25252520%25252520%25252520%25252520path%2525253D%2525257BroutePaths.createQuestion.route%2525257D%2525250A%25252520%25252520%25252520%25252520roles%2525253D%2525257BroutePaths.createQuestion.roles%2525257D%2525250A%25252520%25252520%25252520%25252520Component%2525253D%2525257BCreateQuestions%2525257D%2525250A%25252520%25252520%2525252F%2525253E%2525250A%25252520%25252520%2525253CRouteWrapper%2525250A%25252520%25252520%25252520%25252520exact%2525250A%25252520%25252520%25252520%25252520path%2525253D%2525257BroutePaths.testListPage.route%2525257D%2525250A%25252520%25252520%25252520%25252520roles%2525253D%2525257BroutePaths.testListPage.roles%2525257D%2525250A%25252520%25252520%25252520%25252520Component%2525253D%2525257BTestListPage%2525257D%2525250A%25252520%25252520%2525252F%2525253E%2525250A%25252520%25252520%2525253CRoute%25252520path%2525253D%25252522%2525252Ferror%2525252F%2525253Acode%25252522%2525253E%2525250A%25252520%25252520%25252520%25252520%2525253CErrorComponent%25252520%2525252F%2525253E%2525250A%25252520%25252520%2525253C%2525252FRoute%2525253E%2525250A%25252520%25252520%2525253CRoute%25252520path%2525253D%25252522%2A%25252522%2525253E%2525250A%25252520%25252520%25252520%25252520%2525253CRedirect%25252520to%2525253D%25252522%2525252Ferror%2525252F404%25252522%25252520%2525252F%2525253E%2525250A%25252520%25252520%2525253C%2525252FRoute%2525253E%2525250A%2525253C%2525252FSwitch%2525253E%2525250A&amp;image=https%3A%2F%2Fcarbon.now.sh%2Fstatic%2Fbrand%2Fbanner.png&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;scroll=auto&amp;schema=carbon" width="1024" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/eb237fbff1966e5996ef13bdd24f5757/href">https://medium.com/media/eb237fbff1966e5996ef13bdd24f5757/href</a></iframe><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcarbon.now.sh%2Fembed%3Fbg%3Drgba%2833%252C7%252C46%252C1%29%26t%3Dsynthwave-84%26wt%3Dnone%26l%3Dauto%26ds%3Dfalse%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dfalse%26wa%3Dtrue%26pv%3D56px%26ph%3D56px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D143%2525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3Dconst%252520RouteWrapper%252520%25253D%252520%28%25257BComponent%25252C%252520...props%252520%25257D%29%252520%25253D%25253E%252520%25257B%25250A%252509const%252520auth%252520%25253D%252520checkAuth%28%29%25253B%25250A%25250A%252509let%252520role%252520%25253D%252520getUserRole%28%29%25252C%25250A%252509%252509access%252520%25253D%252520checkAccess%28userRole%25252C%252520props.roles%29%25253B%25250A%25250A%252509if%252520%28%21auth%29%252520%25257B%25250A%252509%252509logout%28%29%25253B%25250A%252509%25257D%252520else%252520if%252520%28auth%252520%252526%252526%252520%21access%29%252520%25257B%25250A%252509%252509return%252520%25253CRedirect%252520to%25253D%252522%25252Ferror%25252F401%252522%252520%25252F%25253E%25253B%25250A%252509%25257D%25250A%25250A%252509return%252520%28%25250A%252509%252509%25253CRoute%25250A%252509%252509%252509%25257B...props%25257D%25250A%252509%252509%252509render%25253D%25257BrouteProps%252520%25253D%25253E%252520%25253CComponent%252520%25257B...routeProps%25257D%252520%25252F%25253E%25257D%25250A%252509%252509%25252F%25253E%25250A%252509%29%25253B%25250A%25257D%25253B&amp;display_name=Carbon&amp;url=https%3A%2F%2Fcarbon.now.sh%2F%3Fbg%3Drgba%2833%25252C7%25252C46%25252C1%29%26t%3Dsynthwave-84%26wt%3Dnone%26l%3Dauto%26ds%3Dfalse%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dfalse%26wa%3Dtrue%26pv%3D56px%26ph%3D56px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D143%252525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3Dconst%25252520RouteWrapper%25252520%2525253D%25252520%28%2525257BComponent%2525252C%25252520...props%25252520%2525257D%29%25252520%2525253D%2525253E%25252520%2525257B%2525250A%25252509const%25252520auth%25252520%2525253D%25252520checkAuth%28%29%2525253B%2525250A%2525250A%25252509let%25252520role%25252520%2525253D%25252520getUserRole%28%29%2525252C%2525250A%25252509%25252509access%25252520%2525253D%25252520checkAccess%28userRole%2525252C%25252520props.roles%29%2525253B%2525250A%2525250A%25252509if%25252520%28%21auth%29%25252520%2525257B%2525250A%25252509%25252509logout%28%29%2525253B%2525250A%25252509%2525257D%25252520else%25252520if%25252520%28auth%25252520%25252526%25252526%25252520%21access%29%25252520%2525257B%2525250A%25252509%25252509return%25252520%2525253CRedirect%25252520to%2525253D%25252522%2525252Ferror%2525252F401%25252522%25252520%2525252F%2525253E%2525253B%2525250A%25252509%2525257D%2525250A%2525250A%25252509return%25252520%28%2525250A%25252509%25252509%2525253CRoute%2525250A%25252509%25252509%25252509%2525257B...props%2525257D%2525250A%25252509%25252509%25252509render%2525253D%2525257BrouteProps%25252520%2525253D%2525253E%25252520%2525253CComponent%25252520%2525257B...routeProps%2525257D%25252520%2525252F%2525253E%2525257D%2525250A%25252509%25252509%2525252F%2525253E%2525250A%25252509%29%2525253B%2525250A%2525257D%2525253B&amp;image=https%3A%2F%2Fcarbon.now.sh%2Fstatic%2Fbrand%2Fbanner.png&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;scroll=auto&amp;schema=carbon" width="1024" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/0ecfb587b03ce2147db90cd55e1bf339/href">https://medium.com/media/0ecfb587b03ce2147db90cd55e1bf339/href</a></iframe><p>route wrapper here will check for authentication data and access based on the role needed by route and user role matrix. If you are un-authenticated the app will log you out to prevent conflicts, If you are authenticated but not authorised to access the route we will redirect to an error page. If your authenticated and authorised we will finally render the component. This check happens on all route changes for our protected routes.</p><h3>2. <strong>Restricting access to a part of the page </strong>🚏<strong>:</strong></h3><p>There might be certain situations where you might want to block features or a section of a page (route) while granting access to others. Here’s how we do it.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcarbon.now.sh%2Fembed%3Fbg%3Drgba%2833%252C7%252C46%252C1%29%26t%3Dsynthwave-84%26wt%3Dnone%26l%3Djsx%26ds%3Dfalse%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dfalse%26wa%3Dtrue%26pv%3D56px%26ph%3D56px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D143%2525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3Dimport%252520%25257B%252520useSelector%252520%25257D%252520from%252520%2527react-redux%2527%25253B%25250Aimport%252520%25257B%252520allowedUserChecker%252520%25257D%252520from%252520%2527utils%2527%25253B%25250Aimport%252520PropTypes%252520from%252520%2527prop-types%2527%25253B%25250A%25250Aconst%252520RBAC%252520%25253D%252520%28%25257B%252520allowedRoles%25252C%252520children%252520%25257D%29%252520%25253D%25253E%252520%25257B%25250A%252509let%252520userRoles%252520%25253D%252520useSelector%28state%252520%25253D%25253E%252520state.userInfo.roles%29%25253B%25250A%252509let%252520access%252520%25253D%252520allowedUserChecker%28userRoles%25252C%252520allowedRoles%29%25253B%25250A%252509return%252520access%252520%252526%252526%252520children%25253B%25250A%25257D%25253B%25250A%25250ARBAC.propTypes%252520%25253D%252520%25257B%25250A%252509allowedRoles%25253A%252520PropTypes.arrayOf%28PropTypes.string%29%25252C%25250A%252509children%25253A%252520PropTypes.element%25250A%25257D%25253B%25250A%25250Aexport%252520default%252520RBAC%25253B&amp;display_name=Carbon&amp;url=https%3A%2F%2Fcarbon.now.sh%2F%3Fbg%3Drgba%2833%25252C7%25252C46%25252C1%29%26t%3Dsynthwave-84%26wt%3Dnone%26l%3Djsx%26ds%3Dfalse%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dfalse%26wa%3Dtrue%26pv%3D56px%26ph%3D56px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D143%252525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3Dimport%25252520%2525257B%25252520useSelector%25252520%2525257D%25252520from%25252520%27react-redux%27%2525253B%2525250Aimport%25252520%2525257B%25252520allowedUserChecker%25252520%2525257D%25252520from%25252520%27utils%27%2525253B%2525250Aimport%25252520PropTypes%25252520from%25252520%27prop-types%27%2525253B%2525250A%2525250Aconst%25252520RBAC%25252520%2525253D%25252520%28%2525257B%25252520allowedRoles%2525252C%25252520children%25252520%2525257D%29%25252520%2525253D%2525253E%25252520%2525257B%2525250A%25252509let%25252520userRoles%25252520%2525253D%25252520useSelector%28state%25252520%2525253D%2525253E%25252520state.userInfo.roles%29%2525253B%2525250A%25252509let%25252520access%25252520%2525253D%25252520allowedUserChecker%28userRoles%2525252C%25252520allowedRoles%29%2525253B%2525250A%25252509return%25252520access%25252520%25252526%25252526%25252520children%2525253B%2525250A%2525257D%2525253B%2525250A%2525250ARBAC.propTypes%25252520%2525253D%25252520%2525257B%2525250A%25252509allowedRoles%2525253A%25252520PropTypes.arrayOf%28PropTypes.string%29%2525252C%2525250A%25252509children%2525253A%25252520PropTypes.element%2525250A%2525257D%2525253B%2525250A%2525250Aexport%25252520default%25252520RBAC%2525253B&amp;image=https%3A%2F%2Fcarbon.now.sh%2Fstatic%2Fbrand%2Fbanner.png&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;scroll=auto&amp;schema=carbon" width="1024" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/ff807c9b89d4fc348602f0b30605dfe5/href">https://medium.com/media/ff807c9b89d4fc348602f0b30605dfe5/href</a></iframe><p>This is a RBAC HOC ( <a href="https://reactjs.org/docs/higher-order-components.html">higher order components</a> ) we get the user roles from a global store (can be through other means as well) and we try to derive the access based on user role matrix check. We render the wrapped component only if access is permitted.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcarbon.now.sh%2Fembed%3Fbg%3Drgba%2833%252C7%252C46%252C1%29%26t%3Dsynthwave-84%26wt%3Dnone%26l%3Djsx%26ds%3Dfalse%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dfalse%26wa%3Dtrue%26pv%3D56px%26ph%3D56px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D143%2525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3Dimport%252520RBAC%252520from%252520%2527.%25252Fdir%25252FRBAC%2527%25253B%25250A...%25250A%25253CRBAC%252520allowedRoles%25253D%25257B%25255B%252520%2527ROLE_AUTHOR%2527%25252C%252520%2527ROLE_ADMIN%2527%25255D%25257D%25253E%25250A%252520%252520%252520%252520%25253CButton%252520type%25253D%252522submit%252522%252520label%25253D%252522VERIFY%252520AND%252520PUBLISH%252522%252520%25252F%25253E%25250A%25253C%25252FRBAC%25253E%25250A...&amp;display_name=Carbon&amp;url=https%3A%2F%2Fcarbon.now.sh%2F%3Fbg%3Drgba%2833%25252C7%25252C46%25252C1%29%26t%3Dsynthwave-84%26wt%3Dnone%26l%3Djsx%26ds%3Dfalse%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dfalse%26wa%3Dtrue%26pv%3D56px%26ph%3D56px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D143%252525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3Dimport%25252520RBAC%25252520from%25252520%27.%2525252Fdir%2525252FRBAC%27%2525253B%2525250A...%2525250A%2525253CRBAC%25252520allowedRoles%2525253D%2525257B%2525255B%25252520%27ROLE_AUTHOR%27%2525252C%25252520%27ROLE_ADMIN%27%2525255D%2525257D%2525253E%2525250A%25252520%25252520%25252520%25252520%2525253CButton%25252520type%2525253D%25252522submit%25252522%25252520label%2525253D%25252522VERIFY%25252520AND%25252520PUBLISH%25252522%25252520%2525252F%2525253E%2525250A%2525253C%2525252FRBAC%2525253E%2525250A...&amp;image=https%3A%2F%2Fcarbon.now.sh%2Fstatic%2Fbrand%2Fbanner.png&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;scroll=auto&amp;schema=carbon" width="1024" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/a713a431c4f9c3197ce452b048f2d628/href">https://medium.com/media/a713a431c4f9c3197ce452b048f2d628/href</a></iframe><h3>3. <strong>Securing Backend Endpoints </strong>🔗:</h3><p>Last but not the least we need to make sure the backend secures their API’s from their end as well.</p><blockquote>Well there are a ton of ways to do this using some API gateway, Proxy Servers etc., For the sake of simplicity I’m assuming certain parameters. These parameters are not necessary and you can implement the logic in way that you need as the crux is core logic only.</blockquote><p>First thing first you’ll need a role to routes file similar to what we saw in frontend.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcarbon.now.sh%2Fembed%3Fbg%3Drgba%28248%252C231%252C28%252C1%29%26t%3Dsynthwave-84%26wt%3Dnone%26l%3Dauto%26ds%3Dtrue%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D56px%26ph%3D56px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D133%2525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3Dmodule.exports%252520%25253D%252520%25257B%25250A%252509homePage%25253A%252520%25257B%25250A%252509%252509route%25253A%252520%2527%25252F%2527%25252C%25250A%252509%252509roles%25253A%252520%25255B%2527ROLE_TEACHER%2527%25252C%252520%2527ROLE_ADMIN%2527%25252C%252520%2527ROLE_EXTERNAL_SOURCER%2527%25255D%25250A%252509%25257D%25252C%25250A%252509createQuestion%25253A%252520%25257B%25250A%252509%252509route%25253A%252520%2527%25252Fquestion-bank%25252Fcreate%25252F%25253Atype%25252F%2527%25252C%25250A%252509%252509roles%25253A%252520%25255B%2527ROLE_TEACHER%2527%25252C%252520%2527ROLE_ADMIN%2527%25252C%252520%2527ROLE_EXTERNAL_SOURCER%2527%25255D%25250A%252509%25257D%25252C%25250A%252509testAttempt%25253A%252520%25257B%25250A%252509%252509route%25253A%252520%2527%25252Fstudent%25252Ftest%25252F%25253Agid%2527%25252C%25250A%252509%252509roles%25253A%252520%25255B%2527ROLE_TEACHER%2527%25252C%252520%2527ROLE_ADMIN%2527%25252C%252520%2527ROLE_STUDENT%2527%25255D%25250A%252509%25257D%25250A%25257D%25253B&amp;display_name=Carbon&amp;url=https%3A%2F%2Fcarbon.now.sh%2F%3Fbg%3Drgba%28248%25252C231%25252C28%25252C1%29%26t%3Dsynthwave-84%26wt%3Dnone%26l%3Dauto%26ds%3Dtrue%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D56px%26ph%3D56px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D133%252525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3Dmodule.exports%25252520%2525253D%25252520%2525257B%2525250A%25252509homePage%2525253A%25252520%2525257B%2525250A%25252509%25252509route%2525253A%25252520%27%2525252F%27%2525252C%2525250A%25252509%25252509roles%2525253A%25252520%2525255B%27ROLE_TEACHER%27%2525252C%25252520%27ROLE_ADMIN%27%2525252C%25252520%27ROLE_EXTERNAL_SOURCER%27%2525255D%2525250A%25252509%2525257D%2525252C%2525250A%25252509createQuestion%2525253A%25252520%2525257B%2525250A%25252509%25252509route%2525253A%25252520%27%2525252Fquestion-bank%2525252Fcreate%2525252F%2525253Atype%2525252F%27%2525252C%2525250A%25252509%25252509roles%2525253A%25252520%2525255B%27ROLE_TEACHER%27%2525252C%25252520%27ROLE_ADMIN%27%2525252C%25252520%27ROLE_EXTERNAL_SOURCER%27%2525255D%2525250A%25252509%2525257D%2525252C%2525250A%25252509testAttempt%2525253A%25252520%2525257B%2525250A%25252509%25252509route%2525253A%25252520%27%2525252Fstudent%2525252Ftest%2525252F%2525253Agid%27%2525252C%2525250A%25252509%25252509roles%2525253A%25252520%2525255B%27ROLE_TEACHER%27%2525252C%25252520%27ROLE_ADMIN%27%2525252C%25252520%27ROLE_STUDENT%27%2525255D%2525250A%25252509%2525257D%2525250A%2525257D%2525253B&amp;image=https%3A%2F%2Fcarbon.now.sh%2Fstatic%2Fbrand%2Fbanner.png&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;scroll=auto&amp;schema=carbon" width="1024" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/087d1d9433292ee3d4be959bb76eb4dd/href">https://medium.com/media/087d1d9433292ee3d4be959bb76eb4dd/href</a></iframe><p>ℹ️ <em>This might be a duplicate but try to host this somewhere common to solve your DRY OCD </em>💠<em> issues if any </em>😂<em> .</em></p><p>then this RBAC middleware below will help manage things for you automatically. Here it’s pretty simple check access and authorise / respond with error.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcarbon.now.sh%2Fembed%3Fbg%3Drgba%289%252C17%252C61%252C1%29%26t%3Dcobalt%26wt%3Dnone%26l%3Dauto%26ds%3Dtrue%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D56px%26ph%3D56px%26ln%3Dfalse%26fl%3D1%26fm%3DIBM%2520Plex%2520Mono%26fs%3D14px%26lh%3D143%2525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3Dmodule.exports%252520%25253D%252520%28rolesWithAccess%29%252520%25253D%25253E%252520%25257B%25250A%252520%252520let%252520%25257Broles%25257D%252520%25253D%252520req.user%25253B%25250A%252520%252520%25252F%25252F%252520eg%25253A%252520%252520roles%252520%25253D%252520%25255B%2527author%2527%25255D%252520comming%252520from%252520JWT%252520token%25250A%252520%252520return%252520%28req%25252C%252520res%25252C%252520next%29%252520%25253D%25253E%252520%25257B%25250A%252520%252520%252520%252520if%252520%28rolesWithAccess.length%252520%252526%252526%252520%21rolesWithAccess.includes%28roles%29%29%252520%25257B%25250A%252520%252520%252520%252520%252520%252520return%252520res.status%28401%29.json%28%25257B%252520message%25253A%252520%2527Unauthorized%2527%252520%25257D%29%25253B%25250A%252520%252520%252520%252520%252520%252520%25252F%25252F%252520send%252520however%252520you%252520want%252520this%252520one%252520way%252520of%252520sending%252520it.%25250A%252520%252520%252520%252520%25257D%252520else%252520%25257B%25250A%252520%252520%252520%252520%252520%252520next%28%29%25253B%25250A%252520%252520%252520%252520%25257D%25250A%252520%252520%25257D%25250A%25257D%25253B&amp;display_name=Carbon&amp;url=https%3A%2F%2Fcarbon.now.sh%2F%3Fbg%3Drgba%289%25252C17%25252C61%25252C1%29%26t%3Dcobalt%26wt%3Dnone%26l%3Dauto%26ds%3Dtrue%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D56px%26ph%3D56px%26ln%3Dfalse%26fl%3D1%26fm%3DIBM%252520Plex%252520Mono%26fs%3D14px%26lh%3D143%252525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3Dmodule.exports%25252520%2525253D%25252520%28rolesWithAccess%29%25252520%2525253D%2525253E%25252520%2525257B%2525250A%25252520%25252520let%25252520%2525257Broles%2525257D%25252520%2525253D%25252520req.user%2525253B%2525250A%25252520%25252520%2525252F%2525252F%25252520eg%2525253A%25252520%25252520roles%25252520%2525253D%25252520%2525255B%27author%27%2525255D%25252520comming%25252520from%25252520JWT%25252520token%2525250A%25252520%25252520return%25252520%28req%2525252C%25252520res%2525252C%25252520next%29%25252520%2525253D%2525253E%25252520%2525257B%2525250A%25252520%25252520%25252520%25252520if%25252520%28rolesWithAccess.length%25252520%25252526%25252526%25252520%21rolesWithAccess.includes%28roles%29%29%25252520%2525257B%2525250A%25252520%25252520%25252520%25252520%25252520%25252520return%25252520res.status%28401%29.json%28%2525257B%25252520message%2525253A%25252520%27Unauthorized%27%25252520%2525257D%29%2525253B%2525250A%25252520%25252520%25252520%25252520%25252520%25252520%2525252F%2525252F%25252520send%25252520however%25252520you%25252520want%25252520this%25252520one%25252520way%25252520of%25252520sending%25252520it.%2525250A%25252520%25252520%25252520%25252520%2525257D%25252520else%25252520%2525257B%2525250A%25252520%25252520%25252520%25252520%25252520%25252520next%28%29%2525253B%2525250A%25252520%25252520%25252520%25252520%2525257D%2525250A%25252520%25252520%2525257D%2525250A%2525257D%2525253B&amp;image=https%3A%2F%2Fcarbon.now.sh%2Fstatic%2Fbrand%2Fbanner.png&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;scroll=auto&amp;schema=carbon" width="1024" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/8b3c1ed0547a1d6f971c8c1ec13ec643/href">https://medium.com/media/8b3c1ed0547a1d6f971c8c1ec13ec643/href</a></iframe><p>Now we need to wire it up with our route like this. Thats it we got them unauthorised users 🛑</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcarbon.now.sh%2Fembed%3Fbg%3Drgba%289%252C17%252C61%252C1%29%26t%3Dcobalt%26wt%3Dnone%26l%3Djavascript%26ds%3Dtrue%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D56px%26ph%3D56px%26ln%3Dfalse%26fl%3D1%26fm%3DIBM%2520Plex%2520Mono%26fs%3D14px%26lh%3D143%2525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3Dconst%252520routes%252520from%252520%2527.%25252Fdir%25252Froutes%2527%25253B%25250Aconst%252520rbac%252520from%252520%2527%25252Fdir%25252Fto%25252Frbac%2527%25253B%25250Aconst%252520publishController%252520from%252520%2527%25252Fdir%25252Fto%25252Fcontrollers%2527%25253B%25250A%25250A...%25250A%25250Aapp.use%28routes.publish.route%25252C%252520rbac%28routes.publish.roles%29%25252C%252520publishController%29%25253B%25250A%25250A...&amp;display_name=Carbon&amp;url=https%3A%2F%2Fcarbon.now.sh%2F%3Fbg%3Drgba%289%25252C17%25252C61%25252C1%29%26t%3Dcobalt%26wt%3Dnone%26l%3Djavascript%26ds%3Dtrue%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D56px%26ph%3D56px%26ln%3Dfalse%26fl%3D1%26fm%3DIBM%252520Plex%252520Mono%26fs%3D14px%26lh%3D143%252525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3Dconst%25252520routes%25252520from%25252520%27.%2525252Fdir%2525252Froutes%27%2525253B%2525250Aconst%25252520rbac%25252520from%25252520%27%2525252Fdir%2525252Fto%2525252Frbac%27%2525253B%2525250Aconst%25252520publishController%25252520from%25252520%27%2525252Fdir%2525252Fto%2525252Fcontrollers%27%2525253B%2525250A%2525250A...%2525250A%2525250Aapp.use%28routes.publish.route%2525252C%25252520rbac%28routes.publish.roles%29%2525252C%25252520publishController%29%2525253B%2525250A%2525250A...&amp;image=https%3A%2F%2Fcarbon.now.sh%2Fstatic%2Fbrand%2Fbanner.png&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;scroll=auto&amp;schema=carbon" width="1024" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/08ad589905f9c914b2661d09eb256ce2/href">https://medium.com/media/08ad589905f9c914b2661d09eb256ce2/href</a></iframe><h3>Stay tuned 🕐 for part 2 of this article I might write about implementation other platform such as Next JS and GraphQL.</h3><p>I’m sure there are modifications / implementations to better the above article and also fun fact route paths can be a regex(<strong>/^\/(api|rest)\/.+$/</strong>) this is something that now many of you may know. Likewise, my work at <a href="https://www.upgrad.com/about/"><strong>upGrad</strong></a><strong> </strong>is now only about learning new things to better my career but also to build a platform that empowers multiple people in transitioning their careers as well. Do visit <a href="https://www.upgrad.com/"><strong>upGrad.com</strong></a> to check out our programs that are completely online! If you wish to work with our ever-enthusiastic team, check out <a href="https://www.upgrad.com/open-positions"><strong>our careers page</strong></a><strong>.</strong> We are always on the lookout for the ambitious, talented folks!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9294146dc6e3" width="1" height="1" alt=""><hr><p><a href="https://engineering.upgrad.com/how-to-role-based-access-control-9294146dc6e3">How to Role Based Access Control (RBAC) ? 👮‍♂️⚠️</a> was originally published in <a href="https://engineering.upgrad.com">Technology at upGrad</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Beginners guide to Analytics]]></title>
            <link>https://engineering.upgrad.com/beginners-guide-to-analytics-c8ce3e92fa42?source=rss----c08812b771e6---4</link>
            <guid isPermaLink="false">https://medium.com/p/c8ce3e92fa42</guid>
            <category><![CDATA[tracking]]></category>
            <category><![CDATA[tracking-user-activity]]></category>
            <category><![CDATA[funnel]]></category>
            <category><![CDATA[attribution]]></category>
            <category><![CDATA[reporting]]></category>
            <dc:creator><![CDATA[SHIV GARG]]></dc:creator>
            <pubDate>Fri, 12 Jun 2020 09:45:50 GMT</pubDate>
            <atom:updated>2020-03-30T13:58:42.813Z</atom:updated>
            <content:encoded><![CDATA[<h3>Beginner’s guide to web analytics</h3><p>If you are reading this blog, you might have encountered a situation where you need to implement some sort of user tracking for your product or you need to make some changes to the existing implementation and now you are looking for some material to understand the whole territory then you are at the right place.</p><p>In this blog, I will be covering all the major topics broadly, which you need to take care of when you are dealing with the analytics</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*V1YxREjMHvafd53w" /><figcaption>Credits: meya.ai</figcaption></figure><h3><strong>What is tracking?</strong></h3><p>The purpose of web tracking is for websites to gain insight into their users, their behaviour and preferences.</p><p>These insights serve to optimise user-friendliness and experience, as well as for statistical purposes, customisation, commerce, and profiling and targeted marketing.</p><p><strong>Why you need a separate platform for tracking?</strong></p><p>Most of people use separate platforms for tracking. Google Analytics, Mixpanel, Clevertap are some of the popular analytics platforms out there. Now the question is why would you want to purchase a tool for tracking whereas you can store this data in your database and save a lot of money. Here is why.</p><ol><li><strong>Separation of concerns</strong>- In your primary database you would like to store things that are close to your actual business e.g products, payments, users etc and you would like to focus on your business-specific code rather than tracking specific code.</li><li><strong>Focus on business-specific tools:</strong>- As a company, you would like to build tools important for your business. Building a tracking platform is not an easy job. It requires a substantial amount of effort and resources which you can spend on the other important things.</li><li>I<strong>nfrastructure considerations</strong>:- Scaling a tracking platform is the hardest thing. As you would like to track each and every click user does, a small app with hundreds of users can generate millions of concurrent events. Handling these many requests is a challenging task and can force you to maintain an infrastructure which could have been avoided in the first place.</li><li><strong>Features and user experience</strong>:- Most of the tracking platforms provide a lot of features e.g. auto-track properties, attribution models, funnel creation, A/B experiment, campaign param handling etc. which are hard to build and maintain. But most importantly these platforms provide a very nice and easy interface, which your business teams are generally familiar with and they can easily create reports.</li></ol><h3><strong>Start with the tracking</strong></h3><p>After you finalise and install a tracking platform. You can start tracking events. For the start, start with the top 10 events which are most important to your business and marketing.</p><p><strong>Important event Types</strong></p><ol><li><strong>Page views</strong>: Page views are the most important events when you start tracking. page views give you an estimate on how many users visited a particular page. Pageviews also help you create funnels, measure the impact of A/B experiment and so on. Most of the tracking platforms expose a separate method to track page views. You should rely on this method only to track page views instead of a custom event.</li><li><strong>Events</strong>: Events are the custom events which you need to fire when a user does specific events like button click, link click, and zoom a particular section. When you are talking about analytics, you are talking about any kind of click, scroll, video, the time elapsed events. On most of the platforms, an event consists of an event-name and few properties. Generally, events are of two types: interaction type and non-interaction type events. You should be really careful when you are marking events as interaction type /non-interaction type. Otherwise, your bounce rate metrics will get screwed.</li><li><strong>User calls</strong>: The third type of call that I am covering over here is user identification calls. The prime focus of this call is to track the user across multiple devices. Most of the tracking platforms set a cookie for distinct id in the user’s browser when the user opens your website for the first time. All the events by the user are tracked against this id. But what about events by the same user from some other device? For this purpose you do a identify call, in which you ask the tracking platform to recognise that particular user with your given id. Generally, you do an identification call after login/signup and ask tracking platform to track that user with the user id in your database. Now if the user logs in from some other device you can track that user with the same database id.</li><li><strong>Reset Calls</strong>: As much as user calls are important, reset calls are equally important. Reset calls help you to reset a particular user from the platform. All the tracking calls after the reset call are considered from a new user. Reset calls are important to make when the user logs out of your application.</li></ol><h3><strong>Reporting</strong></h3><p>If you have done all the above steps you have successfully set up tracking on your platform. Now the next big thing to achieve is reports on the analytics platform. All the tracking platforms offer a lot of views to see your data.</p><ol><li><strong>Reports</strong>: You can create reports based on your events and filter them based on the various event properties that you have sent or based on the properties the particular platform is tracking automatically e.g timestamp, UTM params, page URL etc. Most of the platforms offer a feature to create reports based on the unique events by users which is really helpful to understand the product.</li><li><strong>Funnels</strong>: Funnel is created to get information about the journey of a user when he is using your website. Most of the times funnels are used to see drop-offs at the various stages. e.g a common funnel for an e-commerce platform is search → product view → add to cart → product check out → payment. Now let say you rolled out a discount on a particular product you can see its effect by comparing the change in product view to add to cart ratio.</li><li><strong>Retention reports</strong>:- These reports give you insights on the probability of a user returning on your platform after n days or weeks. These reports are can give you really important insights on user patterns before purchasing a product and you can configure your marketing or re-marketing accordingly.</li><li><strong>Bounce Rate</strong>: Bounced users are the users who landed on your platform and closed platform without doing anything significant. You should create reports for bounce rate on the different sections of the platforms and specific to ads.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4hCqHG_soe3hjjklV8vZUw.png" /><figcaption>Sample Search Funnel</figcaption></figure><h3><strong>Attribution</strong></h3><p>When you are running advertisements on different ad platforms(e.g google, Facebook, youtube etc) you will need to understand that which ad platform is doing better for you and where it makes more sense to spend more. You will also need to bifurcate organic, direct traffic from the traffic coming via ads. For this purpose concept of attribution is used. Attribution in itself is a fairly complex concept.</p><p><strong>How attribution is performed</strong></p><p>In most of the cases, attribution is done via query params. <strong>Urchin Tracking Module</strong> (<strong>UTM</strong>) <strong>parameters</strong> are five variants of <a href="https://en.wikipedia.org/wiki/Query_string">URL parameters</a> used by <a href="https://en.wikipedia.org/wiki/Marketer">marketers</a> to track the effectiveness of online <a href="https://en.wikipedia.org/wiki/Marketing_campaign">marketing campaigns</a> across traffic sources and publishing media. Most of the tracking platforms give inbuilt support for UTM params. When you create an ad on a particular platform you associate unique UTM parameters in the URL. Later when a user lands on your platform by clicking the ad URL, these parameters are present in the URL and are automatically tracked by the tracking platform in all the events fired from your platform. You can later use this data to generate reports.</p><p><strong>Defining an attribution model</strong></p><p>Attribution models come into the scene when you have visitors landing on your platform from multiple campaigns/channels. Let&#39;s say you have a user who lands on your platform first from google ad link and then from Facebook ad after a day and then purchased something. To which ad will you attribute this sale? This is handled by attribution models. You need to select an attribution model which works best for your business. Most common attribution models used.</p><ol><li><strong>First touch</strong>:- In this model, you attribute a user to the first ad campaign which caused this user to land on your platform.</li><li><strong>Last touch</strong>:- In this model, you attribute a user to the last ad campaign from which the user landed on your platform and purchased the product.</li><li><strong>Linear</strong>:- In the Linear attribution model, each touchpoint in the conversion path would share equal credit for the conversion.</li><li><strong>Time decay model</strong>:- In the Time Decay attribution model, the touchpoints closest in time to the sale or conversion get most of the credit</li><li><strong>Position-based model</strong>:- In position-based attribution model, you divide majority attribution to first touch and last touch campaigns and a small percentage to intermediate campaigns.</li></ol><h3><strong>Conclusion and Next Steps:</strong></h3><p>Although all the above-mentioned things can help you set up the tracking infra for your business. But the list is not exhaustive. Tracking is a very vast topic and it’s not possible to cover everything in a single article. As the next step in your learning, you can read more on</p><ol><li>Third-party cookies and how they are used to track the user across different platforms.</li><li>Marketing pixels.</li><li>Server-side tracking.</li><li>Advanced E-Commerce, Video Analytics, form field analytics.</li><li>Re-marketing</li></ol><p>And the list goes on.</p><p>I hope this article helps you and gives you good insights and a head start in the area and you know your users better.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c8ce3e92fa42" width="1" height="1" alt=""><hr><p><a href="https://engineering.upgrad.com/beginners-guide-to-analytics-c8ce3e92fa42">Beginners guide to Analytics</a> was originally published in <a href="https://engineering.upgrad.com">Technology at upGrad</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Creating and deploying chrome extension on Web Store]]></title>
            <link>https://engineering.upgrad.com/creating-and-deploying-chrome-extension-on-web-store-15f779908eb3?source=rss----c08812b771e6---4</link>
            <guid isPermaLink="false">https://medium.com/p/15f779908eb3</guid>
            <category><![CDATA[chrome]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[css]]></category>
            <category><![CDATA[chrome-extension]]></category>
            <category><![CDATA[html]]></category>
            <dc:creator><![CDATA[Jasmin Virdi]]></dc:creator>
            <pubDate>Tue, 21 Jan 2020 12:45:07 GMT</pubDate>
            <atom:updated>2020-01-21T12:50:52.449Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*e3BE4Z9khDpBFJBbKhpkZw.jpeg" /><figcaption><a href="https://www.flickr.com/photos/86979666@N00/6891191751">https://www.flickr.com/photos/86979666@N00/6891191751</a></figcaption></figure><p>A browser extension is a mini-application. It can help in many ways like adding additional features to your browser, modifying content on the website or integrating with other existing services. They are useful in customizing the user’s browsing experience and enable them to tailor Chrome functionality and behavior to individual needs and preferences. An extension must fulfill a single purpose that is narrowly defined and easy to understand. Sometimes a single extension can include multiple components and a range of functionality, as long as everything contributes to a common purpose.</p><h3><strong><em>Creating your first Chrome extension</em></strong></h3><p>Let’s begin by creating <strong><em>a manifest.json</em></strong><em> </em>file. This file contains all the properties which help us in loading our application in chrome.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6d264f8aec43b85d07e69296e918ba21/href">https://medium.com/media/6d264f8aec43b85d07e69296e918ba21/href</a></iframe><p>Most of the fields are self-explanatory but take note of the following:</p><ul><li><strong>browser_action</strong> where we specify the default_icon and default HTML page of the extension.</li><li><strong>permissions</strong> section which specifies the APIs intent which we will be using in our application.</li></ul><p>After this, we can proceed with our HTML and JS file. Since we have declared popup.html in the manifest.json so we will create an HTML file with that name and write the following code in it.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b87551d03a1f7506de097d753472ee81/href">https://medium.com/media/b87551d03a1f7506de097d753472ee81/href</a></iframe><p>Include all custom CSS and JS files. If you have any other dependency you can specify in this file like we have added the jQuery. Below is the code for your popup.js file.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d9b2ed94f5b536f68491b603cf696825/href">https://medium.com/media/d9b2ed94f5b536f68491b603cf696825/href</a></iframe><p>You can test your chrome extension locally by doing the following steps:</p><ol><li><strong>In your chrome type</strong> chrome://extensions</li></ol><p>2.<strong> Turn on the Developer Mode</strong> as this will allow you to upload your created extension on chrome and test it locally.</p><p><strong>3. Click on “Load Unpacked” </strong>and<strong> </strong>select your project folder and now the icon of your extension should appear on the right-hand corner.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qdU2VIRRn2jjTJJv0he5Yg.png" /></figure><p>You can now see your chrome extension at the top right corner.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hWiKy4Ahfi4jKKdUM5Ek7w.png" /></figure><h3>Deploying on web store</h3><p>Instead of including <em>popup.js</em> file directly we can have a minified file by using webpack. Create a <em>webpack.config.js</em> file and add the path to your JS file in place of “<em>path_to_.js”</em> as specified in the below code snippet. This will yield better results.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3dc4496bd4bbc5d8c3182f8d51e00e10/href">https://medium.com/media/3dc4496bd4bbc5d8c3182f8d51e00e10/href</a></iframe><p>When uploading the extension on the Web Store we need to perform certain steps like creating a <strong><em>zip folder</em> </strong>and <strong><em>updating the version</em></strong> in <em>manifest.json</em>. All these tasks can be done by creating a build script to automate the process.</p><p>In <em>package.json</em> you need to specify the following script:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d1d89b2455c587ef7d2903580b5f49d7/href">https://medium.com/media/d1d89b2455c587ef7d2903580b5f49d7/href</a></iframe><p>This build script can be easily used to package data for deploying it on the Web Store. The <em>build.js</em> script will have the following code:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ce9f79ea0528c7a672631eddab81b586/href">https://medium.com/media/ce9f79ea0528c7a672631eddab81b586/href</a></iframe><p>Now you can use <strong>npm run build</strong> to have complete chrome extension together with a zip of your src folder.</p><h3><strong>Conclusion</strong></h3><p>All these steps will help us in building a basic chrome extension project. There are many features like OAuth, Message Passing, events tracking using analytics tool and user privacy, etc which can be implemented in extension for better performance and results. I have referred the<a href="https://developer.chrome.com/extensions/devguide"><em> chrome developer guide</em></a><em> </em>thoroughly for creating the upGrad <strong>Transition Tracker</strong> extension which includes these advanced features. This extension helps the learners to add any job on the web to their Career Centre by using a job link.</p><p><strong><em>Check out upGrad’s chrome extension!</em></strong></p><p><a href="https://chrome.google.com/webstore/detail/transition-tracker/pdfhpcefhfnobpelmlpnjbnfojfconja">Transition Tracker</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=15f779908eb3" width="1" height="1" alt=""><hr><p><a href="https://engineering.upgrad.com/creating-and-deploying-chrome-extension-on-web-store-15f779908eb3">Creating and deploying chrome extension on Web Store</a> was originally published in <a href="https://engineering.upgrad.com">Technology at upGrad</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The One with Vue in React]]></title>
            <link>https://engineering.upgrad.com/the-one-with-vue-in-react-533e701e8de1?source=rss----c08812b771e6---4</link>
            <guid isPermaLink="false">https://medium.com/p/533e701e8de1</guid>
            <category><![CDATA[vuejs-2]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[javascript-tips]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[vuejs]]></category>
            <dc:creator><![CDATA[Anish Kumar]]></dc:creator>
            <pubDate>Mon, 30 Dec 2019 11:43:38 GMT</pubDate>
            <atom:updated>2019-12-30T11:43:38.536Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/500/0*jp0Wkh2h9nyn8cU8.gif" /></figure><blockquote>Why would you do that?</blockquote><p>That was the first reply I got when I mentioned this in the React community. Well, can’t blame them, you might be wondering the same.</p><p><strong><em>I will tell you why</em></strong></p><p>The Vue widget here was a tagging plugin, that we have to integrate across different platforms with tech stacks including Backbone.js and React.js.</p><p>Now you might ask:</p><blockquote>Why didn’t you build it in React then?</blockquote><p>We went for Vue because it provided a much easier and faster development pace and not to mention a much smaller build size. By now the React devs are frowning and grinding their teeth. I know comparing frameworks and libraries can be a controversial topic.</p><p>Just for the record, I am a React fan</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/480/0*w41tjedv1EHHnvVA.gif" /><figcaption>Don’t kill me</figcaption></figure><p><em>So let&#39;s continue the story</em></p><p>My task was to integrate the tagging plugin into 5 types of forms in the test platform which is currently under development ⚒. The platform provides 5 types (Multiple Correct, Single Correct, Numerical, Coding, and Open Text questions) of question creation forms.</p><p>The first thing was obviously to make a separate Tagging Component in React, which will handle all the integration logic of the Vue widget.</p><p><em>Let&#39;s move to the integration</em></p><p>Like every other developer, my first instinct was to google whether someone has done this before. I was confident enough that there will be someone who has faced the same problem. Unfortunately, I wasn’t able to find any example. Whaaaat?!!! That&#39;s the main reason I am writing this blog.</p><p><em>What now?</em></p><p>I knew the only place where I can get help is the Vue docs. To my luck, the Vue documentation was as clear as the frame on Monica’s door. For all those muggles out there (the ones who aren’t crazy fans of F.R.I.E.N.D.S), this just means crystal-clear.</p><p>To begin with, what I needed was a way to create an instance of a Vue app and mount it on a div in the React tagging plugin component. By the way, React doesn’t render a Vue widget if you directly use it in the render HTML like this 👇</p><pre>function TaggingPlugin(){<br>//react tagging plugin component</pre><pre>...</pre><pre>return &lt;vue-tagging-plugin/&gt; // vue tagging component<br>}</pre><p>So I had to create the Vue instance programmatically, like this 👇</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6aa8076f1ba1ee814aee57a1c4fc08f7/href">https://medium.com/media/6aa8076f1ba1ee814aee57a1c4fc08f7/href</a></iframe><p>Not to worry, I am gonna explain this in detail</p><pre>var vm = new Vue({   // options })</pre><p>This creates a Vue app instance and mounts it on the <strong><em>el</em></strong><em> </em>which is the root HTML node.</p><p>We have a Vue instance created and the node to be mounted on. What we need now is the widget we need to render in the Vue app, which brings us to the very important part 👇</p><pre>render: function(h) { // h = createElement</pre><pre>     return h(window.taggingPlugin, {</pre><pre>        props: this.props</pre><pre>     });</pre><pre>}</pre><p>The <strong><em>render</em></strong> API of Vue is passed a <a href="https://vuejs.org/v2/guide/render-function.html#createElement-Arguments">createElement</a> function. This creates the HTML, hydrates it with the data &amp; props if any and finally returns a Vue node which is then appended to the DOM. The first argument to createElement can be an HTML tag name, component option or any async function that resolves (which in our case is the instance we created) to any of the above. The second argument is an object with attributes that you use in a normal Vue component definition, in my case, the props for the widget.</p><p>Now we have is the <strong><em>data </em></strong>prop 👇</p><pre>data() {     <br>    return {<br>          ...     <br>          props: {</pre><pre>          }<br>    }<br>}</pre><p>The data prop in Vue is basically the equivalent of React component’s state. Now you might be wondering why did I create a props object in the data and use that props, in turn, to pass it to the Vue <strong><em>createElement’s</em></strong> prop. So the reason is that the widget I created is a child of the root Vue Instance and it was also a better way to pass down the data from the root of the app itself, instead of directly injecting the props from React component.</p><pre>return h(window.taggingPlugin, {          <br>     props: this.props     <br>});</pre><p>The <strong>this</strong> here is the Vue instance, so we can easily access the data of the Vue app. So whenever I needed to update the id prop in the Vue widget from React component, I would do something like this 👇</p><pre>useEffect(() =&gt; {</pre><pre>     window.taggingInstance.props.config.componentId = id;</pre><pre>}, [id]);</pre><p>Well the part where everything comes together 👇</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3c695ed677ecb994b83a1f03191a3a30/href">https://medium.com/media/3c695ed677ecb994b83a1f03191a3a30/href</a></iframe><p>This is the React component after all the integration with Vue widget.</p><p>Also, JFYI, for the <strong><em>taggingPlugin</em></strong> to be available in the window you have to add the widget build js file, CSS file and Vue bundle files in your index.html of the React app.</p><p><strong>Conclusion</strong></p><p>Not finding any solution when I googled this for the first time was a turning point. Otherwise, I wouldn’t have gone through the amazing Vue docs and got a deeper understanding of Vue internals - how a component is rendered, how the data flow works, and most of all, the rush of finding a way out to solve such an interesting problem. I am sure that there will be better and efficient ways of doing this. If there are any such references or methods please do add them in the comments.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=533e701e8de1" width="1" height="1" alt=""><hr><p><a href="https://engineering.upgrad.com/the-one-with-vue-in-react-533e701e8de1">The One with Vue in React</a> was originally published in <a href="https://engineering.upgrad.com">Technology at upGrad</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>