<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sahil Khurana</title>
    <description>The latest articles on DEV Community by Sahil Khurana (@sahil_khurana_486f374ecf2).</description>
    <link>https://dev.to/sahil_khurana_486f374ecf2</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3911388%2F81bf0e28-1844-48e4-8e2d-1c321202e68d.jpg</url>
      <title>DEV Community: Sahil Khurana</title>
      <link>https://dev.to/sahil_khurana_486f374ecf2</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sahil_khurana_486f374ecf2"/>
    <language>en</language>
    <item>
      <title>AWS API Gateway and Elastic: The Perfect Pair for Seamless API Management</title>
      <dc:creator>Sahil Khurana</dc:creator>
      <pubDate>Thu, 18 Jun 2026 05:59:40 +0000</pubDate>
      <link>https://dev.to/sahil_khurana_486f374ecf2/aws-api-gateway-and-elastic-the-perfect-pair-for-seamless-api-management-ae</link>
      <guid>https://dev.to/sahil_khurana_486f374ecf2/aws-api-gateway-and-elastic-the-perfect-pair-for-seamless-api-management-ae</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Key takeaways&lt;/strong&gt;
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;AWS API Gateway makes the API easier to manage through a single console that eases API organization and management, improves scalability as well as performance, and comes at a lower cost as well as being more secure.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;AWS API Gateway is supported by Elasticsearch to offer efficient search and analysis, which makes data indexing fast, real-time analysis possible, and data visualisation insightful, irrespective of the data type.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;AWS API Gateway also integrates well with Elastic Observability, thus allowing the easy tracking of API Key metrics/logs in real-time and enhancing the ability to pinpoint problems and also enhancing the drive towards increased API efficiency.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What Is An API Gateway?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This is not complicated at all. API Gateway is, in essence, a gateway - an intermediary layer placed between the client accessing your API and the server responsible for responding to it.&lt;/p&gt;

&lt;p&gt;Amazon AWS API Gateway is a product offered by Amazon and serves as a fully-managed implementation of such a gateway. Every request directed either towards a Lambda function, EC2 instance, or even some on-premise infrastructure has to go through this API Gateway first. This is when things like authentication, rate limiting, and validating requests occur, before passing the request on to your actual application.&lt;/p&gt;

&lt;p&gt;One thing I really appreciate about this solution is that you do not need to re-invent the wheel anymore - there's no need for developing security layers or anything like that.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Why Integrate AWS API Gateway into Your System?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Early on, a small project can get away with direct API calls and minimal structure. But once you have multiple services, multiple teams, and real traffic , things start breaking in ways that are hard to trace. API Gateway gives you a layer of control that makes all of it manageable.&lt;/p&gt;

&lt;p&gt;Here's what that looks like day-to-day:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Everything in one console &lt;/strong&gt;- instead of chasing logs across five services, you have a single place to see what every API is doing.&lt;br&gt;
&lt;strong&gt;Auto-scaling that actually works&lt;/strong&gt; - when traffic spikes, it adjusts. Built-in caching means repeated requests don't hammer your backend unnecessarily.&lt;br&gt;
&lt;strong&gt;Security without the headache&lt;/strong&gt; - auth, access policies, and fine-grained permissions are built in. Not an afterthought.&lt;br&gt;
&lt;strong&gt;Cost controls that matter&lt;/strong&gt; - throttling and quotas stop a misbehaving client from running up your AWS bill.&lt;br&gt;
&lt;strong&gt;CloudWatch integration&lt;/strong&gt; - your HTTP, REST, and WebSocket logs go straight into CloudWatch with no extra wiring needed.&lt;/p&gt;

&lt;p&gt;It also opens up patterns that are harder to pull off otherwise - serverless APIs through Lambda, hybrid setups mixing cloud and on-prem, and API endpoints that IoT devices can hit directly. That last one is surprisingly useful if you're working anywhere near connected hardware.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What is Elasticsearch?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Elasticsearch explained for a layman: Imagine you have a warehouse with countless boxes, and you need to find a particular object there as soon as possible. Then, Elasticsearch is your solution because it knows where everything is located, even without any labels on those boxes.&lt;/p&gt;

&lt;p&gt;On the technical side, Elasticsearch is defined as an open-source distributed search engine and analytics engine, which forms the core component of the Elastic Stack. You may feed it with structured logs, unstructured logs, metric data, or geolocation data; it will process it immediately.&lt;/p&gt;

&lt;p&gt;The part that actually impressed me when I first used it was how well it handles scale. You start small, and as your data grows, Elasticsearch grows with it. The distributed architecture isn't just a marketing claim - it actually delivers.&lt;/p&gt;

&lt;p&gt;They can leverage it for numerous purposes: from application discovery to log analysis, anomaly detection using machine learning to dedicated research pipelines. Coupled with Logstash and Beats for data ingestion and Kibana for visualizations, they will get themselves an observability stack that covers a wider range of use cases than one might think.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Monitoring AWS API Gateway with Elastic Observability&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This is the part worth paying attention to.&lt;br&gt;
API Gateway, on its own, generates data. Elastic Observability turns that data into something you can actually act on. The difference sounds subtle until you've spent three hours debugging a latency issue with no clear dashboard to reference - then it becomes very obvious.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Connecting both of these is pretty easy&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sign up for an Elastic Cloud account and deploy your application with the agent.&lt;/li&gt;
&lt;li&gt;From within Elastic, click on Add Integration and search for AWS API Gateway.&lt;/li&gt;
&lt;li&gt;Use your AWS credentials - this integration supports access keys, IAM role ARNs, as well as temporary security credentials.&lt;/li&gt;
&lt;li&gt;Enable logging in CloudWatch from the API Gateway side, and you're good to go.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once it's running, you'll have real-time visibility into metrics that actually tell you something useful:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;4XXError / 5XXError&lt;/strong&gt; - Are failures coming from clients or your backend?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Latency / Integration: Latency&lt;/strong&gt; - Where exactly is time being lost in the request cycle?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CacheHitCount / CacheMissCount &lt;/strong&gt;- Is your caching setup doing what you think it is?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Count &lt;/strong&gt;- Total request volume across any time window you care about&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DataProcessed&lt;/strong&gt; - Volume of data moving through your APIs
ConnectCount / MessageCount - WebSocket connection and message tracking&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The dashboards you get out of the box are genuinely useful, not just pretty. And when you're ready to go deeper, they're easy enough to customize.&lt;/p&gt;

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

&lt;p&gt;There's a version of API management where you're constantly reacting - a service goes down, someone notices, you dig through logs, you eventually figure out what happened. It's exhausting, and it's avoidable.&lt;br&gt;
Connecting AWS API Gateway with Elastic Observability shifts you into a more proactive position. You see what's happening before it becomes a problem. You make decisions based on actual data instead of gut instinct.&lt;br&gt;
For teams that care about reliability - and honestly, all teams should - this is one of the more worthwhile setups you can put in place.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;About Innostax&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Innostax provides businesses with outsourced resources for their software development through our services or through our staff augmentation program. We primarily serve startups, scaleups, and Digital Agencies, all of which require contracted development experts when they require assistance to augment their Development Team or with a custom solution to build their product, and as a result, do not want to incur the cost and time to set up and manage an internal Dev Team.&lt;/p&gt;

&lt;p&gt;Want to see how we deliver scalable, cost-effective Python development without the overhead of an in-house team? We've broken down the full approach - talent sourcing, project onboarding, tech stack decisions, and business outcomes - in detail on the Innostax blog.&lt;br&gt;
Read more: AWS API Gateway and Elastic: The Perfect Pair for Seamless API Management&lt;/p&gt;

&lt;p&gt;Read more: &lt;strong&gt;&lt;a href="https://innostax.com/blog/aws-api-gateway-and-elastic-search/" rel="noopener noreferrer"&gt;AWS API Gateway and Elastic: The Perfect Pair for Seamless API Management&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>api</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>MEAN Stack and MERN Stack: What’s Best for Your Business Needs?</title>
      <dc:creator>Sahil Khurana</dc:creator>
      <pubDate>Wed, 17 Jun 2026 11:43:34 +0000</pubDate>
      <link>https://dev.to/sahil_khurana_486f374ecf2/mean-stack-and-mern-stack-whats-best-for-your-business-needs-72j</link>
      <guid>https://dev.to/sahil_khurana_486f374ecf2/mean-stack-and-mern-stack-whats-best-for-your-business-needs-72j</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Main Points&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Therefore, MEAN is all about combining MongoDB, Express.js, Angular, and Node.js in a single JavaScript environment. All languages involved use the same programming language, and it definitely makes everything simpler during development. It is perfect when developing scalable web apps.&lt;/p&gt;

&lt;p&gt;The difference between MEAN and MERN consists in replacing Angular with React while keeping the rest components identical. MERN allows using React for designing the front-end part of the application, and that makes the process of interface generation faster. It will suit best single-page applications or other web applications that do not aim at the enterprise level.&lt;br&gt;
In general, deciding on MEAN or MERN stack will depend on the specifics of the project that is being developed and the skills of a particular team.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;MERN versus MEAN Stack&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In a MEAN stack, JavaScript is used for every operation. JavaScript is used to handle database queries. JavaScript is used for writing server-side logic code. The frontend is written using JavaScript as well. In other words, no need to switch between different languages here.&lt;/p&gt;

&lt;p&gt;Data handling in the MEAN stack is done by MongoDB. Express.js is used to handle server-side operations. The UI of applications built using a MEAN stack is designed using Angular. Node.js converts JavaScript into server-side code.&lt;br&gt;
A MERN stack works in a similar manner except that React replaces Angular to design the UI of web applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What is MEAN Stack Development?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;MEAN brings together four technologies that work well together:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MongoDB&lt;/strong&gt;: This NoSQL database doesn't lock you into rigid table structures. Everything's stored in JSON-like documents, so you can adjust your data model without massive migrations. Really flexible when requirements change.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Express.js&lt;/strong&gt;: Minimalist server framework. Nothing but what is needed for your server logic. Doesn’t make you configure everything endlessly just to get started.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Angular&lt;/strong&gt;: Created by Google to be a full-featured frontend framework. It’s quite prescriptive in its way of doing things. Some people like the orderliness. Other people dislike being constrained.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Node.js&lt;/strong&gt;: Executes JavaScript code on servers using Google’s V8 engine. Good at dealing with asynchronous processes, so your app stays responsive even under load.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;MEAN Stack: The Real Talk&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What Works&lt;/strong&gt;: Code reusability across platforms cuts development time significantly. Massive open-source ecosystem with tons of libraries. Genuinely excellent for real-time, interactive applications where users expect instant responses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Challenges&lt;/strong&gt;: Without careful architecture, your codebase turns messy fast. Data partitioning gets complicated at scale. Configuration mistakes can lead to data loss, which nobody wants to explain to stakeholders.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What is MERN Stack Development?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;MERN keeps the same backend but switches to React for the frontend:&lt;br&gt;
&lt;strong&gt;MongoDB&lt;/strong&gt;: Still your flexible NoSQL database handling data storage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Express.js&lt;/strong&gt;: Still your reliable framework building web apps and RESTful APIs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;React&lt;/strong&gt;: Facebook created this component-based library for interfaces. You build reusable components that snap together. Need updates? Change one component instead of rewriting everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Node.js&lt;/strong&gt;: Same server-side JavaScript runtime as MEAN, handling all backend operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;MEAN Stack vs MERN Stack: Core Differences&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The choice between MEAN and MERN can be much more straightforward since they both utilize MongoDB, Express.js, and Node.js. However, there is one question where the answers will determine which way to go: Angular or React.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That's how MEAN and MERN frameworks are different:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Front-End Framework: Angular vs React&lt;/strong&gt;&lt;br&gt;
Angular (MEAN) is an entire MVC framework. All you need is included, no need to search for additional libraries.&lt;br&gt;
React (MERN) is a simple UI framework which can create complex applications by itself but requires some help in other areas, such as Redux and React-router.&lt;br&gt;
MEAN should be your choice if you value stability and consistency. Otherwise, MERN may become an advantage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Learning Curve and Development Speed&lt;/strong&gt;&lt;br&gt;
Angular requires more effort – TypeScript, decorators, dependency injection. Experienced teams have no trouble with it, but new developers or time pressure will cause some delays.&lt;br&gt;
React is much easier to learn. The community is large, documentation is plentiful, and most developers can become productive very quickly.&lt;br&gt;
In case you're working under time pressure or don't have a highly skilled team, go for MERN. If the project is long and complicated and your team has lots of experience, MEAN will be a better choice.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Restful API Development&lt;/strong&gt;&lt;br&gt;
To be honest, there isn't much difference here. Both stacks use Express.js on the backend side, so developing APIs using either stack will feel virtually the same. Routing, middleware, request handling – nothing to choose here.&lt;br&gt;
Don't let this factor affect your decision.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Community Support&lt;/strong&gt;&lt;br&gt;
Angular is developed by Google – long-term support, regular updates, enterprise-friendly stack. Not likely to disappear anytime soon.&lt;br&gt;
React has a larger overall community and an enormous ecosystem of libraries and reusable components. Hiring React developers is also easier than Angular at the moment.&lt;br&gt;
MEAN is more stable. MERN is more versatile.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Which Stack Should You Choose?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Choose MEAN when your team knows TypeScript, you're building enterprise-grade software, and structure matters more than speed.&lt;br&gt;
Choose MERN when you need fast iteration, a flexible UI, and your team prefers JavaScript over TypeScript.&lt;br&gt;
Both are battle-tested. The right choice is the one that fits your team's strengths — not whatever's trending right now.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;So Which One Should You Actually Pick?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Want flexibility and simplicity? MERN's probably calling your name. Need a comprehensive framework with structure and guidance? MEAN with Angular might fit better.&lt;/p&gt;

