DEV Community: Ferry Ananda Febian The latest articles on DEV Community by Ferry Ananda Febian (@ferryops). https://dev.to/ferryops https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1093075%2Fc9aa0c9a-d1e2-4486-9114-d191b3d7ddcf.jpeg DEV Community: Ferry Ananda Febian https://dev.to/ferryops en I Stopped Using useEffect for Data Fetching in React Ferry Ananda Febian Tue, 23 Dec 2025 04:11:08 +0000 https://dev.to/ferryops/i-stopped-using-useeffect-for-data-fetching-in-react-3ahn https://dev.to/ferryops/i-stopped-using-useeffect-for-data-fetching-in-react-3ahn <p>For a long time, <code>useEffect</code> was my default tool for handling data fetching in React. It worked but it never felt right.</p> <p>After adopting <strong>SWR</strong>, especially its <code>mutate</code> API, I realized something important:</p> <blockquote> <p>Data fetching is not a side effect.</p> </blockquote> <p><a href="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fruq267wnvqx26hhrcg5j.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fruq267wnvqx26hhrcg5j.png" alt="Example hooks with SWR" width="800" height="607"></a></p> <p>Once I embraced that idea, <code>useEffect</code> slowly disappeared from my data-fetching logic.</p> <h2> The Problem with <code>useEffect</code> for Fetching Data </h2> <p>Using <code>useEffect</code> to fetch data usually looks like this:</p> <ul> <li>Track dependencies carefully</li> <li>Handle loading state manually</li> <li>Handle error state manually</li> <li>Prevent infinite loops</li> <li>Sync UI updates with async behavior</li> </ul> <p>As the application grows, this approach becomes:</p> <ul> <li>Hard to reason about</li> <li>Easy to break</li> <li>Difficult to reuse</li> </ul> <p>Most of the complexity comes from treating data fetching as an <em>imperative side effect</em>, rather than part of application state.</p> <h2> SWR Changed My Mental Model </h2> <p>With SWR, data fetching becomes <strong>declarative</strong>.</p> <p>Instead of asking:</p> <blockquote> <p>“When should I fetch this data?”</p> </blockquote> <p>You describe:</p> <blockquote> <p>“This component needs this data.”</p> </blockquote> <p>SWR handles:</p> <ul> <li>Caching</li> <li>Revalidation</li> <li>Deduplication</li> <li>Loading and error states</li> </ul> <p>And most importantly, it exposes <code>mutate</code>.</p> <h2> Why <code>mutate</code> Replaced <code>useEffect</code> for Me </h2> <p><code>mutate</code> allows you to:</p> <ul> <li>Update cached data directly</li> <li>Perform optimistic updates</li> <li>Revalidate data with a single function call</li> </ul> <p>This means:</p> <ul> <li>No manual refetch logic</li> <li>No dependency arrays</li> <li>No duplicated fetching code</li> </ul> <p>Fetching and updating data becomes <strong>intent-based</strong>, not lifecycle-based.</p> <h2> <code>useEffect</code> vs <code>mutate</code> (SWR) </h2> <h3> <code>useEffect</code> </h3> <ul> <li>Imperative data fetching</li> <li>Manual dependency management</li> <li>Boilerplate for loading and error state</li> <li>Prone to race conditions</li> <li>Fetch logic often scattered across components</li> </ul> <h3> <code>mutate</code> (SWR) </h3> <ul> <li>Declarative data updates</li> <li>Built-in cache and revalidation</li> <li>One-line refetch or optimistic update</li> <li>Centralized and reusable logic</li> <li>UI stays in sync with cache automatically</li> </ul> <h2> When I Still Use <code>useEffect</code> </h2> <p>I still use <code>useEffect</code> but only for <strong>true side effects</strong>, such as:</p> <ul> <li>Event listeners</li> <li>Subscriptions</li> <li>Timers</li> <li>Direct DOM interactions</li> </ul> <p>Not for data fetching.</p> <h2> Final Thoughts </h2> <p>Once you treat data as <strong>state</strong>, not a side effect, your React code becomes:</p> <ul> <li>Cleaner</li> <li>More predictable</li> <li>Easier to maintain</li> </ul> <p>If you’re still relying heavily on <code>useEffect</code> for data fetching, I highly recommend trying SWR and its <code>mutate</code> API.</p> <p>You might never look back 🚀</p> <p><a href="proxy.php?url=https://swr.vercel.app/" rel="noopener noreferrer">https://swr.vercel.app/</a></p> javascript react webdev programming 🚀 My Experience Switching from VS Code to Zed Ferry Ananda Febian Sun, 19 Oct 2025 07:57:18 +0000 https://dev.to/ferryops/my-experience-switching-from-vs-code-to-zed-183d https://dev.to/ferryops/my-experience-switching-from-vs-code-to-zed-183d <p>Over the past few weeks, I’ve been experimenting with a new IDE <strong><a href="proxy.php?url=https://zed.dev" rel="noopener noreferrer">Zed</a></strong> after years of using <strong><a href="proxy.php?url=https://code.visualstudio.com" rel="noopener noreferrer">Visual Studio Code</a></strong>. And honestly, I was surprised by how different (in a good way) the experience felt.</p> <p>I’ve relied on VS Code for so long that I thought it would be hard for any other IDE to compete. But after spending some time with Zed, I can see why so many developers are starting to talk about it.</p> <p><a href="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl9f5iyzte6hcs2m5ubbo.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl9f5iyzte6hcs2m5ubbo.png" alt="Screenshot ZED" width="800" height="500"></a></p> <h4> ✨ Minimal and Distraction-Free </h4> <p>Zed feels <strong>incredibly minimalistic</strong>. The interface is clean, distraction-free, and helps me stay focused on writing code not managing a sea of tabs or extensions.</p> <h4> ⚡ Lightning-Fast Performance </h4> <p>The next thing I noticed was <strong>speed</strong>. Everything just feels snappy file navigation, search, autocomplete all happen almost instantly. Even with heavier projects, Zed maintains smooth performance where VS Code sometimes slows down with too many extensions.</p> <h4> 🧠 Built-in MCP Integration </h4> <p>What impressed me the most is that Zed comes with <strong>built-in MCP (Model Context Protocol)</strong> support. This allows seamless AI integration directly inside the IDE with real context from your code without needing extra setup or third-party extensions like in VS Code.</p> <h4> 🎯 Final Thoughts </h4> <p>VS Code is still an amazing editor with its massive ecosystem and community. But if you’re looking for something that’s <strong>lighter, faster, and helps you stay in the flow</strong>, Zed is absolutely worth a try.</p> <p>Sometimes, the right tools don’t just make you more productive they make coding enjoyable again. 💡</p> <p><a href="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5ofiqdj5tnc27pkn96te.gif" class="article-body-image-wrapper"><img src="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5ofiqdj5tnc27pkn96te.gif" alt="GIF of Preview ZED" width="600" height="375"></a></p> <p>Have you tried Zed yet? Would love to hear what you think!</p> vscode zed developertools productivity 📷Building a Face Recognition Attendance System with Next.js, TypeScript, face-api.js, and Supabase Ferry Ananda Febian Wed, 15 Oct 2025 04:36:16 +0000 https://dev.to/ferryops/building-a-face-recognition-attendance-system-with-nextjs-typescript-face-apijs-and-supabase-41jp https://dev.to/ferryops/building-a-face-recognition-attendance-system-with-nextjs-typescript-face-apijs-and-supabase-41jp <p>Hi, Devs! 👋</p> <p>In this article, I want to share my experience building a <strong>modern attendance system</strong> that leverages <strong>face recognition technology</strong> on the web. This project was developed using <strong>Next.js</strong> (a React framework), <strong>TypeScript</strong>, <strong>face-api.js</strong> for face detection, and <strong>Supabase</strong> for backend and data storage.</p> <p>I designed this system to be easily integrated into various digital attendance needs whether for offices, schools, or communities with a modern user experience and seamless verification process.</p> <h2> 🔍 Background </h2> <p>Manual attendance especially with signatures or conventional fingerprint scanners can be slow and even lead to proxy attendance issues. I saw an opportunity for face recognition to solve these problems, providing a faster, more accurate, and contactless attendance experience.</p> <h2> ⚙️ Tech Stack &amp; Workflow </h2> <p>This project is powered by several key technologies:</p> <ul> <li> <strong>Next.js</strong>: For building fast, scalable, SEO-friendly web apps.</li> <li> <strong>TypeScript</strong>: For structured development and fewer bugs.</li> <li> <strong>face-api.js</strong>: A lightweight JavaScript library for in-browser face detection and recognition.</li> <li> <strong>Supabase</strong>: Backend as a Service supporting auth, database, and storage no need to build your own backend from scratch.</li> </ul> <h3> How It Works (Overview): </h3> <ol> <li><p><strong>User face registration</strong><br> Users simply face their device camera. The system captures their face and stores the <em>face descriptor</em> in Supabase.</p></li> <li><p><strong>Attendance with face recognition</strong><br> For check-in, users only need to open the attendance page. The system automatically detects the face, matches it against saved data, and logs attendance if there’s a match.</p></li> <li><p><strong>Data storage &amp; querying</strong><br> Attendance records and user info are stored neatly in Supabase (PostgreSQL + Storage), making it easy to access for both internal dashboards and further analytics.</p></li> </ol> <h2> ✨ Key Features </h2> <ul> <li> <strong>Live Face Recognition:</strong> Check-in with your face, no need for manual logins.</li> <li> <strong>Real-time Feedback:</strong> The system instantly notifies whether the face is recognized.</li> <li> <strong>Lightweight Dashboard:</strong> Admins can view attendance history and user data through a simple web dashboard.</li> <li> <strong>Data Security:</strong> Raw face photos are never stored only encrypted face descriptors.</li> <li> <strong>Cross-Device:</strong> Works from desktop or mobile browsers.</li> </ul> <h2> 🚀 Why face-api.js &amp; Supabase? </h2> <ul> <li><p><strong>face-api.js</strong><br> This library is battle-tested, delivers solid performance for attendance use cases, and runs entirely client-side (in the browser), so there’s no need for a dedicated inference server and user privacy is enhanced.</p></li> <li><p><strong>Supabase</strong><br> Offers a complete stack for authentication, data, and storage, all ready-to-use. Integration is smooth, documentation is developer-friendly, and features like row-level security keep user data private.</p></li> </ul> <h2> 🛠️ Quick Glimpse at the Implementation </h2> <p>The app is modular by design. Here’s an overview of the main architecture and flow:</p> <ol> <li><p><strong>Frontend:</strong><br> Built with Next.js and face-api.js, users can scan their face directly in the browser.</p></li> <li><p><strong>Auth &amp; Data:</strong><br> User authentication and attendance data management are handled via Supabase Auth and tables.</p></li> <li><p><strong>Face Recognition Integration:</strong><br> face-api.js performs live face detection and compares the captured descriptor with those in Supabase. If there’s a match, attendance is automatically recorded.</p></li> <li><p><strong>Dashboard:</strong><br> Admins can view and download attendance records or manage users through a simple UI.</p></li> </ol> <h2> 💡 Challenges &amp; Solutions </h2> <ul> <li><p><strong>Face Detection Accuracy:</strong><br> The main challenge is optimizing lighting and face positioning for accurate detection. The app provides real-time guidance and tips during the scan.</p></li> <li><p><strong>Data Privacy:</strong><br> Only face descriptors are stored (never raw photos), ensuring stronger privacy for users.</p></li> <li><p><strong>Cross-Platform Compatibility:</strong><br> In-browser face recognition can be tricky across devices. By using face-api.js, I was able to optimize performance and provide fallback options for unsupported devices.</p></li> </ul> <h2> 🎯 Who Is This Project For? </h2> <ul> <li> <strong>Developers</strong> interested in face recognition integration in modern web apps.</li> <li> <strong>Startups/Companies</strong> needing fast, efficient, contactless attendance solutions.</li> <li> <strong>Communities/Organizations</strong> wanting a plug-and-play attendance system just a browser and webcam needed.</li> </ul> <h2> 🧑‍💻 Example Code Snippets </h2> <h3> 1. <strong>Initializing face-api.js in a Next.js Component</strong> </h3> <div class="highlight js-code-highlight"> <pre class="highlight typescript"><code><span class="k">import</span> <span class="p">{</span> <span class="nx">useEffect</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">react</span><span class="dl">'</span><span class="p">;</span> <span class="k">import</span> <span class="o">*</span> <span class="k">as</span> <span class="nx">faceapi</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">face-api.js</span><span class="dl">'</span><span class="p">;</span> <span class="k">export</span> <span class="k">default</span> <span class="kd">function</span> <span class="nf">FaceScanner</span><span class="p">()</span> <span class="p">{</span> <span class="nf">useEffect</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">loadModels</span> <span class="o">=</span> <span class="k">async </span><span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="k">await</span> <span class="nx">faceapi</span><span class="p">.</span><span class="nx">nets</span><span class="p">.</span><span class="nx">tinyFaceDetector</span><span class="p">.</span><span class="nf">loadFromUri</span><span class="p">(</span><span class="dl">'</span><span class="s1">/models</span><span class="dl">'</span><span class="p">);</span> <span class="k">await</span> <span class="nx">faceapi</span><span class="p">.</span><span class="nx">nets</span><span class="p">.</span><span class="nx">faceRecognitionNet</span><span class="p">.</span><span class="nf">loadFromUri</span><span class="p">(</span><span class="dl">'</span><span class="s1">/models</span><span class="dl">'</span><span class="p">);</span> <span class="k">await</span> <span class="nx">faceapi</span><span class="p">.</span><span class="nx">nets</span><span class="p">.</span><span class="nx">faceLandmark68Net</span><span class="p">.</span><span class="nf">loadFromUri</span><span class="p">(</span><span class="dl">'</span><span class="s1">/models</span><span class="dl">'</span><span class="p">);</span> <span class="p">};</span> <span class="nf">loadModels</span><span class="p">();</span> <span class="p">},</span> <span class="p">[]);</span> <span class="k">return </span><span class="p">(</span> <span class="o">&lt;</span><span class="nx">div</span><span class="o">&gt;</span> <span class="p">{</span><span class="cm">/* ... Camera &amp; UI elements here */</span><span class="p">}</span> <span class="o">&lt;</span><span class="sr">/div</span><span class="err">&gt; </span> <span class="p">);</span> <span class="p">}</span> </code></pre> </div> <h3> 2. <strong>Capturing and Generating a Face Descriptor</strong> </h3> <div class="highlight js-code-highlight"> <pre class="highlight typescript"><code><span class="k">async</span> <span class="kd">function</span> <span class="nf">handleCapture</span><span class="p">()</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">video</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nf">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">videoElement</span><span class="dl">'</span><span class="p">)</span> <span class="k">as</span> <span class="nx">HTMLVideoElement</span><span class="p">;</span> <span class="kd">const</span> <span class="nx">detection</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">faceapi</span> <span class="p">.</span><span class="nf">detectSingleFace</span><span class="p">(</span><span class="nx">video</span><span class="p">,</span> <span class="k">new</span> <span class="nx">faceapi</span><span class="p">.</span><span class="nc">TinyFaceDetectorOptions</span><span class="p">())</span> <span class="p">.</span><span class="nf">withFaceLandmarks</span><span class="p">()</span> <span class="p">.</span><span class="nf">withFaceDescriptor</span><span class="p">();</span> <span class="k">if </span><span class="p">(</span><span class="nx">detection</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// The descriptor is saved to Supabase</span> <span class="kd">const</span> <span class="p">{</span> <span class="nx">descriptor</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">detection</span><span class="p">;</span> <span class="k">await</span> <span class="nf">saveFaceDescriptor</span><span class="p">(</span><span class="nx">descriptor</span><span class="p">);</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="nf">alert</span><span class="p">(</span><span class="dl">'</span><span class="s1">Face not detected, please try again.</span><span class="dl">'</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <h3> 3. <strong>Saving a Face Descriptor to Supabase</strong> </h3> <div class="highlight js-code-highlight"> <pre class="highlight typescript"><code><span class="k">import</span> <span class="p">{</span> <span class="nx">createClient</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@supabase/supabase-js</span><span class="dl">'</span><span class="p">;</span> <span class="kd">const</span> <span class="nx">supabase</span> <span class="o">=</span> <span class="nf">createClient</span><span class="p">(</span> <span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">NEXT_PUBLIC_SUPABASE_URL</span><span class="o">!</span><span class="p">,</span> <span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">NEXT_PUBLIC_SUPABASE_KEY</span><span class="o">!</span> <span class="p">);</span> <span class="k">async</span> <span class="kd">function</span> <span class="nf">saveFaceDescriptor</span><span class="p">(</span><span class="nx">descriptor</span><span class="p">:</span> <span class="nb">Float32Array</span><span class="p">)</span> <span class="p">{</span> <span class="kd">const</span> <span class="p">{</span> <span class="nx">data</span><span class="p">,</span> <span class="nx">error</span> <span class="p">}</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">supabase</span> <span class="p">.</span><span class="k">from</span><span class="p">(</span><span class="dl">'</span><span class="s1">users</span><span class="dl">'</span><span class="p">)</span> <span class="p">.</span><span class="nf">update</span><span class="p">({</span> <span class="na">face_descriptor</span><span class="p">:</span> <span class="nb">Array</span><span class="p">.</span><span class="k">from</span><span class="p">(</span><span class="nx">descriptor</span><span class="p">)</span> <span class="p">})</span> <span class="p">.</span><span class="nf">eq</span><span class="p">(</span><span class="dl">'</span><span class="s1">id</span><span class="dl">'</span><span class="p">,</span> <span class="nx">userId</span><span class="p">);</span> <span class="k">if </span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="k">throw</span> <span class="nx">error</span><span class="p">;</span> <span class="k">return</span> <span class="nx">data</span><span class="p">;</span> <span class="p">}</span> </code></pre> </div> <h3> 4. <strong>Attendance Check: Face Matching Logic</strong> </h3> <div class="highlight js-code-highlight"> <pre class="highlight typescript"><code><span class="kd">function</span> <span class="nf">isMatch</span><span class="p">(</span><span class="nx">inputDescriptor</span><span class="p">:</span> <span class="nb">Float32Array</span><span class="p">,</span> <span class="nx">storedDescriptor</span><span class="p">:</span> <span class="kr">number</span><span class="p">[]):</span> <span class="nx">boolean</span> <span class="p">{</span> <span class="c1">// face-api.js has a helper for euclideanDistance</span> <span class="kd">const</span> <span class="nx">distance</span> <span class="o">=</span> <span class="nx">faceapi</span><span class="p">.</span><span class="nf">euclideanDistance</span><span class="p">(</span><span class="nx">inputDescriptor</span><span class="p">,</span> <span class="k">new</span> <span class="nc">Float32Array</span><span class="p">(</span><span class="nx">storedDescriptor</span><span class="p">));</span> <span class="c1">// Threshold can be adjusted (e.g., 0.5)</span> <span class="k">return</span> <span class="nx">distance</span> <span class="o">&lt;</span> <span class="mf">0.5</span><span class="p">;</span> <span class="p">}</span> </code></pre> </div> <h3> 5. <strong>Querying Attendance Data from Supabase</strong> </h3> <div class="highlight js-code-highlight"> <pre class="highlight typescript"><code><span class="k">async</span> <span class="kd">function</span> <span class="nf">fetchAttendanceHistory</span><span class="p">(</span><span class="nx">userId</span><span class="p">:</span> <span class="kr">string</span><span class="p">)</span> <span class="p">{</span> <span class="kd">const</span> <span class="p">{</span> <span class="nx">data</span><span class="p">,</span> <span class="nx">error</span> <span class="p">}</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">supabase</span> <span class="p">.</span><span class="k">from</span><span class="p">(</span><span class="dl">'</span><span class="s1">attendance</span><span class="dl">'</span><span class="p">)</span> <span class="p">.</span><span class="nf">select</span><span class="p">(</span><span class="dl">'</span><span class="s1">*</span><span class="dl">'</span><span class="p">)</span> <span class="p">.</span><span class="nf">eq</span><span class="p">(</span><span class="dl">'</span><span class="s1">user_id</span><span class="dl">'</span><span class="p">,</span> <span class="nx">userId</span><span class="p">)</span> <span class="p">.</span><span class="nf">order</span><span class="p">(</span><span class="dl">'</span><span class="s1">created_at</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span> <span class="na">ascending</span><span class="p">:</span> <span class="kc">false</span> <span class="p">});</span> <span class="k">if </span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="k">throw</span> <span class="nx">error</span><span class="p">;</span> <span class="k">return</span> <span class="nx">data</span><span class="p">;</span> <span class="p">}</span> </code></pre> </div> <h2> 🔗 Demo &amp; Preview Video </h2> <p>Want to see it in action?<br> Check out my short preview video showcasing the app’s features and workflow:</p> <p><a href="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmo2oi1efs45isidqfx20.gif" class="article-body-image-wrapper"><img src="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmo2oi1efs45isidqfx20.gif" alt="Demo apps" width="400" height="250"></a><br> <em>Short Preview Video Showcasing</em></p> <p><a href="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmlk38jze41789ur6rfku.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmlk38jze41789ur6rfku.png" alt="Preview Reports" width="800" height="474"></a><br> <em>Preview Reports</em></p> <p>Thanks for reading!<br> If you have questions or feedback, feel free to drop a comment below.<br> Happy coding &amp; stay creative! 🚀</p> <p>Source Code: <a href="proxy.php?url=https://github.com/ferryops/facial-recognition-attendance-system" rel="noopener noreferrer">https://github.com/ferryops/facial-recognition-attendance-system</a></p> facedetection nextjs typescript supabase [Boost] Ferry Ananda Febian Mon, 08 Sep 2025 02:35:59 +0000 https://dev.to/ferryops/-3c09 https://dev.to/ferryops/-3c09 <div class="ltag__link"> <a href="proxy.php?url=/ferryops" class="ltag__link__link"> <div class="ltag__link__pic"> <img src="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1093075%2Fc9aa0c9a-d1e2-4486-9114-d191b3d7ddcf.jpeg" alt="ferryops"> </div> </a> <a href="proxy.php?url=https://dev.to/ferryops/from-2-5-minutes-to-1-second-how-a-small-nginx-config-change-boosted-my-website-4h8j" class="ltag__link__link"> <div class="ltag__link__content"> <h2>🚀 From 2–5 Minutes to &lt; 1 Second: How a Small Nginx Config Change Boosted My Website</h2> <h3>Ferry Ananda Febian ・ Sep 8 '25</h3> <div class="ltag__link__taglist"> <span class="ltag__link__tag">#nginx</span> <span class="ltag__link__tag">#webdev</span> <span class="ltag__link__tag">#backend</span> <span class="ltag__link__tag">#programming</span> </div> </div> </a> </div> nginx webdev backend programming 🚀 From 2–5 Minutes to < 1 Second: How a Small Nginx Config Change Boosted My Website Ferry Ananda Febian Mon, 08 Sep 2025 00:03:57 +0000 https://dev.to/ferryops/from-2-5-minutes-to-1-second-how-a-small-nginx-config-change-boosted-my-website-4h8j https://dev.to/ferryops/from-2-5-minutes-to-1-second-how-a-small-nginx-config-change-boosted-my-website-4h8j <p>A few days ago, I was puzzled about why the website I built felt extremely slow when loading a JavaScript file.</p> <p>Imagine this: a file of just 2 MB took 2–5 minutes to load in the browser. 😅</p> <p>After digging into it, the issue turned out to be simple: I forgot to enable gzip in my Nginx configuration.</p> <h2> What is gzip? </h2> <p>gzip is a compression method that allows the server to send smaller-sized files to the browser.</p> <p>Instead of downloading the full raw file, the browser only needs to fetch the compressed version.</p> <h2> The Fix </h2> <p>I added the following config to my Nginx Proxy Manager:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight nginx"><code><span class="k">gzip</span> <span class="no">on</span><span class="p">;</span> <span class="k">gzip_comp_level</span> <span class="mi">6</span><span class="p">;</span> <span class="k">gzip_min_length</span> <span class="mi">256</span><span class="p">;</span> <span class="k">gzip_proxied</span> <span class="s">any</span><span class="p">;</span> <span class="k">gzip_vary</span> <span class="no">on</span><span class="p">;</span> <span class="k">gzip_types</span> <span class="nc">text/plain</span> <span class="nc">text/css</span> <span class="nc">application/json</span> <span class="nc">application/javascript</span> <span class="nc">application/x-javascript</span> <span class="nc">text/javascript</span> <span class="nc">application/xml</span> <span class="nc">application/rss</span><span class="s">+xml</span> <span class="nc">application/atom</span><span class="s">+xml</span> <span class="nc">application/vnd</span><span class="s">.ms-fontobject</span> <span class="nc">application/x-font-ttf</span> <span class="nc">font/opentype</span> <span class="nc">image/svg</span><span class="s">+xml</span> <span class="nc">image/x-icon</span><span class="p">;</span> </code></pre> </div> <h2> The Result </h2> <p>The website performance improved instantly:<br> From 2–5 minutes → to less than 1 second for a 2 MB file.</p> <h2> Key Takeaways </h2> <ul> <li>Small optimizations can lead to massive improvements.</li> <li>Never underestimate basic server configuration.</li> <li>A single setting can completely transform user experience.</li> </ul> <p>Sometimes it’s not expensive hardware or the latest framework that makes a website faster, but rather a simple config tweak like this.</p> <p>So, if your website feels slow, it might be worth checking whether gzip is enabled on your server.</p> <p>Have you ever found a performance bottleneck where the solution turned out to be this simple?</p> nginx webdev backend programming [Boost] Ferry Ananda Febian Tue, 19 Aug 2025 05:57:55 +0000 https://dev.to/ferryops/-3jck https://dev.to/ferryops/-3jck <div class="ltag__link"> <a href="proxy.php?url=/ferryops" class="ltag__link__link"> <div class="ltag__link__pic"> <img src="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1093075%2Fc9aa0c9a-d1e2-4486-9114-d191b3d7ddcf.jpeg" alt="ferryops"> </div> </a> <a href="proxy.php?url=https://dev.to/ferryops/monolith-vs-microservices-kenapa-sebaiknya-mulai-dari-monolith-5bi7" class="ltag__link__link"> <div class="ltag__link__content"> <h2>Monolith vs Microservices: Kenapa Sebaiknya Mulai dari Monolith?</h2> <h3>Ferry Ananda Febian ・ Aug 19 '25</h3> <div class="ltag__link__taglist"> <span class="ltag__link__tag">#programming</span> <span class="ltag__link__tag">#webdev</span> <span class="ltag__link__tag">#architecture</span> <span class="ltag__link__tag">#indonesia</span> </div> </div> </a> </div> programming webdev architecture indonesia Monolith vs Microservices: Kenapa Sebaiknya Mulai dari Monolith? Ferry Ananda Febian Tue, 19 Aug 2025 01:14:55 +0000 https://dev.to/ferryops/monolith-vs-microservices-kenapa-sebaiknya-mulai-dari-monolith-5bi7 https://dev.to/ferryops/monolith-vs-microservices-kenapa-sebaiknya-mulai-dari-monolith-5bi7 <p><a href="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwopxnd4q4kwle1rcaqkq.jpg" class="article-body-image-wrapper"><img src="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwopxnd4q4kwle1rcaqkq.jpg" alt=" " width="800" height="418"></a>Dalam dunia pengembangan software, dua arsitektur yang paling sering dibandingkan adalah <strong>monolith</strong> dan <strong>microservices</strong>. Keduanya memiliki kelebihan dan kekurangan masing-masing, tetapi menurut saya ada satu prinsip penting yang sering dilupakan: <strong>setiap project cukup dimulai dengan monolith</strong>.</p> <h2> Apa Itu Monolith dan Microservices? </h2> <ul> <li> <strong>Monolith</strong> adalah arsitektur di mana seluruh aplikasi dibangun sebagai satu kesatuan utuh. Semua fitur, logika bisnis, dan database biasanya terhubung dalam satu codebase.</li> <li> <strong>Microservices</strong> adalah pendekatan di mana aplikasi dipecah menjadi layanan-layanan kecil yang independen, masing-masing bisa dikembangkan, dideploy, dan diskalakan secara terpisah.</li> </ul> <h2> Daya Tarik Microservices </h2> <p>Microservices memang terdengar modern dan keren. Banyak perusahaan besar seperti Netflix, Amazon, atau Gojek menggunakannya. Skalabilitas yang fleksibel, tim yang bisa bekerja paralel, serta kebebasan memilih teknologi per layanan sering dijadikan alasan untuk mengadopsinya sejak awal.</p> <p>Namun, ada sisi lain yang jarang dibicarakan: yaitu <strong>kompleksitas</strong>. Microservices butuh orkestrasi yang rumit, monitoring, logging, komunikasi antar-service, hingga pengelolaan database terdistribusi. Hal-hal ini bisa menjadi beban besar untuk project yang masih kecil atau baru dimulai.</p> <h2> Kenapa Sebaiknya Mulai dari Monolith? </h2> <ol> <li><p><strong>Lebih Cepat dalam Validasi Ide</strong><br> Di tahap awal, fokus utama bukanlah skala, melainkan <em>apakah produk kita dipakai orang atau tidak</em>. Monolith memungkinkan pengembangan lebih cepat karena tidak perlu memikirkan pembagian service, deployment, dan integrasi.</p></li> <li><p><strong>Lebih Mudah Dikelola Tim Kecil</strong><br> Startup atau project baru biasanya dimulai dengan tim kecil. Monolith lebih mudah dipahami karena semua kode ada dalam satu tempat. Tidak ada overhead komunikasi antar-service yang bisa memperlambat proses belajar tim.</p></li> <li><p><strong>Lebih Murah secara Infrastruktur</strong><br> Microservices sering kali butuh banyak server, container, dan sistem pendukung. Sementara monolith bisa jalan di satu mesin atau instance kecil. Dengan begitu, biaya awal jauh lebih rendah.</p></li> <li><p><strong>Refactor ke Microservices Bisa Datang Belakangan</strong><br> Jika nantinya produk tumbuh dan memang benar-benar butuh skalabilitas lebih, monolith tetap bisa di-<em>split</em> menjadi microservices secara bertahap. Banyak perusahaan besar pun awalnya lahir sebagai monolith sebelum berevolusi.</p></li> </ol> <h2> Microservices Bukan Musuh </h2> <p>Bukan berarti microservices buruk. Untuk aplikasi dengan traffic sangat besar, tim besar, atau kebutuhan integrasi yang kompleks, microservices bisa jadi solusi terbaik. Tetapi memulai dari awal justru bisa memperlambat perjalanan menuju <em>product-market fit</em>.</p> <h2> Kesimpulan </h2> <p>Membangun software itu mirip dengan membangun rumah. Kita tidak langsung memikirkan bagaimana jika rumah ini jadi hotel 100 lantai, yang penting adalah apakah rumahnya nyaman ditinggali dulu.</p> <p>Karena itu, menurut saya:<br> 👉 <strong>Setiap project sebaiknya dimulai dengan monolith.</strong><br> Sederhana, cepat, dan hemat. Kalau nanti skalanya memang menuntut, selalu ada jalan untuk berevolusi ke microservices.</p> programming webdev architecture indonesia Tambah Fitur di Aplikasi Malah Bikin Bagian Lain Error? Ferry Ananda Febian Tue, 12 Aug 2025 02:30:04 +0000 https://dev.to/ferryops/tambah-fitur-di-aplikasi-malah-bikin-bagian-lain-error-50hn https://dev.to/ferryops/tambah-fitur-di-aplikasi-malah-bikin-bagian-lain-error-50hn <p>Pernah nggak ngalamin, mau nambah satu fitur di aplikasi, eh tiba-tiba bagian lain ikut error? 😅 Rasanya kayak narik satu kabel di meja, terus semua kabel lain ikut ketarik.</p> <p>Semakin gede proyeknya, biasanya makin rawan efek domino begini. Ujung-ujungnya, kerjaan nambah fitur malah jadi kerjaan padamkan kebakaran.</p> <p>Nah, salah satu “jurus” biar kode nggak gampang berantakan adalah pakai SOLID Principles. Berikut contoh nya untuk project React + Vite:</p> <h3> 1. S — Single Responsibility </h3> <p>Satu file, satu tugas.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight jsx"><code><span class="c1">// services/userService.js</span> <span class="k">export</span> <span class="kd">const</span> <span class="nx">fetchUsers</span> <span class="o">=</span> <span class="k">async </span><span class="p">()</span> <span class="o">=&gt;</span> <span class="nf">fetch</span><span class="p">(</span><span class="dl">"</span><span class="s2">/api/users</span><span class="dl">"</span><span class="p">).</span><span class="nf">then</span><span class="p">(</span><span class="nx">r</span> <span class="o">=&gt;</span> <span class="nx">r</span><span class="p">.</span><span class="nf">json</span><span class="p">());</span> <span class="c1">// pages/Dashboard.jsx</span> <span class="k">import</span> <span class="p">{</span> <span class="nx">fetchUsers</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">../services/userService</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="nx">UserList</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">../components/UserList</span><span class="dl">"</span><span class="p">;</span> </code></pre> </div> <h3> 2. O — Open/Closed </h3> <p>Bisa nambah fitur tanpa bongkar yang lama.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight jsx"><code><span class="k">export</span> <span class="k">default</span> <span class="kd">function</span> <span class="nf">Button</span><span class="p">({</span> <span class="nx">type</span><span class="o">=</span><span class="dl">"</span><span class="s2">primary</span><span class="dl">"</span><span class="p">,</span> <span class="nx">children</span> <span class="p">})</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">styles</span> <span class="o">=</span> <span class="p">{</span> <span class="na">primary</span><span class="p">:</span> <span class="dl">"</span><span class="s2">bg-blue-500</span><span class="dl">"</span><span class="p">,</span> <span class="na">secondary</span><span class="p">:</span> <span class="dl">"</span><span class="s2">bg-gray-200</span><span class="dl">"</span> <span class="p">};</span> <span class="k">return</span> <span class="p">&lt;</span><span class="nt">button</span> <span class="na">className</span><span class="p">=</span><span class="si">{</span><span class="nx">styles</span><span class="p">[</span><span class="nx">type</span><span class="p">]</span><span class="si">}</span><span class="p">&gt;</span><span class="si">{</span><span class="nx">children</span><span class="si">}</span><span class="p">&lt;/</span><span class="nt">button</span><span class="p">&gt;;</span> <span class="p">}</span> </code></pre> </div> <h3> 3. L — Liskov Substitution </h3> <p>Komponen turunan harus bisa gantiin induknya.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight javascript"><code><span class="kd">function</span> <span class="nf">BarChart</span><span class="p">(</span><span class="nx">props</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="o">&lt;</span><span class="nx">BaseChart</span> <span class="p">{...</span><span class="nx">props</span><span class="p">}</span> <span class="sr">/&gt;;</span><span class="err">} </span></code></pre> </div> <h3> 4. I — Interface Segregation </h3> <p>Props sesuai kebutuhan, jangan kebanyakan fungsi yang nggak dipakai.</p> <h3> 5. D — Dependency Inversion </h3> <p>Bergantung ke service, bukan langsung ke API.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight javascript"><code><span class="c1">// services/productService.js</span> <span class="k">export</span> <span class="kd">const</span> <span class="nx">getProducts</span> <span class="o">=</span> <span class="k">async </span><span class="p">()</span> <span class="o">=&gt;</span> <span class="nf">fetch</span><span class="p">(</span><span class="dl">"</span><span class="s2">/api/products</span><span class="dl">"</span><span class="p">).</span><span class="nf">then</span><span class="p">(</span><span class="nx">r</span> <span class="o">=&gt;</span> <span class="nx">r</span><span class="p">.</span><span class="nf">json</span><span class="p">());</span> </code></pre> </div> <p>Kalau prinsip ini dipakai, kode jadi lebih rapi, gampang dikembangin, dan nggak gampang bikin pusing kepala. Proyek pun bisa tumbuh tanpa bikin programmernya stres. Dan yang paling penting… kita masih bisa ngopi santai sambil lihat build sukses tanpa drama. ☕🚀</p> <p>SOLID versi politik 🤭 <br> <iframe width="710" height="399" src="proxy.php?url=https://www.youtube.com/embed/HssE5iieKv4?start=40"> </iframe> </p> programming developers indonesia 🏃Pakai Tanstack Query, Waktunya Meninggalkan useState dan useEffect Ferry Ananda Febian Wed, 07 May 2025 00:03:52 +0000 https://dev.to/ferryops/pakai-tanstack-query-waktunya-meninggalkan-usestate-dan-useeffect-59o7 https://dev.to/ferryops/pakai-tanstack-query-waktunya-meninggalkan-usestate-dan-useeffect-59o7 <p>Dalam dunia React, salah satu pola yang paling sering kita jumpai ketika berinteraksi dengan API adalah kombinasi <code>useState</code> dan <code>useEffect</code>. Namun, dengan hadirnya <strong>TanStack Query (dulu dikenal sebagai React Query)</strong>, pendekatan tersebut kini terasa kuno. TanStack Query menawarkan cara yang lebih deklaratif, efisien, dan mudah untuk melakukan fetching, caching, dan updating data async.</p> <h2> Kenapa Harus Meninggalkan <code>useState</code> dan <code>useEffect</code>? </h2> <p>Biasanya, untuk mengambil data dari API, kita menulis kode seperti ini:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight jsx"><code><span class="kd">const</span> <span class="p">[</span><span class="nx">data</span><span class="p">,</span> <span class="nx">setData</span><span class="p">]</span> <span class="o">=</span> <span class="nf">useState</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span> <span class="kd">const</span> <span class="p">[</span><span class="nx">loading</span><span class="p">,</span> <span class="nx">setLoading</span><span class="p">]</span> <span class="o">=</span> <span class="nf">useState</span><span class="p">(</span><span class="kc">true</span><span class="p">);</span> <span class="kd">const</span> <span class="p">[</span><span class="nx">error</span><span class="p">,</span> <span class="nx">setError</span><span class="p">]</span> <span class="o">=</span> <span class="nf">useState</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span> <span class="nf">useEffect</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nf">fetch</span><span class="p">(</span><span class="dl">'</span><span class="s1">/api/data</span><span class="dl">'</span><span class="p">)</span> <span class="p">.</span><span class="nf">then</span><span class="p">((</span><span class="nx">res</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">res</span><span class="p">.</span><span class="nf">json</span><span class="p">())</span> <span class="p">.</span><span class="nf">then</span><span class="p">(</span><span class="nx">setData</span><span class="p">)</span> <span class="p">.</span><span class="k">catch</span><span class="p">(</span><span class="nx">setError</span><span class="p">)</span> <span class="p">.</span><span class="k">finally</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="nf">setLoading</span><span class="p">(</span><span class="kc">false</span><span class="p">));</span> <span class="p">},</span> <span class="p">[]);</span> </code></pre> </div> <p>Kode ini <em>berfungsi</em>, tapi repetitif dan rawan kesalahan, terutama jika kita ingin menambahkan fitur seperti <strong>refetch otomatis</strong>, <strong>caching</strong>, <strong>stale data control</strong>, dan <strong>synchronization antar komponen</strong>.</p> <p>Dengan TanStack Query, kita bisa menyederhanakannya menjadi:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight jsx"><code><span class="k">import</span> <span class="p">{</span> <span class="nx">useQuery</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@tanstack/react-query</span><span class="dl">'</span><span class="p">;</span> <span class="kd">const</span> <span class="nx">fetchData</span> <span class="o">=</span> <span class="k">async </span><span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">res</span> <span class="o">=</span> <span class="k">await</span> <span class="nf">fetch</span><span class="p">(</span><span class="dl">'</span><span class="s1">/api/data</span><span class="dl">'</span><span class="p">);</span> <span class="k">if </span><span class="p">(</span><span class="o">!</span><span class="nx">res</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="k">throw</span> <span class="k">new</span> <span class="nc">Error</span><span class="p">(</span><span class="dl">'</span><span class="s1">Network response was not ok</span><span class="dl">'</span><span class="p">);</span> <span class="k">return</span> <span class="nx">res</span><span class="p">.</span><span class="nf">json</span><span class="p">();</span> <span class="p">};</span> <span class="kd">function</span> <span class="nf">MyComponent</span><span class="p">()</span> <span class="p">{</span> <span class="kd">const</span> <span class="p">{</span> <span class="nx">data</span><span class="p">,</span> <span class="nx">error</span><span class="p">,</span> <span class="nx">isLoading</span> <span class="p">}</span> <span class="o">=</span> <span class="nf">useQuery</span><span class="p">({</span> <span class="na">queryKey</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">myData</span><span class="dl">'</span><span class="p">],</span> <span class="na">queryFn</span><span class="p">:</span> <span class="nx">fetchData</span> <span class="p">});</span> <span class="k">if </span><span class="p">(</span><span class="nx">isLoading</span><span class="p">)</span> <span class="k">return</span> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>Loading...<span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;;</span> <span class="k">if </span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="k">return</span> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>Error: <span class="si">{</span><span class="nx">error</span><span class="p">.</span><span class="nx">message</span><span class="si">}</span><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;;</span> <span class="k">return</span> <span class="p">&lt;</span><span class="nt">pre</span><span class="p">&gt;</span><span class="si">{</span><span class="nx">JSON</span><span class="p">.</span><span class="nf">stringify</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span><span class="si">}</span><span class="p">&lt;/</span><span class="nt">pre</span><span class="p">&gt;;</span> <span class="p">}</span> </code></pre> </div> <p>Dari potongan kode di atas, kita bisa lihat bahwa kode menjadi lebih <em>declarative</em> dan <em>maintainable</em>. TanStack Query mengelola state secara otomatis, dan kita hanya perlu fokus pada fetching data.</p> <h2> Manfaat Utama TanStack Query </h2> <h3> 1. <strong>Caching Otomatis</strong> </h3> <p>TanStack Query menyimpan hasil fetch di cache. Kalau komponen di-<em>unmount</em> dan kemudian di-<em>mount</em> kembali, Tanstack bisa menampilkan data lama dulu sambil mem-<em>fetch</em> yang baru.</p> <h3> 2. <strong>Refetch Otomatis</strong> </h3> <p>TanStack Query secara otomatis bisa melakukan refetch data saat user kembali ke tab browser atau saat koneksi internet pulih. Ini salah satu fitur yang saya sukai.</p> <h3> 3. <strong>Pengelolaan Status Lebih Mudah</strong> </h3> <p>Status <code>isLoading</code>, <code>error</code>, dan <code>data</code> sudah tersedia langsung dari <code>useQuery</code>. Kita tidak perlu lagi membuat banyak <code>useState</code>.</p> <h3> 4. <strong>Shared State Antar Komponen</strong> </h3> <p>Data yang di-<em>fetch</em> oleh satu komponen bisa langsung digunakan oleh komponen lain tanpa refetch, selama memakai <code>queryKey</code> yang sama.</p> <h3> 5. <strong>Devtools</strong> </h3> <p>TanStack Query menyediakan devtools untuk debugging query, melihat cache, dan memantau status request secara visual.</p> <h2> QueryClient dan useQueryClient </h2> <p>Untuk penggunaan lanjutan, kita bisa menggunakan <code>QueryClient</code> untuk mengatur global config, dan <code>useQueryClient</code> untuk mengakses atau memodifikasi cache langsung:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight jsx"><code><span class="k">import</span> <span class="p">{</span> <span class="nx">QueryClient</span><span class="p">,</span> <span class="nx">QueryClientProvider</span><span class="p">,</span> <span class="nx">useQueryClient</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@tanstack/react-query</span><span class="dl">'</span><span class="p">;</span> <span class="kd">const</span> <span class="nx">queryClient</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">QueryClient</span><span class="p">();</span> <span class="kd">function</span> <span class="nf">App</span><span class="p">()</span> <span class="p">{</span> <span class="k">return </span><span class="p">(</span> <span class="p">&lt;</span><span class="nc">QueryClientProvider</span> <span class="na">client</span><span class="p">=</span><span class="si">{</span><span class="nx">queryClient</span><span class="si">}</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nc">MyComponent</span> <span class="p">/&gt;</span> <span class="p">&lt;/</span><span class="nc">QueryClientProvider</span><span class="p">&gt;</span> <span class="p">);</span> <span class="p">}</span> <span class="kd">function</span> <span class="nf">RefetchButton</span><span class="p">()</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">queryClient</span> <span class="o">=</span> <span class="nf">useQueryClient</span><span class="p">();</span> <span class="k">return</span> <span class="p">&lt;</span><span class="nt">button</span> <span class="na">onClick</span><span class="p">=</span><span class="si">{</span><span class="p">()</span> <span class="o">=&gt;</span> <span class="nx">queryClient</span><span class="p">.</span><span class="nf">invalidateQueries</span><span class="p">([</span><span class="dl">'</span><span class="s1">myData</span><span class="dl">'</span><span class="p">])</span><span class="si">}</span><span class="p">&gt;</span>Refresh Data<span class="p">&lt;/</span><span class="nt">button</span><span class="p">&gt;;</span> <span class="p">}</span> </code></pre> </div> <h2> Kesimpulan </h2> <p>TanStack Query adalah jawaban modern untuk manajemen data async di React. Dengan hanya mengganti <code>useState</code> dan <code>useEffect</code> ke <code>useQuery</code>, kita bisa mendapatkan banyak fitur powerful yang biasanya membutuhkan banyak boilerplate. Jadi, kalau teman-teman masih pakai <code>useEffect</code> buat fetching data — mungkin ini saatnya move on.</p> <p>Terima kasih sudah mampir ke blog saya, sampai ketemu di blog berikutnya!</p> react javascript webdev indonesia Ternyata Selama Ini Saya Fetching Data Tidak Efisien di React Ferry Ananda Febian Sat, 22 Mar 2025 05:08:18 +0000 https://dev.to/ferryops/ternyata-selama-ini-saya-fetching-data-tidak-efisien-di-react-57kh https://dev.to/ferryops/ternyata-selama-ini-saya-fetching-data-tidak-efisien-di-react-57kh <p>Lebih dari setahun sudah saya bekerja dengan React, membuat berbagai macam website dan aplikasi, namun akhir-akhir ini saya menyadari bahwa saya belum menggunakan cara yang efisien dalam melakukan fetching data. Saya sering menggunakan <code>axios</code> atau <code>fetch</code> secara manual, tanpa memperhatikan caching, revalidasi, atau optimasi lainnya.</p> <p>Baru-baru ini, saya menemukan sebuah pustaka yang menarik untuk melakukan data fetching di React, yaitu <strong>SWR</strong>. Saya ingin berbagi pengetahuan ini kepada teman-teman, agar juga bisa memanfaatkan SWR untuk membuat aplikasi React yang lebih cepat dan responsif.</p> <p>SWR (Stale-While-Revalidate) adalah pustaka React yang dikembangkan oleh Vercel untuk melakukan data fetching secara efisien. SWR menawarkan solusi yang cepat, otomatis, dan mudah digunakan dalam pengambilan data dari API.</p> <h2> 1. Apa Itu SWR? </h2> <p>SWR menggunakan strategi <strong>Stale-While-Revalidate</strong>, yaitu:</p> <ul> <li>Mengembalikan data dari cache terlebih dahulu (stale)</li> <li>Kemudian melakukan fetch ulang untuk memperbarui data (revalidate)</li> <li>Memperbarui state jika ada perubahan data</li> </ul> <p>Strategi ini membuat aplikasi terasa lebih responsif karena pengguna tidak perlu menunggu data terbaru sebelum melihat konten.</p> <h2> 2. Menggunakan <code>useSWR</code> </h2> <p><code>useSWR</code> adalah hook utama yang digunakan untuk mengambil data. Contoh penggunaannya:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight tsx"><code><span class="k">import</span> <span class="nx">useSWR</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">swr</span><span class="dl">'</span><span class="p">;</span> <span class="kd">const</span> <span class="nx">fetcher</span> <span class="o">=</span> <span class="p">(</span><span class="nx">url</span><span class="p">:</span> <span class="kr">string</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nf">fetch</span><span class="p">(</span><span class="nx">url</span><span class="p">).</span><span class="nf">then</span><span class="p">((</span><span class="nx">res</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">res</span><span class="p">.</span><span class="nf">json</span><span class="p">());</span> <span class="kd">function</span> <span class="nf">MyComponent</span><span class="p">()</span> <span class="p">{</span> <span class="kd">const</span> <span class="p">{</span> <span class="nx">data</span><span class="p">,</span> <span class="nx">error</span><span class="p">,</span> <span class="nx">isLoading</span> <span class="p">}</span> <span class="o">=</span> <span class="nf">useSWR</span><span class="p">(</span><span class="dl">'</span><span class="s1">/api/users</span><span class="dl">'</span><span class="p">,</span> <span class="nx">fetcher</span><span class="p">);</span> <span class="k">if </span><span class="p">(</span><span class="nx">isLoading</span><span class="p">)</span> <span class="k">return</span> <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>Loading...<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;;</span> <span class="k">if </span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="k">return</span> <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>Error loading data<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;;</span> <span class="k">return</span> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span><span class="si">{</span><span class="nx">JSON</span><span class="p">.</span><span class="nf">stringify</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span><span class="si">}</span><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;;</span> <span class="p">}</span> </code></pre> </div> <h2> 3. Menggunakan <code>mutate</code> </h2> <p><code>mutate</code> atau bahasa Indonesia nya mutasi, digunakan untuk memperbarui cache secara manual dan melakukan re-fetching data setelah perubahan. Contoh implementasi:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight tsx"><code><span class="k">import</span> <span class="nx">useSWR</span><span class="p">,</span> <span class="p">{</span> <span class="nx">mutate</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">swr</span><span class="dl">'</span><span class="p">;</span> <span class="kd">const</span> <span class="nx">fetcher</span> <span class="o">=</span> <span class="p">(</span><span class="nx">url</span><span class="p">:</span> <span class="kr">string</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nf">fetch</span><span class="p">(</span><span class="nx">url</span><span class="p">).</span><span class="nf">then</span><span class="p">((</span><span class="nx">res</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">res</span><span class="p">.</span><span class="nf">json</span><span class="p">());</span> <span class="kd">function</span> <span class="nf">MyComponent</span><span class="p">()</span> <span class="p">{</span> <span class="kd">const</span> <span class="p">{</span> <span class="nx">data</span> <span class="p">}</span> <span class="o">=</span> <span class="nf">useSWR</span><span class="p">(</span><span class="dl">'</span><span class="s1">/api/users</span><span class="dl">'</span><span class="p">,</span> <span class="nx">fetcher</span><span class="p">);</span> <span class="kd">const</span> <span class="nx">updateData</span> <span class="o">=</span> <span class="k">async </span><span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="c1">// Memperbarui cache sebelum fetch ulang</span> <span class="nf">mutate</span><span class="p">(</span><span class="dl">'</span><span class="s1">/api/users</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span> <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">John Doe</span><span class="dl">'</span> <span class="p">},</span> <span class="kc">false</span><span class="p">);</span> <span class="c1">// Melakukan re-fetching data</span> <span class="k">await</span> <span class="nf">mutate</span><span class="p">(</span><span class="dl">'</span><span class="s1">/api/users</span><span class="dl">'</span><span class="p">);</span> <span class="p">};</span> <span class="k">return </span><span class="p">(</span> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>Data: <span class="si">{</span><span class="nx">JSON</span><span class="p">.</span><span class="nf">stringify</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span><span class="si">}</span><span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">button</span> <span class="na">onClick</span><span class="p">=</span><span class="si">{</span><span class="nx">updateData</span><span class="si">}</span><span class="p">&gt;</span>Update Data<span class="p">&lt;/</span><span class="nt">button</span><span class="p">&gt;</span> <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span> <span class="p">);</span> <span class="p">}</span> </code></pre> </div> <h2> 4. Kelebihan SWR </h2> <ul> <li><strong>Otomatis caching dan revalidasi</strong></li> <li><strong>Mendukung pagination dan infinite loading</strong></li> <li><strong>Polling otomatis untuk data real-time</strong></li> <li><strong>Optimistic UI updates dengan <code>mutate</code></strong></li> </ul> <p>Cara saya fetching data sebelum menggunakan SWR:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight jsx"><code><span class="k">import</span> <span class="p">{</span> <span class="nx">useState</span><span class="p">,</span> <span class="nx">useEffect</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">react</span><span class="dl">'</span><span class="p">;</span> <span class="kd">function</span> <span class="nf">MyComponent</span><span class="p">()</span> <span class="p">{</span> <span class="kd">const</span> <span class="p">[</span><span class="nx">data</span><span class="p">,</span> <span class="nx">setData</span><span class="p">]</span> <span class="o">=</span> <span class="nf">useState</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span> <span class="kd">const</span> <span class="p">[</span><span class="nx">isLoading</span><span class="p">,</span> <span class="nx">setIsLoading</span><span class="p">]</span> <span class="o">=</span> <span class="nf">useState</span><span class="p">(</span><span class="kc">true</span><span class="p">);</span> <span class="kd">const</span> <span class="p">[</span><span class="nx">error</span><span class="p">,</span> <span class="nx">setError</span><span class="p">]</span> <span class="o">=</span> <span class="nf">useState</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span> <span class="nf">useEffect</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="kd">let</span> <span class="nx">isMounted</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span> <span class="kd">const</span> <span class="nx">fetchData</span> <span class="o">=</span> <span class="k">async </span><span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="k">try</span> <span class="p">{</span> <span class="nf">setIsLoading</span><span class="p">(</span><span class="kc">true</span><span class="p">);</span> <span class="kd">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="k">await</span> <span class="nf">fetch</span><span class="p">(</span><span class="dl">'</span><span class="s1">/api/users</span><span class="dl">'</span><span class="p">);</span> <span class="k">if </span><span class="p">(</span><span class="o">!</span><span class="nx">response</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="p">{</span> <span class="k">throw</span> <span class="k">new</span> <span class="nc">Error</span><span class="p">(</span><span class="dl">'</span><span class="s1">Network response was not ok</span><span class="dl">'</span><span class="p">);</span> <span class="p">}</span> <span class="kd">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">response</span><span class="p">.</span><span class="nf">json</span><span class="p">();</span> <span class="k">if </span><span class="p">(</span><span class="nx">isMounted</span><span class="p">)</span> <span class="p">{</span> <span class="nf">setData</span><span class="p">(</span><span class="nx">result</span><span class="p">);</span> <span class="nf">setIsLoading</span><span class="p">(</span><span class="kc">false</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="k">catch </span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span> <span class="k">if </span><span class="p">(</span><span class="nx">isMounted</span><span class="p">)</span> <span class="p">{</span> <span class="nf">setError</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span> <span class="nf">setIsLoading</span><span class="p">(</span><span class="kc">false</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="p">};</span> <span class="nf">fetchData</span><span class="p">();</span> <span class="k">return </span><span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nx">isMounted</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span> <span class="p">};</span> <span class="p">},</span> <span class="p">[]);</span> <span class="k">if </span><span class="p">(</span><span class="nx">isLoading</span><span class="p">)</span> <span class="k">return</span> <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>Loading...<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;;</span> <span class="k">if </span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="k">return</span> <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>Error loading data: <span class="si">{</span><span class="nx">error</span><span class="p">.</span><span class="nx">message</span><span class="si">}</span><span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;;</span> <span class="k">return</span> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span><span class="si">{</span><span class="nx">JSON</span><span class="p">.</span><span class="nf">stringify</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span><span class="si">}</span><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;;</span> <span class="p">}</span> </code></pre> </div> <p>Setelah implementasi SWR:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight tsx"><code><span class="k">import</span> <span class="nx">useSWR</span><span class="p">,</span> <span class="p">{</span> <span class="nx">mutate</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">swr</span><span class="dl">'</span><span class="p">;</span> <span class="k">import</span> <span class="p">{</span> <span class="nx">useMemo</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">react</span><span class="dl">'</span><span class="p">;</span> <span class="k">import</span> <span class="p">{</span> <span class="nx">fetcher</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">utils/axios</span><span class="dl">'</span><span class="p">;</span> <span class="k">export</span> <span class="kd">type</span> <span class="nx">ModuleType</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">id</span><span class="p">?:</span> <span class="kr">number</span><span class="p">;</span> <span class="nl">label</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span> <span class="nl">name</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span> <span class="nl">allowed_actions</span><span class="p">:</span> <span class="kr">string</span><span class="p">[];</span> <span class="nl">status</span><span class="p">?:</span> <span class="nx">boolean</span><span class="p">;</span> <span class="p">};</span> <span class="kd">type</span> <span class="nx">ModuleList</span> <span class="o">=</span> <span class="nx">ModuleType</span><span class="p">[];</span> <span class="k">export</span> <span class="kd">const</span> <span class="nx">endpoints</span> <span class="o">=</span> <span class="p">{</span> <span class="na">key</span><span class="p">:</span> <span class="dl">'</span><span class="s1">api/v1/modules</span><span class="dl">'</span> <span class="p">};</span> <span class="k">export</span> <span class="kd">function</span> <span class="nf">useGetModules</span><span class="p">(</span><span class="nx">search</span> <span class="o">=</span> <span class="dl">''</span><span class="p">,</span> <span class="nx">page</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="nx">limit</span> <span class="o">=</span> <span class="mi">10</span><span class="p">)</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">key</span> <span class="o">=</span> <span class="s2">`</span><span class="p">${</span><span class="nx">endpoints</span><span class="p">.</span><span class="nx">key</span><span class="p">}</span><span class="s2">?search=</span><span class="p">${</span><span class="nx">search</span><span class="p">}</span><span class="s2">&amp;page=</span><span class="p">${</span><span class="nx">page</span><span class="p">}</span><span class="s2">&amp;limit=</span><span class="p">${</span><span class="nx">limit</span><span class="p">}</span><span class="s2">`</span><span class="p">;</span> <span class="kd">const</span> <span class="p">{</span> <span class="nx">data</span><span class="p">,</span> <span class="nx">error</span> <span class="p">}</span> <span class="o">=</span> <span class="nf">useSWR</span><span class="p">(</span><span class="nx">key</span><span class="p">,</span> <span class="nx">fetcher</span><span class="p">,</span> <span class="p">{</span> <span class="na">revalidateIfStale</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="na">revalidateOnFocus</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="na">revalidateOnReconnect</span><span class="p">:</span> <span class="kc">false</span> <span class="p">});</span> <span class="k">return</span> <span class="nf">useMemo</span><span class="p">(</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">({</span> <span class="na">lists</span><span class="p">:</span> <span class="p">(</span><span class="nx">data</span><span class="p">?.</span><span class="nx">data</span><span class="p">?.</span><span class="nx">modules</span> <span class="k">as</span> <span class="nx">ModuleList</span><span class="p">)</span> <span class="o">??</span> <span class="p">[],</span> <span class="na">totalPages</span><span class="p">:</span> <span class="nx">data</span><span class="p">?.</span><span class="nx">data</span><span class="p">?.</span><span class="nx">total_pages</span> <span class="o">||</span> <span class="mi">1</span><span class="p">,</span> <span class="na">totalItems</span><span class="p">:</span> <span class="nx">data</span><span class="p">?.</span><span class="nx">data</span><span class="p">?.</span><span class="nx">total_items</span> <span class="o">||</span> <span class="mi">10</span><span class="p">,</span> <span class="na">isLoading</span><span class="p">:</span> <span class="o">!</span><span class="nx">error</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="nx">data</span><span class="p">,</span> <span class="na">isError</span><span class="p">:</span> <span class="nx">error</span> <span class="p">}),</span> <span class="p">[</span><span class="nx">data</span><span class="p">,</span> <span class="nx">error</span><span class="p">]</span> <span class="p">);</span> <span class="p">}</span> </code></pre> </div> <p>Kekurangan Cara Lama:</p> <ul> <li>Tidak Ada Caching Otomatis → Setiap kali komponen dirender ulang, data akan selalu di-fetch ulang dari server.</li> <li>Harus Mengelola State Secara Manual → Perlu membuat useState untuk menyimpan data, error, dan status loading.</li> <li>Tidak Ada Revalidasi Otomatis → Jika data berubah di server, komponen tidak akan tahu dan tidak akan memperbarui datanya.</li> <li>Tidak Ada Fetch Deduplication → Jika banyak komponen mem-fetch data yang sama, setiap komponen akan melakukan request terpisah.</li> </ul> <h2> Kesimpulan </h2> <p>SWR adalah solusi yang efisien untuk data fetching di React. Dengan caching otomatis dan fitur <code>mutate</code>, SWR membuat aplikasi lebih cepat dan responsif tanpa perlu konfigurasi kompleks. Jika ingin meningkatkan performa aplikasi dengan cara yang sederhana, SWR adalah pilihan yang tepat!</p> <p>Sekian artikel keren saya kali ini, semoga bermanfaat dan terima kasih sudah membaca!</p> react reactnative website javascript 🚀TypeScript 10x Lebih Ngebut: Inovasi Gede Buat Developer JavaScript Ferry Ananda Febian Sun, 16 Mar 2025 03:43:17 +0000 https://dev.to/ferryops/typescript-10x-lebih-ngebut-inovasi-gede-buat-developer-javascript-4gm1 https://dev.to/ferryops/typescript-10x-lebih-ngebut-inovasi-gede-buat-developer-javascript-4gm1 <p>Siapa sih yang gak kenal TypeScript, TypeScript sudah jadi salah satu tools utama buat developer JavaScript, ngasih pengalaman ngoding yang lebih enak lewat sistem tipe yang kuat. </p> <p>Buat yang belum tau apa itu sistem tipe, yaitu static type system yang dipakai oleh TypeScript. Berbeda dengan JavaScript yang bersifat dynamically typed (tipe data bisa berubah-ubah saat runtime), TypeScript memperkenalkan static typing yang memungkinkan pengecekan tipe data sebelum kode dijalankan.</p> <p>Dengan munculnya update ini, artinya error akan 10x lebih cepat terdeteksi, dan kode jadi lebih aman dan mudah dipelihara. Update ini dinamakan <strong>TypeScript 7.0 (native)</strong>, yang bakal bikin performa TypeScript jadi <strong>10x lebih cepat</strong> dari sebelumnya.</p> <h2> <strong>Kenapa Harus Implementasi Native?</strong> </h2> <p>Saat ini, TypeScript jalan di atas JavaScript, yang menyebabkan munculnya batasan performa, terutama buat proyek besar. Jadi implementasi native ini bakal ngebantu dengan:</p> <ul> <li> <strong>Mempercepat waktu muat editor</strong> secara drastis.</li> <li><strong>Build lebih cepat, sampai 10x lipat!</strong></li> <li> <strong>Konsumsi memori lebih kecil</strong>, jadi lebih ringan dipakai.</li> <li> <strong>Dukungan lebih mantap buat AI tools</strong> yang butuh akses cepat ke kode.</li> </ul> <h2> <strong>Seberapa Ngebut Emang?</strong> </h2> <p>Tim TypeScript udah ngetes implementasi native ini di beberapa proyek open-source gede di GitHub. Nih hasil perbandingan waktu eksekusi <code>tsc</code> (TypeScript Compiler) antara versi lama dan versi native:</p> <div class="table-wrapper-paragraph"><table> <thead> <tr> <th><strong>Proyek</strong></th> <th><strong>Ukuran (LOC)</strong></th> <th><strong>Sekarang</strong></th> <th><strong>Native</strong></th> <th><strong>Lebih Cepat</strong></th> </tr> </thead> <tbody> <tr> <td>VS Code</td> <td>1.505.000</td> <td>77,8s</td> <td>7,5s</td> <td><strong>10,4x</strong></td> </tr> <tr> <td>Playwright</td> <td>356.000</td> <td>11,1s</td> <td>1,1s</td> <td><strong>10,1x</strong></td> </tr> <tr> <td>TypeORM</td> <td>270.000</td> <td>17,5s</td> <td>1,3s</td> <td><strong>13,5x</strong></td> </tr> <tr> <td>date-fns</td> <td>104.000</td> <td>6,5s</td> <td>0,7s</td> <td><strong>9,5x</strong></td> </tr> <tr> <td>tRPC</td> <td>18.000</td> <td>5,5s</td> <td>0,6s</td> <td><strong>9,1x</strong></td> </tr> <tr> <td>rxjs</td> <td>2.100</td> <td>1,1s</td> <td>0,1s</td> <td><strong>11,0x</strong></td> </tr> </tbody> </table></div> <p>Hasilnya, peningkatan performanya gila-gilaan! Proses <strong>build dan cek tipe jadi jauh lebih cepat</strong>.</p> <h2> <strong>Editor Jadi Lebih Ringan dan Responsif</strong> </h2> <p>Sebagian besar waktu developer dihabisin di editor kode, jadi kalau makin cepat, makin enak. Dengan implementasi native ini, <strong>waktu muat proyek di editor kayak VS Code berkurang dari 9,6 detik jadi 1,2 detik</strong>—hampir <strong>8x lebih cepat!</strong> Selain itu:</p> <ul> <li><strong>Konsumsi memori turun sampai 50%.</strong></li> <li><strong>Autocomplete, go to definition, dan find all references jadi lebih responsif.</strong></li> <li> <strong>Integrasi lebih baik dengan Language Server Protocol (LSP),</strong> jadi bisa dipakai di lebih banyak editor.</li> </ul> <h2> <strong>Jadwal Rilis TypeScript Baru</strong> </h2> <p>Tim TypeScript udah nentuin roadmap buat transisi ini:</p> <ul> <li> <strong>TypeScript 6.x</strong> masih bakal berbasis JavaScript dengan update rutin.</li> <li> <strong>TypeScript 7.0 (native)</strong> bakal dirilis setelah fitur-fiturnya setara dengan versi lama.</li> <li> <strong>Nama kode proyek ini "Corsa",</strong> yang jadi dasar TypeScript ke depan.</li> <li> <strong>Bakal tetap kompatibel</strong> sama proyek lama, meskipun mungkin ada beberapa penyesuaian.</li> </ul> <h2> <strong>Apa Selanjutnya?</strong> </h2> <p>Tim TypeScript punya rencana buat:</p> <ul> <li>Ngerilis versi preview buat <strong>type-checking di CLI pertengahan 2025</strong>.</li> <li>Meluncurin <strong>versi penuh dengan fitur build dan layanan bahasa akhir 2025</strong>.</li> <li>Ngadain sesi AMA (Ask Me Anything) sama komunitas TypeScript <strong>13 Maret 2025</strong>.</li> <li>Ngasih update rutin soal progres proyek di GitHub.</li> </ul> <h2> <strong>Kesimpulan</strong> </h2> <p>Implementasi native TypeScript ini bakal bawa perubahan gede buat dunia JavaScript. Dengan <strong>kecepatan 10x lipat, konsumsi memori lebih kecil, dan editor yang lebih ngebut</strong>, developer bisa kerja lebih efektif dan nyaman. Ini juga bakal ngebuka peluang baru buat AI tools, analisis kode lebih dalam, dan refactoring yang lebih smooth.</p> <p>Buat kalian yang sering pakai TypeScript, siap-siap aja buat pengalaman ngoding yang jauh lebih cepat! 🚀</p> <p>Dikutip dari: <br> <iframe width="710" height="399" src="proxy.php?url=https://www.youtube.com/embed/pNlq-EVld70"> </iframe> </p> typescript microsoft javascript indonesia 🔁Cara Mudah Reverse Proxy di cPanel dengan Nginx Ferry Ananda Febian Sat, 15 Mar 2025 01:45:23 +0000 https://dev.to/ferryops/cara-mudah-reverse-proxy-di-cpanel-dengan-nginx-4opi https://dev.to/ferryops/cara-mudah-reverse-proxy-di-cpanel-dengan-nginx-4opi <h2> Kendala Reverse Proxy dengan Apache di cPanel </h2> <p>Ketika saya pertama kali mencoba melakukan reverse proxy untuk aplikasi Node.js khususnya Next.js di server cPanel, saya mengalami kendala karena default web server di cPanel adalah Apache. Apache memang powerful, tetapi konfigurasi reverse proxy di Apache bisa lebih kompleks dibandingkan Nginx.</p> <p>Setelah mencari solusi, saya menemukan cara yang lebih mudah yaitu menggunakan Engintron. Engintron for cPanel/WHM adalah cara termudah untuk mengintegrasikan Nginx sebagai reverse proxy di server cPanel/WHM. </p> <p><a href="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8w5hdpuowtv9ig4nevou.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8w5hdpuowtv9ig4nevou.png" alt="Cara Kerja Engintron" width="800" height="461"></a><br> <em>Cara Kerja Engintron</em></p> <p>Dengan Engintron, kita bisa mengatur reverse proxy dengan lebih fleksibel dan meningkatkan performa server secara signifikan.</p> <h2> Cara Install Engintron di cPanel </h2> <p>Untuk menginstal Engintron di server cPanel/WHM, ikuti langkah-langkah berikut:</p> <ol> <li>Masuk ke server melalui SSH sebagai root.</li> <li>Jalankan perintah berikut untuk mengunduh dan menginstal Engintron: </li> </ol> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code><span class="nb">cd</span> /<span class="p">;</span> <span class="nb">rm</span> <span class="nt">-f</span> engintron.sh<span class="p">;</span> wget <span class="nt">--no-check-certificate</span> https://raw.githubusercontent.com/engintron/engintron/master/engintron.sh<span class="p">;</span> bash engintron.sh <span class="nb">install</span> </code></pre> </div> <ol> <li>Setelah instalasi selesai, login ke WHM melalui browser dengan mengakses: <code>https://yourserver.com:2087</code> </li> <li>Buka menu <strong>Plugins</strong>.</li> <li>Cari dan buka <strong>Engintron for cPanel/WHM</strong>.</li> <li>Engintron sudah aktif dan siap digunakan untuk mengatur reverse proxy.</li> </ol> <p><a href="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk4w99dad6z4ch9nkwm9g.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk4w99dad6z4ch9nkwm9g.png" alt="Tampilan Engintron di WHM" width="800" height="415"></a><br> <em>Tampilan Engintron di WHM</em></p> <h2> Mengatur Custom Rules di Engintron </h2> <p>Setelah Engintron terpasang, kita dapat menyesuaikan konfigurasi Nginx agar bekerja sesuai dengan kebutuhan kita. Salah satunya adalah mengatur redirect domain dan konfigurasi reverse proxy untuk aplikasi Node.js.</p> <h3> Redirect domain.com ke <a href="proxy.php?url=http://www.domain.com" rel="noopener noreferrer">www.domain.com</a> </h3> <p>Jika ingin mengalihkan semua permintaan dari domain.com ke <a href="proxy.php?url=http://www.domain.com" rel="noopener noreferrer">www.domain.com</a>, tambahkan aturan berikut di konfigurasi Nginx:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code><span class="c"># Redirect domain.com to www.domain.com</span> <span class="k">if</span> <span class="o">(</span><span class="nv">$host</span> <span class="o">=</span> <span class="s2">"domain.com"</span><span class="o">)</span> <span class="o">{</span> <span class="k">return </span>301 https://www.domain.com<span class="nv">$request_uri</span><span class="p">;</span> <span class="o">}</span> </code></pre> </div> <h3> Reverse Proxy ke Aplikasi Node.js </h3> <p>Misalkan aplikasi Node.js berjalan di port <code>3000</code>, kita bisa mengatur Nginx untuk meneruskan permintaan ke aplikasi tersebut:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code><span class="c"># Proxy rules for www.domain.com</span> <span class="k">if</span> <span class="o">(</span><span class="nv">$host</span> <span class="o">=</span> <span class="s2">"www.domain.com"</span><span class="o">)</span> <span class="o">{</span> <span class="nb">set</span> <span class="nv">$PROXY_SCHEME</span> <span class="s2">"http"</span><span class="p">;</span> <span class="nb">set</span> <span class="nv">$PROXY_TO_PORT</span> 3000<span class="p">;</span> <span class="o">}</span> </code></pre> </div> <p>Dengan aturan di atas, semua request ke <code>www.domain.com</code> akan diteruskan ke aplikasi Node.js yang berjalan di port <code>3000</code>.</p> <h2> Kesimpulan </h2> <p>Menggunakan Engintron di cPanel sangat membantu dalam mengelola reverse proxy dengan Nginx tanpa perlu konfigurasi manual yang rumit. Dengan sedikit penyesuaian pada custom rules, kita bisa dengan mudah mengarahkan domain, meningkatkan performa, dan mengoptimalkan server sesuai kebutuhan.</p> <p>Jika ingin menjalankan aplikasi Node.js atau layanan lain dengan reverse proxy di cPanel, Engintron adalah solusi terbaik yang patut dicoba!</p> <p>Selamat bereksperimen dan terima kasih sudah mampir di artikel keren saya. Semoga bermanfaat!</p> <p>Referensi:<br> [1] <a href="proxy.php?url=https://engintron.com/" rel="noopener noreferrer">https://engintron.com/</a></p> linux nginx devops indonesia