&lt;p&gt;Both stacks build modern, high-performance web applications just fine. The real trick is matching your choice to actual project requirements—not what sounds trendy right now.&lt;/p&gt;

&lt;p&gt;Think about your team's existing skills. Consider your timeline. Be realistic about complexity. Got Angular experts on staff? Teaching them React wastes time. Same applies in reverse.&lt;br&gt;
Still feeling stuck about which stack fits your specific situation? Sometimes talking through options with experienced full-stack developers saves months of frustration. Innostax works with both MERN and MEAN regularly and can help you figure out what actually makes sense for your needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;About Innostax&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Innostax provides businesses with outsourced resources for their software development through our services or through our staff augmentation program. We primarily serve Startup, Scaleup, and Digital Agencies; all of which require contracted development experts when they require assistance to augment their Development Team or with a custom solution to build their product and as a result do not want to incur the cost and time to set up and manage an internal Dev Team.&lt;/p&gt;

&lt;p&gt;Want to see how we deliver scalable, cost-effective Python development without the overhead of an in-house team? We've broken down the full approach—talent sourcing, project onboarding, tech stack decisions, and business outcomes—in detail on the Innostax blog.&lt;/p&gt;

&lt;p&gt;Read the full guide: &lt;a href="https://innostax.com/blog/mean-stack-and-mern-stack-whats-best-for-your-business-needs/" rel="noopener noreferrer"&gt;MERN vs MEAN: Choosing the Right Stack&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>react</category>
      <category>angular</category>
    </item>
    <item>
      <title>Web Workers Vs. Service Workers in JavaScript</title>
      <dc:creator>Sahil Khurana</dc:creator>
      <pubDate>Tue, 16 Jun 2026 10:55:42 +0000</pubDate>
      <link>https://dev.to/sahil_khurana_486f374ecf2/web-workers-vs-service-workers-in-javascript-2pg5</link>
      <guid>https://dev.to/sahil_khurana_486f374ecf2/web-workers-vs-service-workers-in-javascript-2pg5</guid>
      <description>&lt;p&gt;Experiencing an event like this is something we've all encountered. You click on something after a short wait for your web application to load, and immediately the entire page becomes unresponsive before you can even see what happened; you may want to throw your chair through your monitor at this point; your only option is to refresh (collapse) and hope it will work next time. Many times, this problem has been caused by JavaScript; it’s not that JavaScript is a “bad” programming language; it’s just that it performs too much work using a single thread; once that single thread gets overloaded, your user interface won’t perform as intended.&lt;/p&gt;

&lt;p&gt;The primary purpose of Web Workers and Service Workers are to provide practical solutions to the problems with web applications; although those are both great-sounding names, they’re, in actuality, two entirely different mechanisms designed to alleviate the issues experienced when using traditional web applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Key Takeaways&lt;/strong&gt;
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;By using Web Workers and Service Workers in your web applications, you can lower the main UI processing load and run tasks on separate threads; thus increasing the speed of your web application by offloading to other threads to reduce the time an operation requires.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Heavy calculations can be run on the server using web workers and can be done independently of the UI, while at the same time allowing for service workers, which gives users an experience of being able to access a web application without an internet connection, leading to a better experience for the user.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Through web workers and service workers being implemented in your web application, users will see improved performance with faster load times and seamless operation even with slow internet connectivity.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;However, what occurs when your UI freezes due to a JavaScript issue? In summary, your JavaScript runs in a thread, which means that there is only one thread for all tasks. And since each task is running on the same thread as all others, once something computationally extensive is executed, everything else subsequently has to wait until that task completes before the rest can move on.&lt;/p&gt;

&lt;p&gt;Simply making JavaScript run faster will not resolve this problem; rather, you need to provide an alternate source from which JavaScript can access resources so that it does not have to complete all computing operations in parallel on one thread&lt;br&gt;
For this purpose, web workers allow you to create a secondary thread for performing the more complicated work and thus keep your main thread free to operate in real-time; however, service workers do not perform any computation and instead act as intermediaries between your application and any network resource determining whether to issue requests out to the internet or to return the cached result from previous requests.&lt;/p&gt;

&lt;p&gt;These two types of workers each serve a purpose outside of the other; therefore, let’s examine each of them separately and in greater depth.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;What are Web Workers?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Think of it like this: your JavaScript app is like a restaurant that has one person cooking. This person has to do everything. They take orders they prepare the food, and cook it. They serve it. When the restaurant gets really busy, everything slows down.&lt;/p&gt;

&lt;p&gt;If you hire another person to help with the cooking, that is of like what a Web Worker does. This new person helps with the work in the kitchen so the other person can keep serving the customers without any problems.&lt;/p&gt;

&lt;p&gt;When we talk about code, a Web Worker is like a person who works on a task. You give them something to do, and they do it on their own. When they are done, they send you the answer. Your main Web Workers app never even knows that the Web Worker was busy doing something.&lt;/p&gt;
&lt;h2&gt;
  
  
  How Web Workers Work:
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Creating a Worker&lt;/strong&gt;: You just create a Worker object and point it at your worker script file. One line and you're off.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Communication&lt;/strong&gt;: Workers and the main thread can't share variables directly - instead they pass messages back and forth using postMessage() and onmessage. It's a bit like sending notes through a door.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Limitations&lt;/strong&gt;: Here's the catch - Web Workers have zero access to the DOM. They can't read or update anything on the page. Their entire job is computation, and that's it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// main.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;worker.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello, worker!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Received from worker:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// worker.js&lt;/span&gt;
&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Received from main thread:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello, main thread!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What are Service Workers?
&lt;/h2&gt;

&lt;p&gt;Service Workers do something completely different. Instead of helping with processing, they intercept network requests. Every single time your app tries to fetch something from the internet, a Service Worker can step in and say - "I've got this. No need to hit the server."&lt;/p&gt;

&lt;p&gt;That's how apps load in under a second even on a slow connection. That's how some web apps work when you're completely offline. The Service Worker cached the right resources ahead of time, and it's serving them locally.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Service Workers Work:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Installation&lt;/strong&gt;: This occurs when the Service Worker is first created and implemented into the browser environment.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Activation&lt;/strong&gt;: Once the Service Worker has been created, it will be activated after the Install event is fired off.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fetch Event&lt;/strong&gt;: Each time an application requests any web page, it fires off a Fetch event, which allows you to determine how you will serve that request from either your cache or the network, depending on whether you cached the page previously.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lifetime&lt;/strong&gt;: The lifetime of your Service Worker will not end when you close the browser window. The Service Worker will remain in the background waiting for the next time you use the application to intercept requests.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why Use Web Workers?
&lt;/h2&gt;

&lt;p&gt;If any part of your app does something computationally expensive — think image processing, parsing a huge JSON file, running a real-time algorithm — you should seriously consider moving that work to a Web Worker.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// service-worker.js&lt;/span&gt;
&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;install&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;caches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;v1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addAll&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/styles.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/script.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fetch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respondWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;caches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Advantages of Web Workers&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;When your application is dealing with a long-running computational process such as manipulating an image, parsing a large JSON file, or running a real-time algorithm, consider the idea of processing this work inside a Web Worker:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Boost Performance&lt;/strong&gt;: The main thread is not busy with expensive operations any longer. While your application is still usable, the heavy lifting takes place in the backend.&lt;br&gt;
Keep Your Users Interacting: Users don't need to care about the computationally intensive operation and can continue scrolling, clicking, etc.. Normally.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Keep Your Users Interacting&lt;/strong&gt;: Users don't need to care about the computationally intensive operation and can continue scrolling, clicking, etc.. Normally.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Run in Parallel&lt;/strong&gt;: You can have as many web workers as you need running on the same instance, doing various kinds of tasks. In fact, this is the closest JavaScript gets to true multithreading.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;**&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Use Service Workers?
&lt;/h2&gt;

&lt;p&gt;**&lt;/p&gt;

&lt;p&gt;If you want your app to feel fast - not just be fast, but actually feel fast - Service Workers are how you get there.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Offline Capability&lt;/strong&gt;: Your app doesn't die when the internet drops. It serves cached content and keeps working, which is huge for users on spotty connections.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Improve Performance&lt;/strong&gt;: Pulling from cache is nearly instant. No DNS lookup, no server round-trip, no waiting. Users notice the difference immediately.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Background Sync&lt;/strong&gt;: If a user submits a form while offline, a Service Worker can hold onto that data and sync it the moment connectivity comes back. No lost work, no frustrating error messages.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How to Implement Web Workers and Service Workers?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Implementing Web Workers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It’s simpler than most people expect. Create a separate JS file for your worker logic, then initialize it from your main script:&lt;/p&gt;

&lt;p&gt;Creating a Worker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;worker.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Communication:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Message to worker&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Message from worker:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Implementing Service Workers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One important habit before you register: always check if the browser actually supports Service Workers first. It saves a lot of headaches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Registering a Service Worker&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;serviceWorker&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
&lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serviceWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/service-worker.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;registration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Service Worker registered with scope:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;registration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Handling Events:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// service-worker.js&lt;/span&gt;
&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;install&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="c1"&gt;// Perform installation steps&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fetch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="c1"&gt;// Handle fetch events&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;As soon as you’ve learned about Web Workers and Service Workers and what they actually do, you won’t be able to look at them any other way. One worker, Web Worker, allows you to run your web-app from within something like a browser, with no chance of freezing your app when there is a lot of activity happening in there. The other (Service Worker) allows your users to receive fast and dependable service, no matter what type of connection they are on.&lt;/p&gt;

&lt;p&gt;Both have their own respective problems to solve, but when you put the two together, they solve a lot of issues that might be causing your users to experience poor service. The setup is very easy to do with only event listeners to register, some minor setup scripts, and you’ll have a much more robust experience for your end users.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;About Innostax&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Innostax&lt;/strong&gt; is a custom software development and IT staff augmentation company. We deliver outsourced Python development services, build custom software solutions, and provide managed engineering teams for startups, scale-ups, and digital agencies — typically when businesses need skilled Python expertise on demand, without the time and cost of building and maintaining an in-house development team.&lt;/p&gt;

&lt;p&gt;Want to see how we deliver scalable, cost-effective Python development without the overhead of an in-house team? We’ve broken down the full approach — talent sourcing, project onboarding, tech stack decisions, and business outcomes — in detail on the Innostax blog.&lt;/p&gt;

&lt;p&gt;Read the full guide: &lt;a href="https://innostax.com/blog/web-workers-vs-service-workers-in-javascript/" rel="noopener noreferrer"&gt;Web Workers Vs. Service Workers in JavaScript&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
    <item>
      <title>SQLAlchemy Essentials: A Beginner’s Roadmap</title>
      <dc:creator>Sahil Khurana</dc:creator>
      <pubDate>Mon, 15 Jun 2026 07:27:53 +0000</pubDate>
      <link>https://dev.to/sahil_khurana_486f374ecf2/sqlalchemy-essentials-a-beginners-roadmap-244n</link>
      <guid>https://dev.to/sahil_khurana_486f374ecf2/sqlalchemy-essentials-a-beginners-roadmap-244n</guid>
      <description>&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;SQLAlchemy is both an ORM and a SQL toolkit — pick one, use both, whatever works for your situation.&lt;/li&gt;
&lt;li&gt;Learn Engine, Session, and Declarative Base first. Seriously, before you write anything.&lt;/li&gt;
&lt;li&gt;Runs on PostgreSQL, MySQL, SQLite, Oracle — you can swap databases without torching the whole codebase.&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;Month two or three into a backend project is when it usually happens. Not a crash, not a catastrophic failure — just this slow creep of annoyance. The raw SQL strings you threw in early on? They start multiplying. You rename one column and suddenly something is broken in three places you forgot even existed. You spend twenty minutes ctrl+F-ing through files trying to find where you wrote that query six weeks ago.&lt;/p&gt;

&lt;p&gt;It's not a crisis. It's just painful in a boring, grinding kind of way.&lt;/p&gt;

&lt;p&gt;That's usually when people go looking for something better and end up at SQLAlchemy. It's been around since 2006. In Python terms that's ancient history — and yet it's still the default choice for serious backend work. Not because it's easy, it's not, but because it never forces your hand. You want ORM abstractions? Fine. You want to drop down and write actual SQL because the ORM is acting strange? Also fine, you can do that in the same codebase, same connection, no hacks needed. That kind of flexibility is rarer than it sounds.&lt;/p&gt;

&lt;p&gt;Here's what you actually need to get started.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Even Is SQLAlchemy?
&lt;/h2&gt;

&lt;p&gt;Honestly, it's two tools that ship as one.&lt;/p&gt;

&lt;p&gt;The first is the SQL Expression Language — SQL, but expressed as Python. You get precise query control without ever touching raw string concatenation. The second is the ORM, which sits on top and removes the database from your mental model almost entirely. Classes become tables. Objects become rows. You think in Python, SQLAlchemy handles the translation.&lt;/p&gt;

&lt;p&gt;Most people start with the ORM, fall back to the Expression Language when things get weird, and bounce between both depending on what a given query needs. Some teams skip the ORM altogether and live in the Expression Language. Totally valid either way, nobody's going to argue with you.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Bother With It at All?
&lt;/h2&gt;

&lt;p&gt;For a script or a tiny side project — genuinely, don't bother. Plain &lt;code&gt;sqlite3&lt;/code&gt; is sitting right there in the standard library and it'll do the job without any setup.&lt;/p&gt;

&lt;p&gt;But once you've got real scale, real team size, a schema that keeps shifting — the "SQL strings everywhere" approach starts falling apart. Fast. That's where SQLAlchemy starts paying rent.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What You Get&lt;/th&gt;
&lt;th&gt;Why It Matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ORM and raw SQL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You're not locked into either. Use what the situation calls for.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cross-database support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;PostgreSQL, MySQL, SQLite, Oracle — change the connection string, not the whole app.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Models as Python classes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Schema lives in code. Any teammate can read it without knowing the database.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Full query visibility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Log every query. No hidden performance problems sneaking through.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;18 years of community&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Whatever weird thing you run into, someone already has. Check the forums.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;sqlalchemy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Plus a driver for your database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;psycopg2      &lt;span class="c"&gt;# PostgreSQL&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;pymysql       &lt;span class="c"&gt;# MySQL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SQLite ships with Python so nothing extra needed there.&lt;/p&gt;




&lt;h2&gt;
  
  
  Four Things to Understand Before You Write a Single Line
&lt;/h2&gt;

&lt;p&gt;Most SQLAlchemy confusion is from jumping into code examples cold, without any mental picture of the parts involved. Read this first and a lot of the friction disappears.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Engine&lt;/strong&gt; — Your actual database connection. Manages the connection pool, sits under everything, you set it up once and mostly forget it's there. Nothing else functions without it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Session&lt;/strong&gt; — Where you do your work. Queries, inserts, updates — all go through a session. Key thing: changes are held in memory until you call &lt;code&gt;commit()&lt;/code&gt;. Nothing hits the database for real until that point. This is intentional. Means you can back out of a half-finished operation if something breaks mid-way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Declarative Base&lt;/strong&gt; — A base class. Your model classes inherit from it. It's what lets SQLAlchemy know these Python classes are supposed to map to database tables. You make it once, inherit from it everywhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Model&lt;/strong&gt; — A class that represents a table. One class, one table. One instance of that class, one row. That's the whole idea.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building Something Step by Step
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1 — Connect to a Database
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;create_engine&lt;/span&gt;

&lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sqlite:///example.db&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Production would look more like &lt;code&gt;postgresql://user:pass@localhost/mydb&lt;/code&gt; — same pattern. SQLite is just the easiest starting point locally since there's nothing to install or configure.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 2 — Define a Model
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy.ext.declarative&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;declarative_base&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;

&lt;span class="n"&gt;Base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;declarative_base&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;__tablename__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;users&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

    &lt;span class="nb"&gt;id&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;primary_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;age&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;__tablename__&lt;/code&gt; is what the table gets called in the actual database. The attributes are columns. Read it a couple times and the pattern sticks pretty naturally.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 3 — Create the Tables
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks at everything hanging off &lt;code&gt;Base&lt;/code&gt;, runs the &lt;code&gt;CREATE TABLE&lt;/code&gt; statements for you. No hand-writing DDL.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 4 — Insert, Query, Update
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy.orm&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sessionmaker&lt;/span&gt;

&lt;span class="n"&gt;Session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sessionmaker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Add a record
&lt;/span&gt;&lt;span class="n"&gt;new_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;John Doe&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Fetch records
&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Update something
&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;John Doe&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;31&lt;/span&gt;
&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change something, commit. That's the loop. Forget the commit and the change silently disappears — you'll do this at least once and spend a baffled ten minutes before figuring out why. It happens to everyone, don't stress it.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 5 — Relationships
&lt;/h3&gt;

&lt;p&gt;Here's where it gets genuinely useful. Define a relationship once and stop writing joins every time you need linked data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ForeignKey&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy.orm&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;relationship&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;__tablename__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;posts&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

    &lt;span class="nb"&gt;id&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;primary_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;users.id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;relationship&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;back_populates&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;posts&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;relationship&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Post&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order_by&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;back_populates&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now &lt;code&gt;user.posts&lt;/code&gt; gives you the list. &lt;code&gt;post.user&lt;/code&gt; gives you the author. The join happens behind the scenes. You just access properties like it's a regular Python object, because it is.&lt;/p&gt;




&lt;h2&gt;
  
  
  SQLAlchemy vs the Other Options
&lt;/h2&gt;

&lt;p&gt;Django ORM vs SQLAlchemy is a question that comes up constantly. Here's a straight answer:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;SQLAlchemy&lt;/th&gt;
&lt;th&gt;Django ORM&lt;/th&gt;
&lt;th&gt;Peewee&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Flexibility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High — ORM and raw SQL&lt;/td&gt;
&lt;td&gt;Moderate, ORM-first design&lt;/td&gt;
&lt;td&gt;Moderate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Learning Curve&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Steep up front&lt;/td&gt;
&lt;td&gt;Gentler&lt;/td&gt;
&lt;td&gt;Easy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Query Control&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Full&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best Fit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Standalone backends, complex apps&lt;/td&gt;
&lt;td&gt;Django projects&lt;/td&gt;
&lt;td&gt;Scripts, small projects&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you're in a Django project, just use Django ORM — no argument there, it integrates perfectly and fighting it makes no sense. Peewee is a solid pick for smaller stuff, fast to get going. But outside Django, or whenever query complexity is a real concern, SQLAlchemy is the one that ages well. It's the tool you won't find yourself trying to route around two years in.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where to Go From Here
&lt;/h2&gt;

&lt;p&gt;The complexity reputation SQLAlchemy has is real but exaggerated. Most projects use a narrow slice of it — models, sessions, queries, relationships. That slice is well-built and once it clicks, it stays clicked.&lt;/p&gt;

&lt;p&gt;Start with the ORM. Get something working. When the ORM starts making a particular query harder than it should be — and that will happen eventually — reach for the Expression Language and write the SQL directly. That's the actual workflow, not a theoretical fallback. Experienced people do exactly this, switching between layers depending on what the problem needs.&lt;/p&gt;

&lt;p&gt;Runs on a local SQLite file on your machine. Runs on a Postgres cluster with serious traffic. The mental model you build in the first week transfers cleanly years later. That doesn't happen by accident, and it's one of the main reasons the library has lasted this long.&lt;/p&gt;




&lt;p&gt;At &lt;a href="https://innostax.com/" rel="noopener noreferrer"&gt;Innostax&lt;/a&gt;, we work through this kind of thing regularly — picking the right database layer, structuring backends that stay maintainable, building data models that hold up under pressure. If you're working through the same questions for your own project, reach out at &lt;a href="https://innostax.com/contact-us/" rel="noopener noreferrer"&gt;innostax.com/contact&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://innostax.com/blog/sqlalchemy-essentials-a-beginners-roadmap/" rel="noopener noreferrer"&gt;Innostax Engineering Blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>sql</category>
      <category>softwaredevelopment</category>
      <category>programming</category>
    </item>
    <item>
      <title>Is Outsourcing ASP.NET the Smarter Route for Your Next Web Project?</title>
      <dc:creator>Sahil Khurana</dc:creator>
      <pubDate>Fri, 12 Jun 2026 09:36:21 +0000</pubDate>
      <link>https://dev.to/sahil_khurana_486f374ecf2/is-outsourcing-aspnet-the-smarter-route-for-your-next-web-project-476h</link>
      <guid>https://dev.to/sahil_khurana_486f374ecf2/is-outsourcing-aspnet-the-smarter-route-for-your-next-web-project-476h</guid>
      <description>&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Performance That Holds Under Pressure&lt;/strong&gt; — ASP.NET Core doesn't start struggling when traffic spikes. That's the point of it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security Baked In, Not Bolted On&lt;/strong&gt; — The defaults cover authentication, authorization, data protection. You're not starting from zero.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tooling That's Already Figured Out&lt;/strong&gt; — Visual Studio, a mature ecosystem, years of community problem-solving. The sharp edges are mostly gone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Outsourcing Buys Expertise, Not Just Headcount&lt;/strong&gt; — The real value is a team that's already made the mistakes your project hasn't made yet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runs Anywhere&lt;/strong&gt; — Linux, any cloud, no infrastructure lock-in. That flexibility compounds over time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modern Web Features Without the Setup Tax&lt;/strong&gt; — SignalR, WebSockets, clean routing. Already there.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Works for New Builds and Legacy Fixes Alike&lt;/strong&gt; — That combination is rarer than the framework marketing makes it seem.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Nobody's Asking the Right Question
&lt;/h2&gt;

&lt;p&gt;Here's what usually happens.&lt;/p&gt;

&lt;p&gt;A company needs to build something serious on ASP.NET — or modernize something that's been slowly falling apart for years. Someone raises the outsourcing question. And immediately, the conversation turns into a debate about rates, time zones, and that one bad experience from 2019 that somebody in the room won't let go of.&lt;/p&gt;

&lt;p&gt;The meeting ends. Either nothing gets decided, or something gets decided for the wrong reasons.&lt;/p&gt;

&lt;p&gt;Meanwhile, the question that actually determines whether outsourcing makes sense — does your current team, with the time and capacity they actually have, have the depth to pull this off well — goes completely unasked.&lt;/p&gt;

&lt;p&gt;I've watched this happen a lot. It's almost always the wrong conversation happening instead of the right one.&lt;/p&gt;

&lt;h2&gt;
  
  
  ASP.NET Core: What It Actually Does Well
&lt;/h2&gt;

&lt;p&gt;I'm not going to run through a feature list. You can find those anywhere.&lt;/p&gt;

&lt;p&gt;What's worth saying is that ASP.NET Core is a framework that was built for the uncomfortable end of the requirements spectrum. High traffic. Real security obligations. Applications that need to be running in three years, not rebuilt. The modular architecture means you're not carrying dead weight. The open-source model with Microsoft backing means it's not going anywhere. The performance under load is the reason it keeps showing up in enterprise infrastructure that can't afford to be slow.&lt;/p&gt;

&lt;p&gt;The security piece deserves more credit than it usually gets. Authentication, authorization, data protection — these aren't add-ons. They're defaults. For anything operating under compliance requirements, that's not a nice-to-have. It's the difference between a framework that handles those requirements and one that leaves them as an exercise for the developer.&lt;/p&gt;

&lt;p&gt;The toolchain is mature in a way that genuinely saves time. Not "mature" as a euphemism for old. Mature as in: most of the problems you'll hit have already been hit, solved, documented, and turned into Stack Overflow answers.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You're Actually Getting When You Outsource This
&lt;/h2&gt;

&lt;p&gt;Let me be blunt about something.&lt;/p&gt;

&lt;p&gt;"Outsourcing" as a word carries a lot of baggage that doesn't apply here. The value isn't cheaper labor. Anyone still selling outsourcing purely on rate arbitrage is selling the wrong thing.&lt;/p&gt;

&lt;p&gt;The actual value is this: ASP.NET Core expertise at an enterprise level takes years to develop. Not months. Years of working through real production failures, catching security misconfigurations before they become incidents, understanding which performance patterns hold up at scale and which ones only look good in development. When you bring in a team that already has that, you're not buying their hours. You're buying the years of context behind them.&lt;/p&gt;

&lt;p&gt;That context shows up in ways that are hard to put in a proposal but very easy to feel mid-project. Decisions that get made right the first time. Patterns that don't need to be refactored six weeks later. A codebase that doesn't require emergency cleanup before it can go live.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Speed compounds differently with experienced teams.&lt;/strong&gt; They're not learning the framework while trying to ship. They've already hit the walls your project is going to hit. That's not a small thing — it's often the difference between a deadline that holds and one that doesn't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scaling without the hiring circus.&lt;/strong&gt; Building an internal team for a project-specific need means recruiting, onboarding, and eventually having an uncomfortable conversation about what those people do when the project wraps. Outsourcing skips all of that. The team scales with the work, not the other way around.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An outside view that your internal team structurally cannot have.&lt;/strong&gt; This one sounds soft but it isn't. Teams that have been working on the same problems in the same codebase for a long time develop patterns — good ones and bad ones — and stop being able to see either clearly. External teams bring different defaults, different instincts, exposure to a wider range of failure modes. That perspective is genuinely useful and genuinely unavailable from inside the building.&lt;/p&gt;

&lt;h2&gt;
  
  
  When This Framework Is Actually the Right Call
&lt;/h2&gt;

&lt;p&gt;Not every project needs ASP.NET Core. Worth saying that plainly.&lt;/p&gt;

&lt;p&gt;If the application needs to handle real traffic from day one, carry meaningful security requirements, and not require a rethink of its architecture in eighteen months — this is probably the right framework. It was built for exactly that combination.&lt;/p&gt;

&lt;p&gt;If you're dealing with a legacy .NET system that's become a liability — technical debt that's starting to affect delivery speed, or an architecture that doesn't match how the business works anymore — ASP.NET Core gives you a migration path that doesn't require burning the whole thing down. That matters when there's years of business logic embedded in a stack you can't simply walk away from.&lt;/p&gt;

&lt;p&gt;If someone in the room is pushing for Django, Laravel, or Rails instead — those frameworks are fine for the right project. Smaller scope, lower traffic expectations, less demanding security requirements. When those conditions apply, they're legitimate choices. When they don't, ASP.NET Core is usually the more defensible decision and easier to justify twelve months later.&lt;/p&gt;

&lt;p&gt;The wrong reason to land on ASP.NET Core: familiarity. "We used it last time" isn't a technical argument. It's a comfort argument. Sometimes they overlap. Often they don't.&lt;/p&gt;

&lt;h2&gt;
  
  
  So Is Outsourcing the Smarter Route?
&lt;/h2&gt;

&lt;p&gt;For some projects, yes. Clearly.&lt;/p&gt;

&lt;p&gt;Specifically: when the project needs the framework, the timeline doesn't allow for internal team ramp-up, and the cost of getting it wrong is higher than the cost of bringing in people who've already gotten it right.&lt;/p&gt;

&lt;p&gt;In that situation, outsourcing isn't a compromise. It's the faster, lower-risk path. The partner you bring in starts contributing on day one instead of week six. The decisions they make are informed by experience your team hasn't had yet. The codebase they build doesn't immediately create new problems for whoever maintains it.&lt;/p&gt;

&lt;p&gt;The organizations that use ASP.NET Core well — whether they build that capability internally over time or bring it in for specific projects — consistently ship better outcomes than the ones discovering the framework's edges mid-delivery.&lt;/p&gt;

&lt;p&gt;The question was never "is outsourcing smart in general." That's not a useful question. The useful question is whether it's the right call for this project, with this team, at this moment. Get that answer right and everything downstream gets easier.&lt;/p&gt;




&lt;p&gt;At &lt;a href="https://innostax.com/" rel="noopener noreferrer"&gt;Innostax&lt;/a&gt;, we build with both — the choice comes from what the project actually needs, not what we happen to prefer. If you're trying to figure out whether outsourcing your ASP.NET development makes sense for what you're building, &lt;a href="https://innostax.com/contact" rel="noopener noreferrer"&gt;innostax.com/contact&lt;/a&gt; is the right place to start.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://innostax.com/blog/is-outsourcing-asp-net-the-smarter-route-for-your-next-web-project/" rel="noopener noreferrer"&gt;Innostax Engineering Blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aspnet</category>
      <category>djangocms</category>
      <category>devops</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>AngularJS: Why Businesses and Developers Still Rely on It</title>
      <dc:creator>Sahil Khurana</dc:creator>
      <pubDate>Fri, 12 Jun 2026 06:05:41 +0000</pubDate>
      <link>https://dev.to/sahil_khurana_486f374ecf2/angularjs-why-businesses-and-developers-still-rely-on-it-3a1f</link>
      <guid>https://dev.to/sahil_khurana_486f374ecf2/angularjs-why-businesses-and-developers-still-rely-on-it-3a1f</guid>
      <description>&lt;p&gt;A lot of developers I've spoken to treat AngularJS like a legacy decision. Something you maintain, not something you start with. But then you look at the actual list of products built on it — Gmail, PayPal, Upwork, YouTube — and that assumption starts to crack.&lt;/p&gt;

&lt;p&gt;AngularJS launched in 2010. Google built it. Over a million websites run on it. That's not a framework on life support. That's one that earned its place by solving real problems in ways people kept coming back to.&lt;/p&gt;

&lt;p&gt;So what does it actually do well? And why do teams still pick it when starting something new?&lt;/p&gt;




&lt;h2&gt;
  
  
  What Do People Actually Build With AngularJS?
&lt;/h2&gt;

&lt;p&gt;The range is wider than most people expect.&lt;/p&gt;

&lt;h3&gt;
  
  
  Single Page Applications
&lt;/h3&gt;

&lt;p&gt;Gmail. Trello. A single page application rewrites itself dynamically as you interact — no full page reloads, no waiting. The experience feels faster because the browser isn't doing full page requests on every click.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enterprise Web Applications
&lt;/h3&gt;

&lt;p&gt;Internal tools at scale. Data management, HR systems, supply chain visibility. Several Google and Microsoft products that companies rely on daily were built using AngularJS. Not a framework you'd pick if it couldn't handle complexity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dynamic Websites
&lt;/h3&gt;

&lt;p&gt;Upwork and Forbes both run on it. A dynamic website isn't just displaying fixed content — it's pulling and updating data based on who you are, what you're doing, when you're doing it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dynamic Web Applications
&lt;/h3&gt;

&lt;p&gt;PayPal. That one tends to surprise people. A dynamic web app responds to your actions in the browser without reloading the full page. The fact that PayPal handles transactions this way tells you something about how the framework performs under pressure.&lt;/p&gt;

&lt;h3&gt;
  
  
  E-commerce Platforms
&lt;/h3&gt;

&lt;p&gt;Shift4Shop and AngularSpree are both built on it. AngularJS handles product browsing, cart updates, and checkout flows without breaking a sweat.&lt;/p&gt;

&lt;h3&gt;
  
  
  Content Management Systems
&lt;/h3&gt;

&lt;p&gt;Taiga and Angular CMS. A CMS is multiple people touching the same content at the same time without stepping on each other. AngularJS handles that kind of concurrent state management well.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dashboards
&lt;/h3&gt;

&lt;p&gt;ArchitectUI, SB Admin Angular, Monarch. A good dashboard makes a lot of information readable at once, and it updates as the data changes without manual pushing. The framework's data binding is well-suited to this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Social Media and Chat Applications
&lt;/h3&gt;

&lt;p&gt;YouTube is the headline. Sport-Hub and VibeVortex too. Real-time feeds, comment threads, live interactions — exactly the kind of work AngularJS was built for.&lt;/p&gt;

&lt;h3&gt;
  
  
  Progressive Web Applications
&lt;/h3&gt;

&lt;p&gt;Starbucks and Twitter went this route. A PWA can be installed on a device or run in a browser. AngularJS handles this without needing a completely separate mobile build.&lt;/p&gt;

&lt;h3&gt;
  
  
  Real-time Applications
&lt;/h3&gt;

&lt;p&gt;Weather.com. Data that updates constantly, served to millions of users. AngularJS handles the update cycle without extra plumbing required.&lt;/p&gt;




&lt;h2&gt;
  
  
  10 Reasons Developers Choose AngularJS for Web Development
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. MVC Architecture: Structure Before You Need It
&lt;/h3&gt;

&lt;p&gt;Anyone who's worked on a frontend codebase that grew without structure knows what happens. Logic scattered everywhere. No clear ownership. Bugs that are nearly impossible to trace.&lt;/p&gt;

&lt;p&gt;MVC fixes that before it starts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model&lt;/strong&gt; handles the data — fetches it, organizes it, holds it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;View&lt;/strong&gt; shows the data. That's it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Controller&lt;/strong&gt; sits between them — processes user input, talks to the Model, delivers output through the View.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Three responsibilities. Three places to look when something breaks. For anything you plan to maintain for more than six months, this matters.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Two-Way Data Binding: You Feel It Immediately
&lt;/h3&gt;

&lt;p&gt;You type in a field — the model updates. The model updates — the UI reflects it. No wiring required. No manual sync.&lt;/p&gt;

&lt;p&gt;(Once you've built with two-way binding, going back to managing DOM updates manually is genuinely unpleasant in a way nobody prepares you for.)&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Dependency Injection: Your Tests Will Thank You
&lt;/h3&gt;

&lt;p&gt;Instead of hardcoding dependencies inside your components, you declare what each piece needs and AngularJS provides it. The practical payoff is in testing — you can swap a real service for a mock one without rewriting anything. Tests stay fast and isolated. For teams that actually run test suites regularly, this matters more than almost anything else on this list.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Reusable Components and Directives
&lt;/h3&gt;

&lt;p&gt;Write it once, use it wherever you need it.&lt;/p&gt;

&lt;p&gt;Same dropdown logic in three places? Same date-picker behavior across multiple forms? Build it once as a custom directive or component, reference it everywhere. Less duplication, fewer places for bugs to hide, and when requirements change, you update one thing — not six.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Single Page App Support That Holds Up
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://innostax.com/technology/angular-js-development/" rel="noopener noreferrer"&gt;AngularJS for single page app development&lt;/a&gt; gives you fast load times and navigation that feels immediate. The application only re-renders what changed — not the whole page.&lt;/p&gt;

&lt;p&gt;For products where speed affects whether users stick around, this is worth caring about.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Karma and Jasmine Come With It
&lt;/h3&gt;

&lt;p&gt;AngularJS ships with testing tools built in — Karma to run them, Jasmine to write them. Unit tests and end-to-end tests. No separate configuration project just to get your test suite off the ground.&lt;/p&gt;

&lt;p&gt;The error messages point somewhere useful too. That might sound like a low bar, but spend a few hours chasing an unhelpful stack trace somewhere else and you'll appreciate it.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. HTML Templates That Stay Readable
&lt;/h3&gt;

&lt;p&gt;Dynamic directives let you put loops, conditions, and event bindings directly in the template. Not scattered across a collection of JavaScript files someone has to mentally map back to what's on screen.&lt;/p&gt;

&lt;p&gt;New people on the team can look at a template and understand what it does. That's worth more than it sounds.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. Form Validation Is Already There
&lt;/h3&gt;

&lt;p&gt;Required fields, format rules, custom validators — AngularJS has this built in. You're configuring it, not building it. On anything with heavy form work — registration, checkout, multi-step flows — this saves actual time.&lt;/p&gt;

&lt;h3&gt;
  
  
  9. Same Codebase for Mobile and Desktop
&lt;/h3&gt;

&lt;p&gt;Cross-platform development means one codebase, not two parallel ones. Same logic, same components, consistent behavior across devices. Less to maintain when requirements shift.&lt;/p&gt;

&lt;h3&gt;
  
  
  10. The Community Hasn't Gone Anywhere
&lt;/h3&gt;

&lt;p&gt;Open-source, Google-maintained, running on over a million sites. The Stack Overflow threads, GitHub repos, libraries, and tutorials are all still there. When you hit something unusual, the odds are good someone already solved it and wrote it up.&lt;/p&gt;




&lt;h2&gt;
  
  
  Should You Still Use AngularJS in 2026?
&lt;/h2&gt;

&lt;p&gt;If you're on an existing AngularJS codebase — there's rarely a good reason to rip it out. It's stable, well-documented, Google-backed, and still running in production at massive scale.&lt;/p&gt;

&lt;p&gt;If you're starting something fresh — give it a real evaluation. Not the dismissive "isn't that old?" kind. An actual one. For teams that want structure from day one, testable code without a long setup process, and a framework that's been proven at scale, AngularJS still competes.&lt;/p&gt;

&lt;p&gt;The case for it isn't nostalgia. A framework powering Gmail, PayPal, YouTube, and Upwork earned its spot on that list.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Using AngularJS in production right now? Curious what the use case is — drop it in the comments. And if you've migrated away, what pushed you to do it? 👇&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;#javascript #webdev #frontend #angular&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Read more — &lt;a href="https://innostax.com/blog/10-benefits-of-using-angularjs-for-web-development/" rel="noopener noreferrer"&gt;AngularJS For Web Development: 10 Key Benefits Explained&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Sahil Khurana&lt;br&gt;
Chief Technology Officer at &lt;a href="https://innostax.com" rel="noopener noreferrer"&gt;Innostax&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;&lt;a href="https://innostax.com" rel="noopener noreferrer"&gt;About Innostax&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://innostax.com" rel="noopener noreferrer"&gt;Innostax&lt;/a&gt; is a global software consulting and custom software development company helping growth-stage startups, scaleups, and enterprises build reliable, scalable digital products. Founded in 2014 and headquartered in Framingham, Massachusetts, Innostax specializes in custom software development, web and mobile app development, IT staff augmentation, offshore software development, and digital transformation services — across industries including healthcare, retail, education, travel, and fintech. With a dedicated development team model, a 2-week risk-free trial, and deep expertise in technologies like React.js, Node.js, Python, .NET, and React Native, Innostax co-creates breakthrough solutions that help founders, CTOs, and product leaders ship better software, faster. Learn more at &lt;a href="https://innostax.com" rel="noopener noreferrer"&gt;innostax.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>frontend</category>
      <category>angular</category>
    </item>
    <item>
      <title>Django vs. Flask: Choosing the Right Python Framework for Your Business</title>
      <dc:creator>Sahil Khurana</dc:creator>
      <pubDate>Wed, 10 Jun 2026 09:17:38 +0000</pubDate>
      <link>https://dev.to/sahil_khurana_486f374ecf2/django-vs-flask-choosing-the-right-python-framework-for-your-business-2gaj</link>
      <guid>https://dev.to/sahil_khurana_486f374ecf2/django-vs-flask-choosing-the-right-python-framework-for-your-business-2gaj</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;The real question isn't which framework is better. It's which one you can stop thinking about six months into the project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Project Suitability&lt;/strong&gt; — Django is built for weight. Flask is built for speed. Know which one your project actually needs before you commit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Development Flexibility&lt;/strong&gt; — Django makes decisions so your team doesn't have to. Flask hands those decisions back. Both are features, depending on who's writing the code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability &amp;amp; Performance&lt;/strong&gt; — Scaling is an architecture problem first, a framework problem second. Pick the one that matches the system you're building — not the one you hope to build.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security Features&lt;/strong&gt; — Django's protections are on by default. Flask's require you to turn them on. In a fast-moving team, that difference is more significant than it sounds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ecosystem &amp;amp; Community&lt;/strong&gt; — Both communities are active and well-documented. You won't be stuck either way.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  The Decision Nobody Takes Seriously Enough
&lt;/h2&gt;

&lt;p&gt;I've watched this play out more times than I'd like to count.&lt;/p&gt;

&lt;p&gt;A team kicks off a Python project, someone picks a framework — usually the one the most senior person knows best — and everyone moves on. Fast forward six months and the codebase is exhausting to work in. Either they're dragging a full framework through a service that should've been twenty lines of Flask, or they're rebuilding authentication from scratch on something that outgrew its lightweight origins two sprints in.&lt;/p&gt;

&lt;p&gt;The framework choice isn't irreversible. But undoing it mid-project is expensive in a way that doesn't show up in any estimate.&lt;/p&gt;

&lt;p&gt;Django and Flask are both genuinely good. What they're good &lt;em&gt;for&lt;/em&gt; is different. That's the part worth slowing down on.&lt;/p&gt;




&lt;h2&gt;
  
  
  What You're Actually Getting With Each One
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Django&lt;/strong&gt; arrives with almost everything a web application needs already assembled — an ORM, an admin panel, authentication, form handling, CSRF protection, and more. The design assumption is that most web applications need most of these things, so it makes more sense to ship them integrated than to have every team assemble them independently. For the right project, that assumption holds up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flask&lt;/strong&gt; gives you a core and a blank canvas. Routing, request handling, templates — that's your starting point. Database access, authentication, validation, all of it comes from libraries you select and wire together yourself. That's not a weakness. It's the point. Flask is designed for teams that know exactly what they want and don't want a framework guessing on their behalf.&lt;/p&gt;

&lt;p&gt;The honest framing: Django makes the common path fast. Flask makes the custom path clean. Neither is universally better. They're different bets.&lt;/p&gt;




&lt;h2&gt;
  
  
  Project Size and Complexity
&lt;/h2&gt;

&lt;p&gt;Django is where it makes sense when the application is genuinely large — e-commerce platforms, internal enterprise tools, anything carrying a complex data model and real user management requirements. The built-in admin panel alone has saved weeks of development time on projects that needed internal dashboards but didn't want to build one. Instagram runs on Django. Pinterest too. That's not a marketing point, it's a load reference.&lt;/p&gt;

&lt;p&gt;Flask is the right call when the scope stays narrow. A microservice that does one thing well. A REST API where you want full control over what's happening. An MVP that needs to exist in two weeks without inheriting a framework's worth of structure it doesn't need yet.&lt;/p&gt;

&lt;p&gt;The failure mode I've seen most: picking Flask because it feels lighter, then six months later manually rebuilding the scaffolding Django would have provided on day one. If the roadmap points toward complexity, start with the framework that handles it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Development Speed and Flexibility
&lt;/h2&gt;

&lt;p&gt;Django's conventions are the speed. When your project matches what Django expects — which is most standard web applications — the framework carries a lot of the load. You configure rather than construct. The patterns for authentication, the ORM, the admin, the request lifecycle — they're settled. Your team ships features instead of infrastructure.&lt;/p&gt;

&lt;p&gt;Flask's freedom is the speed, but only for the right team. Developers who have a clear architectural vision and the experience to execute it move faster when nothing is in the way. The risk is that Flask's openness creates decisions, and decisions create divergence, and divergence in a codebase creates the kind of technical debt that's slow to pay back. Junior teams and Flask is a combination that requires strong leadership to go well.&lt;/p&gt;




&lt;h2&gt;
  
  
  Scalability and Performance
&lt;/h2&gt;

&lt;p&gt;Both frameworks scale. The more useful question is how the scaling actually works.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Django&lt;/strong&gt; is well-suited to monolithic architectures under significant traffic. One application, many features, high load. It's been stress-tested in exactly those environments and holds up.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flask&lt;/strong&gt; is well-suited to distributed systems where each service is small and independently deployable. In a microservices architecture, Flask's footprint is an advantage — you're not carrying Django's full weight in a service that handles one API endpoint.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One thing worth being direct about: Flask doesn't inherently perform better than Django. Raw performance is an infrastructure and query question far more than a framework question. Pick the architecture that fits your system, then pick the framework that fits the architecture.&lt;/p&gt;




&lt;h2&gt;
  
  
  Security
&lt;/h2&gt;

&lt;p&gt;This one matters more in practice than most framework comparisons acknowledge.&lt;/p&gt;

&lt;p&gt;Django ships with protection against XSS, CSRF, and SQL injection turned on by default. You don't opt into them — you'd have to opt out. For any application handling sensitive user data, that's meaningful. The framework enforces best practices whether or not your team is thinking about them on a given sprint.&lt;/p&gt;

&lt;p&gt;Flask can match Django's security posture. But it requires deliberate configuration of each protection, and it requires someone on the team to know what needs configuring. In a well-resourced team with security-aware developers, that's manageable. In a startup moving fast with shifting priorities, it's the kind of thing that slips until it matters. Django removes that risk by design.&lt;/p&gt;




&lt;h2&gt;
  
  
  Ecosystem and Community
&lt;/h2&gt;

&lt;p&gt;Both frameworks have large, active communities and extensive documentation. Neither is going to leave your team stuck without library support or community knowledge.&lt;/p&gt;

&lt;p&gt;Django's ecosystem is broader and more integrated — third-party packages are generally designed to plug into Django's architecture specifically, which means less wiring. Flask's extension ecosystem is healthy and growing, with the practical advantage that you install exactly what the project needs and nothing more.&lt;/p&gt;

&lt;p&gt;For most teams, ecosystem isn't the deciding factor here. Both have what you need.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Actually Decide
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Django is probably the right call when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The application has real complexity — multiple entities, user management, evolving business logic&lt;/li&gt;
&lt;li&gt;The team isn't made up of senior engineers who thrive on architectural freedom&lt;/li&gt;
&lt;li&gt;Requirements will keep changing and the schema will keep evolving&lt;/li&gt;
&lt;li&gt;You need an internal admin and you don't want to build one&lt;/li&gt;
&lt;li&gt;Security coverage from the framework is worth more than configuration control&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Flask is probably the right call when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're building a microservice, a lightweight API, or a narrowly scoped MVP&lt;/li&gt;
&lt;li&gt;The team is experienced and has a clear picture of what the architecture should look like&lt;/li&gt;
&lt;li&gt;The system is distributed and each service has one job&lt;/li&gt;
&lt;li&gt;You want full control over every library and pattern in the stack&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;One note: these aren't interchangeable within the same project the way some tooling decisions are. Pick based on the architecture you're committing to — not the one that feels more familiar.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;Django and Flask have each been solving real problems in production for years. The ecosystems are mature. The communities are active. Neither choice is a risk in isolation.&lt;/p&gt;

&lt;p&gt;The risk is picking one without thinking clearly about the other. Django in a project that needed Flask's simplicity means overhead you'll feel every day. Flask in a project that needed Django's structure means scaffolding you'll rebuild piece by piece.&lt;/p&gt;

&lt;p&gt;Answer the architecture question first. The framework choice follows from it — and when you get it right, you stop noticing the framework entirely.&lt;/p&gt;




&lt;p&gt;At &lt;a href="https://innostax.com/" rel="noopener noreferrer"&gt;Innostax&lt;/a&gt;, we build with both — the choice comes from what the project actually needs, not what we happen to prefer. If you're figuring out the right stack for something you're building, &lt;a href="https://innostax.com/contact" rel="noopener noreferrer"&gt;innostax.com/contact&lt;/a&gt; is the right place to start that conversation.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://innostax.com/blog/django-vs-flask-choosing-the-right-python-framework-for-your-business/" rel="noopener noreferrer"&gt;Innostax Engineering Blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>flask</category>
      <category>django</category>
      <category>development</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Entity Framework vs. Dapper: Choosing the Right ORM for .NET</title>
      <dc:creator>Sahil Khurana</dc:creator>
      <pubDate>Tue, 09 Jun 2026 07:42:08 +0000</pubDate>
      <link>https://dev.to/sahil_khurana_486f374ecf2/entity-framework-vs-dapper-choosing-the-right-orm-for-net-47cc</link>
      <guid>https://dev.to/sahil_khurana_486f374ecf2/entity-framework-vs-dapper-choosing-the-right-orm-for-net-47cc</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;It's not about which ORM is better. It's about which one stops getting in your way six months into the project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Tool Fit Over Familiarity&lt;/strong&gt; — The wrong ORM for your project isn't a code problem. It's a decision problem that shows up in production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EF for Complexity&lt;/strong&gt; — Entity Framework earns its place when the data model is large, the schema keeps evolving, and developer speed matters more than raw query performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dapper for Performance&lt;/strong&gt; — Dapper is the right call when latency is a product constraint and your team is comfortable owning the SQL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Abstractions Have a Cost&lt;/strong&gt; — Every layer EF adds between your code and the database is useful — until it isn't. Know when that line gets crossed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Using Both Is a Valid Answer&lt;/strong&gt; — EF for complex business logic, Dapper for performance-critical reads. Not the cleanest architecture, but often the honest one.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  The Decision Nobody Gets Right on the First Try
&lt;/h2&gt;

&lt;p&gt;I'll be honest — this is a debate I've watched teams get wrong more often than I'd like to admit. Including teams I've been part of.&lt;/p&gt;

&lt;p&gt;The conversation usually goes something like this: someone picks whichever ORM they're most comfortable with, ships the project, and six months later they're either wrestling with inexplicable query slowdowns or drowning in handwritten SQL that nobody wants to touch. The tool wasn't wrong. The fit was.&lt;/p&gt;

&lt;p&gt;So let me skip the feature matrix and talk about what actually matters when you're making this call.&lt;/p&gt;




&lt;h2&gt;
  
  
  ORMs Exist Because SQL Gets Tedious. Fast.
&lt;/h2&gt;

&lt;p&gt;If you've ever written the same CRUD logic fifteen times across fifteen different tables, you understand why ORMs exist. They sit between your application and your database and handle the repetitive parts — connections, mappings, relationships — so your team can focus on what the application actually does.&lt;/p&gt;

&lt;p&gt;The catch is that every abstraction hides something. The question is whether what it hides is something you needed to see.&lt;/p&gt;




&lt;h2&gt;
  
  
  Entity Framework: When You Want the Database to Mostly Handle Itself
&lt;/h2&gt;

&lt;p&gt;EF is Microsoft's answer to database management in .NET, and it's genuinely impressive in scope. You write C# and LINQ, it figures out the SQL. Schema changes? There's a migration system that tracks them as code and applies them consistently. Entity relationships, change tracking, support across database providers — it's all there.&lt;/p&gt;

&lt;p&gt;I've seen EF shine on large enterprise codebases where the data model is genuinely complex — dozens of entities, evolving business requirements, teams rotating in and out. The ability to write database interactions in C# without thinking about SQL keeps the codebase approachable for developers who aren't SQL-first thinkers.&lt;/p&gt;

&lt;p&gt;Where it trips people up: EF's generated queries aren't always efficient. When you're dealing with high data volumes or tight latency requirements, the abstraction starts costing you. And if you don't know enough SQL to recognize when EF is doing something suboptimal, you won't know there's a problem until users start noticing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Dapper: When You Trust Your Developers More Than Your ORM
&lt;/h2&gt;

&lt;p&gt;Dapper was built by the Stack Overflow team — which tells you everything about its priorities. Stack Overflow serves enormous query volumes. They needed something fast and something they could control completely. Dapper is what they built.&lt;/p&gt;

&lt;p&gt;It's a micro-ORM in the most literal sense. You write SQL. It maps the results to .NET objects. That's most of the story.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you get:&lt;/strong&gt; Query execution that sits almost directly on top of ADO.NET with minimal overhead. Performance benchmarks consistently favor Dapper, especially at scale. You know exactly what query is hitting your database because you wrote it yourself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you give up:&lt;/strong&gt; Everything EF handles automatically. Migrations, relationship management, change tracking — gone. You own all of that. If your team isn't comfortable writing and maintaining raw SQL, Dapper is going to create more problems than it solves.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where the Real Differences Show Up
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Speed&lt;/strong&gt; is the obvious one. Dapper wins on raw query performance. For most internal business tools or CRUD-heavy applications, it won't matter enough to change your decision. For high-traffic APIs or anything processing serious data volumes in real time, it absolutely will.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Schema management&lt;/strong&gt; is where EF has no competition. The migration system is one of the most useful things in the .NET ecosystem and it's often underrated. Dapper has nothing equivalent — you're either writing schema changes by hand or pulling in external tooling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Query control&lt;/strong&gt; is the flip side. EF's LINQ queries are readable and cover most cases, but they're not always what you'd write if you were writing SQL directly. Dapper gives you full control. Sometimes that's exactly what a complex or database-specific query needs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Relationship handling&lt;/strong&gt; is where Dapper starts to feel like work. EF navigates entity relationships through navigation properties — it's almost invisible. In Dapper, you write the joins yourself. In a simple schema, fine. In a complex one, that adds up quickly.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Actually Decide
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Reach for EF when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're building something with a complex data model&lt;/li&gt;
&lt;li&gt;Your team doesn't live in SQL all day&lt;/li&gt;
&lt;li&gt;The schema is going to keep evolving&lt;/li&gt;
&lt;li&gt;Developer speed matters more than database performance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Reach for Dapper when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're building something performance-sensitive&lt;/li&gt;
&lt;li&gt;Your team is comfortable with SQL&lt;/li&gt;
&lt;li&gt;The schema is relatively stable&lt;/li&gt;
&lt;li&gt;Latency is something your users will actually feel&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;And don't rule out using both. I've worked on codebases where EF handled the complex business logic and Dapper handled the performance-critical read operations. It's not the cleanest answer architecturally, but it's often the honest one.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Real Question Underneath All of This
&lt;/h2&gt;

&lt;p&gt;It's not "which ORM is better." It's "what's the actual bottleneck in this project — developer time, query performance, SQL expertise, schema complexity?"&lt;/p&gt;

&lt;p&gt;Answer that honestly and the tool choice mostly picks itself. The teams that struggle are the ones who skip that question and default to whatever they used last time.&lt;/p&gt;




&lt;p&gt;At &lt;a href="https://innostax.com/" rel="noopener noreferrer"&gt;Innostax&lt;/a&gt;, we build with both — the choice comes from what the project actually needs, not what we happen to prefer. If you're figuring out the right stack for something you're building, &lt;a href="https://innostax.com/contact" rel="noopener noreferrer"&gt;innostax.com/contact&lt;/a&gt; is the right place to start that conversation.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://innostax.com/blog/entity-framework-vs-dapper-choosing-the-right-orm-for-net/" rel="noopener noreferrer"&gt;Innostax Engineering Blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>database</category>
      <category>dapper</category>
      <category>orm</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Google Authenticator integration with Spring Boot</title>
      <dc:creator>Sahil Khurana</dc:creator>
      <pubDate>Mon, 08 Jun 2026 07:10:21 +0000</pubDate>
      <link>https://dev.to/sahil_khurana_486f374ecf2/google-authenticator-integration-with-spring-boot-2abp</link>
      <guid>https://dev.to/sahil_khurana_486f374ecf2/google-authenticator-integration-with-spring-boot-2abp</guid>
      <description>&lt;h1&gt;
  
  
  Google Authenticator Integration with Spring Boot
&lt;/h1&gt;

&lt;p&gt;Passwords alone just don't cut it anymore. We've all seen the headlines — data breaches, credential stuffing, account takeovers. And yet, a shocking number of applications still rely on a single password as the only line of defense. If you're building something with Spring Boot and haven't wired up multi-factor authentication yet, this guide is for you.&lt;/p&gt;

&lt;p&gt;We'll walk through integrating Google Authenticator into a Spring Boot project — step by step, with real code. By the end, your users will need both their password &lt;em&gt;and&lt;/em&gt; a time-sensitive code from their phone to get in. That's a meaningful security upgrade.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Exactly Is MFA — and Why Should You Care?
&lt;/h2&gt;

&lt;p&gt;Multi-factor authentication (MFA) — sometimes called two-factor authentication or 2FA — is the practice of requiring more than one proof of identity before granting access. Instead of just "something you know" (your password), you add "something you have" (your phone) or "something you are" (a fingerprint).&lt;/p&gt;

&lt;p&gt;It sounds simple, but the impact is significant. Even if an attacker manages to steal or guess a user's password, they still can't log in without that second factor. For industries where data sensitivity is high — think fintech, healthcare, legal — MFA isn't optional, it's expected.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Does a TOTP-Based Factor Actually Work?
&lt;/h2&gt;

&lt;p&gt;Google Authenticator generates what's called a &lt;strong&gt;Time-based One-Time Password (TOTP)&lt;/strong&gt;. Here's the basic idea:&lt;/p&gt;

&lt;p&gt;When a user first enables 2FA, the server creates a shared secret key and hands it to the user (usually via a QR code). From that point on, both the server and the app on the user's phone use the same algorithm — combining that secret with the current timestamp — to independently calculate a 6-digit code. The codes are valid for only 30 seconds.&lt;/p&gt;

&lt;p&gt;When the user logs in, they open Google Authenticator, read the current code, and type it in. The server runs the same calculation and checks if the numbers match. No network call required on the app's side, no SMS that could be intercepted. It's elegant and robust.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Makes Google Authenticator a Good Choice?
&lt;/h2&gt;

&lt;p&gt;There are several authenticator apps out there, but Google Authenticator remains a go-to for good reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It works completely offline — no cell signal or Wi-Fi needed once set up&lt;/li&gt;
&lt;li&gt;It's immune to SIM swap attacks, which plague SMS-based 2FA&lt;/li&gt;
&lt;li&gt;Setup is as easy as scanning a QR code&lt;/li&gt;
&lt;li&gt;It's free, widely trusted, and available on both iOS and Android&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Integrating Google Authenticator into a Spring Boot Project
&lt;/h2&gt;

&lt;p&gt;Let's get into the actual implementation. There are four steps: add the right dependencies, generate a secret key per user, produce a QR code for setup, and verify the TOTP code at login time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1 — Add the Required Dependencies
&lt;/h3&gt;

&lt;p&gt;Open your &lt;code&gt;pom.xml&lt;/code&gt; and add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;de.taimos&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;totp&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.google.zxing&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;core&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.3.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;compile&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.google.zxing&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;javase&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.3.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;compile&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;totp&lt;/code&gt; library handles the TOTP math. The two &lt;code&gt;zxing&lt;/code&gt; libraries handle QR code generation — they're from Google and do a solid job.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 2 — Generate a Secret Key for Each User
&lt;/h3&gt;

&lt;p&gt;Every user gets their own unique secret key. This is generated once during setup and stored (securely) in your database alongside their account.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;generateSecretKey&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;SecureRandom&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SecureRandom&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="o"&gt;];&lt;/span&gt;
    &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;nextBytes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;Base32&lt;/span&gt; &lt;span class="n"&gt;base32&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Base32&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;base32&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encodeToString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;SecureRandom&lt;/code&gt; ensures the key is cryptographically random. The result is a Base32-encoded string — that format is what TOTP libraries and authenticator apps expect.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 3 — Generate the QR Code for Setup
&lt;/h3&gt;

&lt;p&gt;Once you have the secret key, you need to present it to the user in a way Google Authenticator can consume. That means building a specially formatted &lt;code&gt;otpauth://&lt;/code&gt; URI and then encoding it as a QR code image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="no"&gt;ENCODED_SPACE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"%20"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="no"&gt;PLUS_SYMBOL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"+"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;generateAuthenticatorQR&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;secretKey&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;accountName&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;barCodeUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getGoogleAuthenticatorBarCode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secretKey&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;accountName&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;createQRCode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;barCodeUrl&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error generating QR code: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getGoogleAuthenticatorBarCode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;secretKey&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;issuer&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;utf8&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"otpauth://totp/"&lt;/span&gt;
            &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;URLEncoder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;issuer&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;":"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;utf8&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;replace&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PLUS_SYMBOL&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;ENCODED_SPACE&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"?secret="&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;URLEncoder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secretKey&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;utf8&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;replace&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PLUS_SYMBOL&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;ENCODED_SPACE&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"&amp;amp;issuer="&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;URLEncoder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;issuer&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;utf8&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;replace&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PLUS_SYMBOL&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;ENCODED_SPACE&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;UnsupportedEncodingException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;IllegalStateException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;createQRCode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;barCodeData&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;filePath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"QRCode.png"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="nc"&gt;BitMatrix&lt;/span&gt; &lt;span class="n"&gt;matrix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MultiFormatWriter&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;encode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;barCodeData&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;BarcodeFormat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;QR_CODE&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;FileOutputStream&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FileOutputStream&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filePath&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;MatrixToImageWriter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;writeToStream&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;matrix&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"png"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;File&lt;/span&gt; &lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;File&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filePath&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;imgBytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FileUtils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;readFileToByteArray&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"data:image/PNG;base64,"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;Base64&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEncoder&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;encodeToString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;imgBytes&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error creating QR code: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Failed to generate QR"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The method returns a Base64-encoded PNG image that you can drop directly into an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag on your setup page. The user scans it with Google Authenticator, and they're enrolled.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 4 — Verify the TOTP at Login
&lt;/h3&gt;

&lt;p&gt;When a user logs in and submits their 6-digit code, you need to verify it against the secret key stored for their account:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getTOTPCode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;secretKey&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Base32&lt;/span&gt; &lt;span class="n"&gt;base32&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Base32&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base32&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;decode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secretKey&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;hexKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Hex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encodeHexString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;TOTP&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOTP&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hexKey&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Call this method with the user's stored secret key, get the expected code back, and compare it to what they entered. If it matches — they're in. If not — reject the attempt.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Few Things Worth Keeping in Mind
&lt;/h2&gt;

&lt;p&gt;Before you ship this, a couple of practical notes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Store secret keys securely.&lt;/strong&gt; Treat them like passwords — encrypt them at rest. If an attacker gets your database and the keys are in plaintext, the 2FA protection is gone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Handle clock drift.&lt;/strong&gt; TOTP codes are time-based, so if a user's phone clock is significantly off, valid codes might get rejected. Most TOTP libraries have a tolerance window — usually ±1 interval — to accommodate minor drift. Make sure yours is configured reasonably.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Give users a recovery path.&lt;/strong&gt; What happens if someone loses their phone? You'll want backup codes or an admin-assisted recovery flow before you mandate 2FA for all users.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Adding Google Authenticator to a Spring Boot app is genuinely not that complex — a couple of libraries, some key generation logic, a QR code at setup, and a verification check at login. The lift is small; the security payoff is real.&lt;/p&gt;

&lt;p&gt;For any application handling sensitive user data, this kind of layered authentication is increasingly table stakes. Whether you're building for finance, healthcare, or just want to protect your users properly, getting MFA in place early saves a lot of headache down the road.&lt;/p&gt;

&lt;p&gt;Have questions or running into something unexpected during setup? Drop a comment or reach out — happy to help troubleshoot.&lt;/p&gt;




&lt;p&gt;Building secure authentication systems and need a team that understands backend security beyond just checking compliance boxes? At Innostax, we help companies implement MFA, strengthen authentication flows, and build secure Spring Boot applications that hold up in real-world production environments. Learn more at &lt;a href="https://innostax.com/" rel="noopener noreferrer"&gt;innostax.com&lt;/a&gt; or get in touch at &lt;a href="https://innostax.com/contact-us" rel="noopener noreferrer"&gt;innostax.com/contact&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Originally published on the &lt;a href="https://innostax.com/blog/google-authenticator-integration-with-spring-boot/" rel="noopener noreferrer"&gt;Innostax Engineering Blog&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>springboot</category>
      <category>java</category>
      <category>backendsecurity</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>How to Use Multiple Prettier Plugins for Consistent Code Formatting ?</title>
      <dc:creator>Sahil Khurana</dc:creator>
      <pubDate>Fri, 05 Jun 2026 07:40:37 +0000</pubDate>
      <link>https://dev.to/sahil_khurana_486f374ecf2/how-to-use-multiple-prettier-plugins-for-consistent-code-formatting--a5j</link>
      <guid>https://dev.to/sahil_khurana_486f374ecf2/how-to-use-multiple-prettier-plugins-for-consistent-code-formatting--a5j</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Incorporating Multiple Prettier Plugins&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;I've sat in code reviews where a perfectly decent PR stayed open for two days. Tests green. Logic sound. But somewhere around comment fifteen, the thread had devolved into a debate about trailing commas — and at that point, nobody was reviewing code anymore. They were defending preferences.&lt;/p&gt;

&lt;p&gt;That's not a people problem. It's a tooling problem. And Prettier — especially paired with the right set of plugins — solves it completely. The multi-plugin setup most tutorials gloss over is worth the extra thirty minutes to configure.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;💡 The short version: A codebase where nobody argues about formatting isn't a utopia. It's what happens when you hand that decision to a tool and tell everyone to move on.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Key Takeaways&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  Manual formatting consistency breaks down fast. Two developers with different editor settings will produce two versions of the same file. Prettier removes that variable entirely — same output, every time, from everyone.&lt;/li&gt;
&lt;li&gt;  Formatting time is hidden until you measure it. Across a team running 15–20 PRs a week, the minutes lost to formatting debates add up to roughly a full engineer-day per sprint. That time disappears the moment Prettier is wired in.&lt;/li&gt;
&lt;li&gt;  Plugins extend Prettier to your full stack — not just JavaScript. One config file, one command, consistent formatting across every language your project touches.&lt;/li&gt;
&lt;li&gt;  Better plugins mean sharper editor integrations — more accurate autocomplete, earlier error detection, and language server suggestions that actually fit the code you're writing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Why Prettier Exists — and Why It Took Off&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Clean code means more than working code. It means code that someone else can sit down with at 11pm during an incident — exhausted, slightly stressed — understand in under a minute, and modify without second-guessing every formatting choice the original author made.&lt;/p&gt;

&lt;p&gt;Prettier was built to clear that bar automatically. It's an open-source formatter, free and opinionated by design. You don't configure Prettier to match your preferences. You configure your preferences to match Prettier.&lt;/p&gt;

&lt;p&gt;That sounds like a frustrating trade-off until you realize the whole point is removing formatting from the list of things anyone has to think about. Once developers stop spending review cycles on formatting, the quality of technical feedback on everything else goes up. That's a pattern, not a coincidence.&lt;/p&gt;

&lt;p&gt;Out of the box, Prettier handles JavaScript, TypeScript, CSS, HTML, Markdown, JSON, YAML, GraphQL, and a few others — which covers the core of most web stacks. But most real projects are messier. A PHP backend here, a Go microservice there, some XML config nobody wants to touch. That's where plugins come in.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What Prettier Actually Does Under the Hood&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This matters more than most tutorials suggest — especially once you start working with plugins.&lt;/p&gt;

&lt;p&gt;Prettier doesn't scan your code looking for violations the way a linter does. It parses your source file into an abstract syntax tree — a structured representation of what the code means, stripped of all formatting — and then reprints that tree from scratch, according to its own rules. The original file's formatting is irrelevant. What comes out is always Prettier's version.&lt;/p&gt;

&lt;p&gt;That's why Prettier is so consistent. It's not fixing your formatting. It's replacing it.&lt;/p&gt;

&lt;p&gt;It also explains why parser support matters so much. If Prettier can't parse a language, it can't format it. Every language needs its own parser, and that parser either ships with Prettier or comes via a plugin. No parser, no formatting — full stop.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Why Multiple Plugins? The Real Argument.&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The base Prettier install handles JavaScript and the web stack well. Here's where teams usually run into friction: they set up Prettier, it works beautifully on JavaScript files, and then someone notices it's doing nothing for the SCSS files. Or the PHP templates. Or the GraphQL schemas.&lt;/p&gt;

&lt;p&gt;So those files stay inconsistently formatted. The value of having a formatter drops proportionally. Plugins fix this.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Language-specific formatting — and why "good enough" isn't&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;A generic formatter applied to Python code will produce something syntactically valid but wrong in feel — wrong enough that any developer who writes Python regularly will notice on first read. Same with Go. Same with Ruby. A well-written language plugin carries the formatting expectations of that language's community. It doesn't just make the code consistent — it makes it feel like it belongs.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Framework awareness — more specific than you'd expect&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Framework-specific plugins go a step further. A React plugin doesn't just understand JSX syntax — it understands how JSX is supposed to look. Self-closing tags, prop order, multi-line JSX parentheses. That said, check the last commit date before committing to one in a long-lived project. A formatter that hasn't been updated in 18 months may not understand newer framework patterns.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;The productivity argument — what "saves time" actually means&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Every decision has a cognitive cost. Across a full workday, eliminating a whole category of low-stakes decisions — "should this trailing comma be here?" — hands attention back to things that actually need it. Not just fewer minutes spent formatting — fewer interruptions to flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Setting It Up — What the Process Actually Looks Like&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Three steps. No magic.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 1: Install everything at once&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; prettier &lt;span class="se"&gt;\\&lt;/span&gt;

prettier-plugin-tailwindcss &lt;span class="se"&gt;\\&lt;/span&gt;

@prettier/plugin-php &lt;span class="se"&gt;\\&lt;/span&gt;

prettier-plugin-organize-imports
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use your package manager to pull in Prettier and whatever plugins you need. Grab them all in a single install command.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 2: Write one config file&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Create a .prettierrc or prettier.config.js at the project root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nl"&gt;"semi"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nl"&gt;"singleQuote"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nl"&gt;"trailingComma"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"es5"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nl"&gt;"printWidth"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nl"&gt;"plugins"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="s2"&gt;"prettier-plugin-tailwindcss"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="s2"&gt;"@prettier/plugin-php"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="s2"&gt;"prettier-plugin-organize-imports"&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;


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

&lt;/div&gt;



&lt;p&gt;Prettier picks this up automatically. One file. One place.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 3: Run it — and set it to run automatically&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx prettier &lt;span class="nt"&gt;--write&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Formats everything. The first time you run this on a codebase that's never had a formatter, expect a large diff — sometimes thousands of files. Every single change is cosmetic. Nothing functional. After that first run, most teams wire Prettier into a pre-commit hook via lint-staged.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What Prettier Handles Natively — and Where Plugins Pick Up&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Native support covers the core of modern web development:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  JavaScript (JS) and TypeScript (TS), including JSX and TSX variants&lt;/li&gt;
&lt;li&gt;  CSS, HTML, and Markdown&lt;/li&gt;
&lt;li&gt;  JSON and YAML&lt;/li&gt;
&lt;li&gt;  GraphQL queries&lt;/li&gt;
&lt;li&gt;  Vue.js single-file components&lt;/li&gt;
&lt;li&gt;  Handlebars templates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Languages without native support: C, C++, Rust, Java, Python, Ruby, PHP, Bash, XML (outside JSX), LaTeX, Perl, SQL. Several have community-maintained plugins — PHP is the most mature example.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Parsers: The Mechanism Behind the Formatting&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A parser reads source code and converts it into a format Prettier can work with — specifically, an abstract syntax tree. That tree represents the structure and meaning of the code, independent of how it's formatted.&lt;/p&gt;

&lt;p&gt;Prettier ships with parsers for everything it natively supports: Babel for modern JavaScript including JSX, TypeScript natively, Flow for annotated JavaScript, and separate parsers for CSS, SCSS, Less, HTML, Markdown, JSON, YAML, GraphQL, Vue.js, Handlebars, and Angular templates.&lt;/p&gt;

&lt;p&gt;When you install a language plugin, what you're really installing is a parser for that language plus the formatting rules Prettier should apply once it has the AST. That's the whole mechanism.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What This Looks Like in a Real Project&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Take a moderately sized React codebase — six developers, forty or fifty component files. Without Prettier, the codebase accumulates inconsistency over time. Not because anyone is careless, but because developers set up their editors differently, make different calls under deadline pressure, and inherit formatting decisions from whoever wrote the file first.&lt;/p&gt;

&lt;p&gt;After Prettier: every file looks like it came from the same hand. The JSX is consistent. Import blocks follow the same order. Prop formatting doesn't shift between components. New developers can read any file without mentally translating between styles — which in practice is one of the more reliable ways to cut ramp-up time for new hires.&lt;/p&gt;

&lt;p&gt;The change in code review is equally noticeable. When formatting is handled automatically, reviewers stop leaving comments about it. The discussion shifts to logic, architecture, edge cases — the parts that actually benefit from human attention.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Case for Boring Infrastructure&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Prettier doesn't ship features. It doesn't write better algorithms or make architectural decisions. What it does — with very little ongoing effort — is eliminate an entire category of low-value friction from development.&lt;/p&gt;

&lt;p&gt;Multiple plugins extend that to the full surface of a real-world project. One tool. One config. One command.&lt;/p&gt;

&lt;p&gt;Prettier isn't a silver bullet, and it's worth being clear about that. It handles formatting, not code quality. You still need linters for logic issues, code review for architectural decisions, and experienced developers to catch the things tools miss. But for the specific problem it solves — consistent formatting across a shared codebase — it solves it completely.&lt;/p&gt;

&lt;p&gt;Set it up once. Wire it into your workflow. Then take formatting off the agenda permanently.&lt;/p&gt;

&lt;p&gt;Read More - &lt;a href="https://innostax.com/blog/effortless-code-enhancement-incorporating-multiple-prettier-plugins/" rel="noopener noreferrer"&gt;Effortless Code Enhancement: Incorporating Multiple Prettier Plugins&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sahil Khurana - CTO, &lt;a href="//Innostax.com"&gt;Innostax&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;About &lt;a href="//Innostax.com"&gt;Innostax&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Founded in 2014, Innostax is a software development company built on accountability and ownership. We deliver progress with clarity—flagging risks early, aligning teams, and ensuring quality at every step. With a commitment to responsibility and reliability, we take full ownership of everything we build, making software development seamless and dependable.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>coding</category>
      <category>resources</category>
    </item>
    <item>
      <title>Serverless Framework Deployment: Unleash the Power of AWS Lambda</title>
      <dc:creator>Sahil Khurana</dc:creator>
      <pubDate>Thu, 04 Jun 2026 09:45:24 +0000</pubDate>
      <link>https://dev.to/sahil_khurana_486f374ecf2/serverless-framework-deployment-unleash-the-power-of-aws-lambda-1fed</link>
      <guid>https://dev.to/sahil_khurana_486f374ecf2/serverless-framework-deployment-unleash-the-power-of-aws-lambda-1fed</guid>
      <description>&lt;p&gt;Let me tell you exactly what happened the first time I tried to set up Lambda manually.&lt;/p&gt;

&lt;p&gt;Four hours. IAM trust policies I didn't fully understand, ARNs copy-pasted into the wrong fields, an API Gateway that was technically configured but somehow not routing anything correctly, and a deploy that failed with an error message pointing me nowhere useful. I hadn't written a single line of actual business logic yet.&lt;/p&gt;

&lt;p&gt;That's when someone on my team mentioned the Serverless Framework. My first reaction was honestly skepticism — another abstraction layer sounded like another thing to learn and eventually fight with. I was wrong about that.&lt;/p&gt;

&lt;p&gt;This isn't a "look how clean this tool is" post. It's more like: here's what I actually did to get a Postgres-backed CRUD API running on Lambda, step by step, including the parts that tripped me up.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What the Framework Is Actually Doing Under the Hood&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Worth knowing before you start: the Serverless Framework isn't magic. It's generating CloudFormation templates and submitting them to AWS on your behalf. Your Lambda functions, API Gateway routes, CloudWatch log groups — all of it gets provisioned from a single config file.&lt;/p&gt;

&lt;p&gt;It works with other providers too, but the AWS integration is where it really earns its keep. The console clicking and manual ARN-wiring that burns time at the start of every serverless project? Gone. Same deploy workflow whether you're building a REST API, an event processor, or a cron job. Once you've done it once, the second project takes a fraction of the time.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What You're Building&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Four live endpoints backed by PostgreSQL. A Users table. Create, read, update, delete — nothing exotic, but a real enough foundation that you can extend it into something actual once this guide is done.&lt;/p&gt;

&lt;p&gt;You'll need an AWS account, the AWS CLI installed, and the Serverless Framework installed before starting. That's it.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 1: Sort Out Your AWS Credentials&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Run this to create both config files in one go:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; ~/.aws/credentials
[default]
aws_access_key_id = &amp;lt;REPLACE_WITH_YOUR_SECRET_KEY&amp;gt;
aws_secret_access_key = &amp;lt;REPLACE_WITH_YOUR_ACCESS_KEY&amp;gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; ~/.aws/config
[default]
region = eu-west-1
output = json
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace the placeholders with your actual keys. I'm using eu-west-1 — swap in whatever region makes sense for where you're deploying.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 2: The IAM Role (Seriously, Don't Skip This)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Okay so this is the one that burned me the first time around. The Serverless Framework needs an IAM role with the right permissions to provision resources during deployment. If you skip this, the deploy fails. The error you'll get doesn't say "hey you're missing an IAM role" — it says something vague about permissions and you'll spend a while chasing the wrong thing.&lt;br&gt;
Create the role:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash
aws iam create-role &lt;span class="nt"&gt;--role-name&lt;/span&gt; serverlessLabs &lt;span class="nt"&gt;--assume-role-policy-document&lt;/span&gt; &lt;span class="s1"&gt;'{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": { "Service": "lambda.amazonaws.com" },
      "Action": "sts:AssumeRole"
    }
  ]
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Attach the Lambda execution policy to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash
aws iam attach-role-policy &lt;span class="nt"&gt;--role-name&lt;/span&gt; serverlessLabs &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-arn&lt;/span&gt; arn:aws:iam::aws:policy/AWSLambda_FullAccess
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify it's actually there:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash
aws iam get-role &lt;span class="nt"&gt;--role-name&lt;/span&gt; serverlessLabs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see role details, good. If you get an error, fix it here rather than finding out it's broken in step 7.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 3: Project Setup&lt;/strong&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash
&lt;span class="nb"&gt;mkdir &lt;/span&gt;node-crud
&lt;span class="nb"&gt;cd &lt;/span&gt;node-crud
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; serverless
npm i prisma
Then init Prisma:
bash
npx prisma init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things get created — a prisma/ directory with schema.prisma inside it, and a .env file at the root. The .env is where your database URL goes. Add it to .gitignore right now. Not later. Now.&lt;/p&gt;

&lt;p&gt;Step 4: Database Schema&lt;br&gt;
Open prisma/schema.prisma:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;prisma
datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model Users {
  id    String  @id @default(uuid())
  name  String? @db.VarChar(255)
  email String? @db.VarChar(255)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Drop your connection string in .env:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;env
&lt;/span&gt;&lt;span class="nv"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"postgresql://&amp;lt;username&amp;gt;:&amp;lt;password&amp;gt;@&amp;lt;hosted dbURL&amp;gt;:5432/mydb?schema=public"&lt;/span&gt;
Now run these three, &lt;span class="k"&gt;in &lt;/span&gt;this order, all three:
bash
npx prisma format
npx prisma generate
npx prisma migrate dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;format tidies the schema file. generate builds the Prisma client your functions will import. migrate dev actually creates the Users table in your database. If migrate dev fails, stop here and figure out why — your connection string is probably wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 5: The Handler Functions&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;handler.js at the project root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;javascript&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PrismaClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@prisma/client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PrismaClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newUser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Failed to create user: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;read&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Failed to fetch users: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathParameters&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;updatedUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;updatedUser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Failed to update user: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathParameters&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deletedUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;User deleted successfully&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;deletedUser&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Failed to delete user: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same structure across all four — try the operation, return 200 with the result, catch and return 500 with the actual error message. I've debugged enough Lambda functions that swallow errors silently to know: always include error.message in the 500 response. Future you will appreciate it.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 6: serverless.yml&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Wipe whatever's in there and replace with this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;yaml&lt;/span&gt;
&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres-crud-api&lt;/span&gt;
&lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws&lt;/span&gt;
  &lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nodejs18.x&lt;/span&gt;
&lt;span class="na"&gt;functions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;handler.create&lt;/span&gt;
    &lt;span class="na"&gt;events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;create&lt;/span&gt;
          &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;post&lt;/span&gt;
  &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;handler.read&lt;/span&gt;
    &lt;span class="na"&gt;events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
          &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;get&lt;/span&gt;
  &lt;span class="na"&gt;update&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;handler.update&lt;/span&gt;
    &lt;span class="na"&gt;events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;update/{id}&lt;/span&gt;
          &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;put&lt;/span&gt;
  &lt;span class="na"&gt;delete&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;handler.delete&lt;/span&gt;
    &lt;span class="na"&gt;events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;delete/{id}&lt;/span&gt;
          &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;delete&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The provider block sets the target cloud and runtime — Node 18.x here, though honestly check if there's a newer LTS available by the time you're reading this.&lt;br&gt;
functions is where each handler export gets mapped to a route and HTTP method. API Gateway configuration is derived automatically from these entries — you're not touching it anywhere else.&lt;/p&gt;

&lt;p&gt;The {id} segments in the update and delete paths become event.pathParameters.id inside the handler. So a request to /update/some-uuid hands you "some-uuid" to work with. That's all there is to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 7: Deploy&lt;/strong&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash
sls deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Grab a coffee. Two minutes, give or take. When it finishes, your terminal shows the live API Gateway URLs for all four endpoints. Pop open the AWS console if you want to see what was built — Lambda functions, API Gateway config, CloudFormation stack, all of it created without you touching the console once.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 8: Testing&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Postman, cURL, doesn't matter. Here's the order I'd go in:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GET /read first&lt;/strong&gt;. Should return an empty array. If it throws an error instead, something's broken in the Lambda-to-Prisma connection and you want to know before you try writing data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;POST /create&lt;/strong&gt; — send { "name": "Jane", "email": "&lt;a href="mailto:jane@example.com"&gt;jane@example.com&lt;/a&gt;" }. Get back a user object with an id? Prisma is talking to Postgres. Lambda is executing. That's the hard part confirmed working.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PUT /update/{id}&lt;/strong&gt; — use the id from whatever create returned. Change the name. Verify the response reflects it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DELETE /delete/{id}&lt;/strong&gt; — the response includes the record that got deleted, so you can confirm you removed the right one.&lt;/p&gt;

&lt;p&gt;Create works, read returns it — the full chain is functioning. Everything else from here is adding more of the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What's Next&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The setup is the slow part. It's done. Adding a new endpoint now is: write the handler function, add the route to serverless.yml, run sls deploy. That's genuinely it.&lt;/p&gt;

&lt;p&gt;Natural next steps: JWT auth if you're building anything that needs to be secured, environment variable separation for staging vs production (set it up early, not after you've already deployed to prod by accident), more Prisma models for additional tables, more complex query logic inside the existing handlers.&lt;/p&gt;

&lt;p&gt;The deployment model doesn't change regardless of what you add to the application. That's the whole point of it.&lt;/p&gt;




&lt;p&gt;At &lt;a href="https://innostax.com/" rel="noopener noreferrer"&gt;Innostax&lt;/a&gt;, we've helped engineering teams get through exactly this — serverless setup on AWS, Prisma + Postgres wiring, IAM configurations that don't silently break at deploy time — and built it into backends that hold up past the prototype stage. If you're figuring out the right serverless foundation for your project, &lt;a href="https://innostax.com/contact-us" rel="noopener noreferrer"&gt;innostax.com/contact&lt;/a&gt; is where that conversation happens.&lt;/p&gt;

&lt;p&gt;Originally published on the &lt;a href="https://innostax.com/blog/serverless-framework/" rel="noopener noreferrer"&gt;Innostax Engineering Blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>softwaredevelopment</category>
      <category>node</category>
    </item>
    <item>
      <title>Redux Thunk vs Saga: Choosing the Best Middleware</title>
      <dc:creator>Sahil Khurana</dc:creator>
      <pubDate>Wed, 03 Jun 2026 08:17:24 +0000</pubDate>
      <link>https://dev.to/sahil_khurana_486f374ecf2/redux-thunk-vs-saga-choosing-the-best-middleware-nn7</link>
      <guid>https://dev.to/sahil_khurana_486f374ecf2/redux-thunk-vs-saga-choosing-the-best-middleware-nn7</guid>
      <description>&lt;p&gt;Here's the situation that gets everyone eventually.&lt;/p&gt;

&lt;p&gt;Your Redux setup is clean. Actions dispatch, reducers update state, components re-render, the whole pipeline works exactly like it's supposed to. Then a requirement lands that needs an API call, and suddenly you're staring at a reducer wondering where exactly you're supposed to put a fetch().&lt;/p&gt;

&lt;p&gt;You search around. Two names come up constantly: Redux Thunk and Redux Saga. Both solve async side effects in Redux. That's roughly where the similarity ends.&lt;/p&gt;

&lt;p&gt;I've shipped production apps with both. They're not interchangeable and the difference isn't just syntax, it's a fundamentally different way of thinking about async control flow. Picking the wrong one for your project's complexity level will cost you later, usually at the worst possible time.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Quick context on what Redux actually does&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;For anyone newer to this: Redux centralizes your application state in a single store. Components don't manage their own data — they read from the store and dispatch actions to change it. Every state change flows through the same path: action dispatched → reducer processes it → store updates → components re-render.&lt;/p&gt;

&lt;p&gt;The connect function from react-redux wires this into your components. It's elegant when it works, and for synchronous state changes it works really well.&lt;/p&gt;

&lt;p&gt;The issue is that reducers have to be pure functions. No side effects, no async operations, nothing with unpredictable timing. A fetch call inside a reducer isn't just bad practice — it fundamentally breaks the model. Redux has no built-in answer for this. Thunk and Saga are both third-party answers to that gap, just wildly different ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Redux Thunk: Functions returning functions&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Thunk's insight is simple. Normally an action creator returns a plain object. Thunk middleware intercepts action creators that return functions instead, and calls them with dispatch and getState. That's the whole trick.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;javascript&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;FETCH_SUCCESS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;FETCH_ERROR&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why does this work so well for simple cases? Because it's just JavaScript. Functions returning functions — closures, promises, .then() chains. Nothing here requires learning a new paradigm. A developer who's never touched Redux middleware before can read a thunk file and understand what it's doing in about ten minutes.&lt;/p&gt;

&lt;p&gt;That's genuinely valuable, and I don't want to undersell it. Fast onboarding matters.&lt;/p&gt;

&lt;p&gt;What Thunk doesn't give you is structure for when things get complicated. Need to cancel a request if the user navigates away? Manual. Race condition where two requests fire and you only want the last one? Manual. Multiple async operations that need to coordinate? You're building that coordination logic yourself, in the thunk, and it gets messy fast. I've seen thunk files that are 200 lines of nested promise chains that technically work but that nobody wants to touch.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Redux Saga: A completely different mental model&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Saga takes an approach that seemed strange to me when I first encountered it. Instead of intercepting action creators, Saga runs persistent background processes — "sagas" — that watch for specific actions and respond with their own async logic. They live entirely separately from your normal Redux flow.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;javascript&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;fetchDataSaga&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;FETCH_SUCCESS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;FETCH_ERROR&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;watchFetchData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nf"&gt;takeEvery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;FETCH_REQUEST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fetchDataSaga&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function* and yield syntax is what stops people cold. Generator functions aren't something most JavaScript developers use regularly, and the first time you see yield call(api.fetchData) instead of just await api.fetchData(), it raises obvious questions about why you'd add that layer of indirection.&lt;/p&gt;

&lt;p&gt;The answer becomes clear when complexity hits. takeEvery, takeLatest, race, cancel — these are built-in Saga effects for patterns that Thunk handles awkwardly at best. takeLatest automatically cancels any previous in-flight request when a new one fires. One line. In Thunk that's a manual cancellation token, a ref, conditional dispatch logic. It's doable but it's work.&lt;/p&gt;

&lt;p&gt;Once generators click — and it takes a few days of actual use, not just reading about them — the sequential-looking flow of a saga is easier to reason about than deeply nested promise chains. That's the tradeoff: steeper upfront cost, better ceiling.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The testing difference is bigger than people realize&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This is something the comparison articles usually gloss over.&lt;br&gt;
Testing a thunk is quick:&lt;/p&gt;

&lt;p&gt;ja&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;vascript&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;mockStore&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getActions&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;([{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;FETCH_SUCCESS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;mockData&lt;/span&gt; &lt;span class="p"&gt;}]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You call it, check what got dispatched, done. If you're moving fast and the async logic is simple, this is a real advantage.&lt;br&gt;
Testing a saga is more involved. You're stepping through a generator, asserting on each yield effect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;javascript&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;gen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fetchDataSaga&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mockData&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;FETCH_SUCCESS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;mockData&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's more setup. Libraries like redux-saga-test-plan help significantly. The end result is actually more thorough coverage of your async behavior — you're testing the logic of the saga step by step, not just the final dispatched actions. But it takes longer to write. That's just true.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;So which one&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Genuinely depends on where your app is and where it's going.&lt;/p&gt;

&lt;p&gt;Simple app, a handful of API calls, standard loading/error states, team that needs to move quickly — Thunk. There's no complexity justifying the Saga investment and adding it anyway just creates overhead without payoff.&lt;/p&gt;

&lt;p&gt;Larger app, async operations that need to cancel each other, coordinate with each other, debounce, handle race conditions — Saga. The generator model that feels awkward in week one becomes the thing you're grateful for in month six when you need to add cancellation to something and it takes an hour instead of a day.&lt;/p&gt;

&lt;p&gt;The mistake I see most often is teams adopting Saga on a simple app because it seems more "professional," then spending weeks onboarding developers onto generator syntax for async logic that a few thunks would have handled fine. The other mistake is teams sticking with Thunk on a growing application well past the point where the complexity is screaming for something more structured.&lt;/p&gt;

&lt;p&gt;Neither is universally better. They solve different versions of the same problem.&lt;/p&gt;




&lt;p&gt;At &lt;a href="https://innostax.com/" rel="noopener noreferrer"&gt;Innostax&lt;/a&gt;, we've helped React teams make this exact call — Thunk when the complexity didn't warrant more, Saga when it did, and migrated between the two when an app outgrew its original choice. If you're figuring out the right async architecture for your Redux application, &lt;a href="https://innostax.com/contact-us" rel="noopener noreferrer"&gt;innostax.com/contact&lt;/a&gt; is where that conversation happens.&lt;/p&gt;

&lt;p&gt;Originally published on the &lt;a href="https://innostax.com/blog/redux-thunk-vs-saga-choosing-the-best-middleware/" rel="noopener noreferrer"&gt;Innostax Engineering Blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>react</category>
      <category>frontend</category>
      <category>javascript</category>
      <category>redux</category>
    </item>
  </channel>
</rss>
