<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Tomás Alegre Sepúlveda</title>
    <description>The latest articles on Forem by Tomás Alegre Sepúlveda (@alpha018).</description>
    <link>https://forem.com/alpha018</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2032745%2F7d24f6ba-283c-402f-826d-e73a9318f3a7.jpeg</url>
      <title>Forem: Tomás Alegre Sepúlveda</title>
      <link>https://forem.com/alpha018</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/alpha018"/>
    <language>en</language>
    <item>
      <title>☠️ How to Connect Your Router Directly to Fiber Without Your ISP's Modem (SFP GPON Hack)</title>
      <dc:creator>Tomás Alegre Sepúlveda</dc:creator>
      <pubDate>Sat, 25 Apr 2026 21:07:04 +0000</pubDate>
      <link>https://forem.com/alpha018/how-to-connect-your-router-directly-to-fiber-without-your-isps-modem-sfp-gpon-hack-15b2</link>
      <guid>https://forem.com/alpha018/how-to-connect-your-router-directly-to-fiber-without-your-isps-modem-sfp-gpon-hack-15b2</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/alpha018/como-conectar-tu-router-directo-a-la-fibra-de-entel-chile-sin-su-modem-sfp-gpon-hack-19nd"&gt;🇪🇸 Leer este post en Español&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Entel Chile told me bridge mode wasn't available for business customers. A week later, my Ubiquiti router was plugged directly into fiber with no ISP hardware anywhere in the path.&lt;/p&gt;

&lt;p&gt;This post covers how: a Ubiquiti UF-Instant SFP GPON transceiver, UART access, direct flash via U-Boot, and GPON spoofing to make Entel's OLT accept the stick as if it were the original Huawei ONT.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Condensed version.&lt;/strong&gt; This post covers the process without the failed attempts or dead ends. The extended version — kernel panic on the MC220L, three firmware attempts, TFTP recovery — is on &lt;a href="https://buymeacoffee.com/alpha018/how-i-got-rid-my-isp-modem-hacking-sfp-gpon-transceiver" rel="noopener noreferrer"&gt;Buy Me a Coffee&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Legal disclaimer:&lt;/strong&gt; This content is for educational purposes only. Modifying firmware on telecommunications devices may violate your ISP contract and local regulations. Check applicable laws before replicating this process.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The problem: ISP vendor lock-in
&lt;/h2&gt;

&lt;p&gt;ISPs install their own ONT and refuse bridge mode, forcing you to use their router for PPPoE. If you want your own router, you're stuck with double NAT or bypassing ISP hardware entirely.&lt;/p&gt;

&lt;p&gt;I wanted dual WAN on my Ubiquiti UniFi Gateway Fiber with a static IP from Entel. No double NAT. No Entel modem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hardware
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Transceiver&lt;/td&gt;
&lt;td&gt;Ubiquiti UFiber UF-Instant (RTL9601CI)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Router&lt;/td&gt;
&lt;td&gt;Ubiquiti UniFi Gateway Fiber (Dual WAN, SFP+ slot)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ISP&lt;/td&gt;
&lt;td&gt;Entel Chile (Huawei OLT)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Original ONT&lt;/td&gt;
&lt;td&gt;Huawei OptiXstar (cloned for spoofing)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Console access&lt;/td&gt;
&lt;td&gt;UART TTL adapter (3.3V — mandatory)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TFTP server&lt;/td&gt;
&lt;td&gt;Mac with tftp-now or tftp-hpa&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rootfs prep&lt;/td&gt;
&lt;td&gt;Docker (required on macOS)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="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%2Fl1byf16a7ihoeoss8i5k.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="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%2Fl1byf16a7ihoeoss8i5k.jpeg" alt="UF-Instant alongside the UART TTL adapter — wires soldered to the PCB pads"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Ff6cx02igafiw98rl4peg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="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%2Ff6cx02igafiw98rl4peg.jpeg" alt="Full experiment setup — modified transceiver connected to the switch and USB-C hub"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Fnracnn29o2nxcoc7qeql.png" class="article-body-image-wrapper"&gt;&lt;img src="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%2Fnracnn29o2nxcoc7qeql.png" alt="UF-Instant pinout reference (Rx, Tx, GND, VCC)"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Source: &lt;a href="https://github.com/stich86/UF-Instant-Mod" rel="noopener noreferrer"&gt;stich86/UF-Instant-Mod&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  UART access: the primary method
&lt;/h2&gt;

&lt;p&gt;Every flash operation runs from here. The UF-Instant exposes UART pads on the PCB (TX, RX, GND). 3.3V adapter, &lt;code&gt;115200 8N1&lt;/code&gt;:&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="c"&gt;# macOS&lt;/span&gt;
screen /dev/tty.usbserial-XXXXX 115200

&lt;span class="c"&gt;# Linux&lt;/span&gt;
screen /dev/ttyUSB0 115200

&lt;span class="c"&gt;# With minicom&lt;/span&gt;
minicom &lt;span class="nt"&gt;-D&lt;/span&gt; /dev/ttyUSB0 &lt;span class="nt"&gt;-b&lt;/span&gt; 115200 &lt;span class="nt"&gt;-8&lt;/span&gt; &lt;span class="nt"&gt;--noinit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Power on the stick with UART connected and you get the full U-Boot output. Press any key in the first few seconds to interrupt boot and land at the &lt;code&gt;9601C#&lt;/code&gt; prompt, where you can flash partitions directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  MTD partition map
&lt;/h2&gt;

&lt;p&gt;The UF-Instant uses a dual-image boot system. The relevant partitions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mtd4  → kernel0 / k0 (3MB)   — Image 0 kernel (original Ubiquiti — fallback)
mtd5  → rootfs0 / r0 (6MB)   — Image 0 rootfs (original Ubiquiti firmware)
mtd6  → kernel1 / k1 (3MB)   — Image 1 kernel — cloned kernel goes here
mtd7  → rootfs1 / r1 (4.5MB) — Image 1 rootfs — stich86 MOD goes here
mtd8  → europa.data          — Laser calibration ⚠️ NEVER TOUCH
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Boot control:&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="c"&gt;# Check active image&lt;/span&gt;
nv getenv sw_active sw_commit

&lt;span class="c"&gt;# Switch to image 1&lt;/span&gt;
setenv sw_active 1 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; setenv sw_commit 1 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; saveenv &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; boot

&lt;span class="c"&gt;# Fall back to image 0 (original Ubiquiti firmware)&lt;/span&gt;
setenv sw_active 0 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; setenv sw_commit 0 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; saveenv &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; boot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The process: straight to the point
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;On the host&lt;/strong&gt; (before connecting UART): download the MOD rootfs and Ubiquiti v4.4.2 kernel directly from &lt;a href="https://github.com/stich86/UF-Instant-Mod" rel="noopener noreferrer"&gt;stich86/UF-Instant-Mod&lt;/a&gt; and place them in the TFTP server directory. stich86's squashfs binary flashes as-is — no reprocessing needed.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;When do you need Docker?&lt;/strong&gt; Only if you modify the rootfs (extract, change files, repack). On macOS, &lt;code&gt;mksquashfs&lt;/code&gt; can't recreate device nodes without explicit parameters. For the standard flow with stich86's binary, skip Docker.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Via UART, in U-Boot (&lt;code&gt;9601C#&lt;/code&gt;):&lt;/strong&gt; initialize flash, clone the kernel from image 0 to image 1, and flash the MOD rootfs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sf probe 0

&lt;span class="c"&gt;# Clone Ubiquiti v4.4.2 kernel from image 0 → image 1&lt;/span&gt;
sf &lt;span class="nb"&gt;read  &lt;/span&gt;0x81000000 0x50000  0x300000   &lt;span class="c"&gt;# read kernel0 into RAM&lt;/span&gt;
sf erase 0x4e0000 0x300000              &lt;span class="c"&gt;# erase kernel1 (mtd6)&lt;/span&gt;
sf write 0x81000000 0x4e0000 0x300000   &lt;span class="c"&gt;# write to mtd6&lt;/span&gt;

&lt;span class="c"&gt;# Flash MOD rootfs (stich86) via TFTP&lt;/span&gt;
tftpboot 0x81000000 &lt;span class="o"&gt;[&lt;/span&gt;HOST_IP]:[stich86_rootfs_filename]
sf erase 0x7e0000 0x480000              &lt;span class="c"&gt;# erase rootfs1 (mtd7)&lt;/span&gt;
sf write 0x81000000 0x7e0000 &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;filesize&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Activate image 1 and boot&lt;/span&gt;
setenv sw_active 1
setenv sw_commit 1
saveenv
boot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Via UART, from Linux&lt;/strong&gt; (after booting image 1, credentials &lt;code&gt;admin&lt;/code&gt;/&lt;code&gt;admin&lt;/code&gt; — some builds use &lt;code&gt;ubnt&lt;/code&gt;/&lt;code&gt;ubnt&lt;/code&gt;): configure GPON identity with the original Huawei ONT data and neutralize the bootlimit:&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="c"&gt;# GPON identity — use your actual Huawei ONT data&lt;/span&gt;
flash &lt;span class="nb"&gt;set &lt;/span&gt;GPON_SN HWTC[XXXXXXXX]          &lt;span class="c"&gt;# ONT SN: HWTC + 8 hex chars&lt;/span&gt;
flash &lt;span class="nb"&gt;set &lt;/span&gt;PON_VENDOR_ID HWTC
flash &lt;span class="nb"&gt;set &lt;/span&gt;OMCI_OLT_MODE 1
flash &lt;span class="nb"&gt;set &lt;/span&gt;OMCI_FAKE_OK 1
flash &lt;span class="nb"&gt;set &lt;/span&gt;OMCI_SW_VER1 &lt;span class="o"&gt;[&lt;/span&gt;ONT_SW_VERSION]   &lt;span class="c"&gt;# ONT software version&lt;/span&gt;
flash &lt;span class="nb"&gt;set &lt;/span&gt;OMCI_SW_VER2 &lt;span class="o"&gt;[&lt;/span&gt;ONT_SW_VERSION]
flash &lt;span class="nb"&gt;set &lt;/span&gt;HW_HWVER &lt;span class="o"&gt;[&lt;/span&gt;ONT_HW_VERSION]       &lt;span class="c"&gt;# ONT hardware version&lt;/span&gt;
flash &lt;span class="nb"&gt;set &lt;/span&gt;GPON_ONU_MODEL &lt;span class="o"&gt;[&lt;/span&gt;ONT_MODEL]      &lt;span class="c"&gt;# ONT model&lt;/span&gt;
flash &lt;span class="nb"&gt;set &lt;/span&gt;ELAN_MAC_ADDR &lt;span class="o"&gt;[&lt;/span&gt;MAC_NO_COLONS]   &lt;span class="c"&gt;# ONT MAC without colons&lt;/span&gt;

&lt;span class="c"&gt;# Disable automatic factory reset (bootlimit=10 by default)&lt;/span&gt;
nv setenv bootcount 0
nv setenv bootlimit 0

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verify it works
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# O5 = Operation State = OLT accepted the stick as a valid ONT&lt;/span&gt;
diag gpon get onu-state
&lt;span class="c"&gt;# ONU state: Operation State(O5)&lt;/span&gt;

&lt;span class="c"&gt;# Correct OLT vendor&lt;/span&gt;
omcicli mib get 131
&lt;span class="c"&gt;# OltVendorId: 0x48575443  ← HWTC (Huawei/Entel Chile) ✓&lt;/span&gt;

&lt;span class="c"&gt;# VLANs provisioned by Entel's OLT&lt;/span&gt;
omcicli mib get 84
&lt;span class="c"&gt;# VLAN 3610 → Internet | 3620 → TV | 3630 → VoIP | 3680 → TR-069&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the stick at O5: configure the Ubiquiti SFP+ port with VLAN 3610 and PPPoE with Entel credentials (&lt;code&gt;20011279169002&lt;/code&gt; / &lt;code&gt;D4310T&lt;/code&gt; — tested on 2026-04-23, subject to change).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🎥 &lt;strong&gt;&lt;a href="https://drive.google.com/file/d/1jLBNIA8xwyT7rVpS8N7n40CP2AoTfu8s/view?usp=sharing" rel="noopener noreferrer"&gt;The official Entel modem farewell ceremony&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Trade-offs: when NOT to do this
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No UART/U-Boot experience:&lt;/strong&gt; The process requires bootloader commands. A wrong address in &lt;code&gt;sf write&lt;/code&gt; can brick the stick (recoverable via TFTP if U-Boot still responds).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Using a media converter (TP-Link MC220L):&lt;/strong&gt; The &lt;code&gt;europa_drv&lt;/code&gt; kernel panics with that hardware as an intermediary. The UF-Instant must go &lt;strong&gt;directly&lt;/strong&gt; into the router's SFP+ slot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ISP uses authentication beyond PLOAM serial:&lt;/strong&gt; Some ISPs also validate PLOAM password, OMCI details, or run clone detection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Using Entel's TV or VoIP:&lt;/strong&gt; Those run on separate VLANs (3620 and 3630). You'll need to configure them on the router too.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The failed attempts, kernel panic details, and the MC220L diagnosis are documented in full on ☕ &lt;a href="https://buymeacoffee.com/alpha018/how-i-got-rid-my-isp-modem-hacking-sfp-gpon-transceiver" rel="noopener noreferrer"&gt;Buy Me a Coffee&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Glossary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Term&lt;/th&gt;
&lt;th&gt;What it is&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GPON&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Gigabit Passive Optical Network. Fiber delivering up to 2.5 Gbps downstream shared among multiple users&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ONT&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Optical Network Terminal. The device at your location converting optical signal to ethernet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OLT&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Optical Line Termination. ISP equipment managing all ONTs in an area&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SFP / SFP+&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Small Form-factor Pluggable. Standard connectivity module. SFP+ supports up to 10 Gbps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PLOAM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Physical Layer Operations, Administration and Maintenance. Authentication protocol between ONT and OLT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OMCI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ONT Management and Control Interface. Channel through which the OLT remotely manages the ONT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PPPoE&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Point-to-Point Protocol over Ethernet. Authentication protocol ISPs use for internet connections&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MTD&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Memory Technology Device. Linux interface for embedded flash storage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;U-Boot&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Universal bootloader for embedded systems. Lets you flash partitions if the OS won't start&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;UART&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Universal Asynchronous Receiver-Transmitter. Serial port for console access before Linux loads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TFTP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Trivial File Transfer Protocol. Minimal protocol for transferring firmware to embedded devices&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Spoofing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Impersonating another device's identity to the OLT&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Resources and credits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/stich86/UF-Instant-Mod" rel="noopener noreferrer"&gt;stich86/UF-Instant-Mod&lt;/a&gt; — Modified rootfs for UF-Instant&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Anime4000/RTL960x" rel="noopener noreferrer"&gt;Anime4000/RTL960x&lt;/a&gt; — Firmware and RTL960x documentation&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hack-gpon.org/" rel="noopener noreferrer"&gt;hack-gpon.org&lt;/a&gt; — Community GPON hack reference&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://false-balaur-bca.notion.site/MA5671A-Configuration-Guide-HEX-PLOAM-2eee31cf27198055a79afb46d7b17b2d" rel="noopener noreferrer"&gt;MA5671A Config Guide (PLOAM)&lt;/a&gt; — PLOAM configuration guide&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;If you've done something similar with a different ISP or OLT vendor, let me know in the comments. I'd like to know which parameters change between carriers.&lt;/p&gt;




&lt;h3&gt;
  
  
  💌 Special Acknowledgments
&lt;/h3&gt;

&lt;p&gt;To a girl who, without knowing it, pushed me to keep creating and coding in my free time. To the one who was once my spark: for "YLP", with gratitude... and with scars that also teach.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;For those who also refused "it can't be done" as a final answer: this post exists because I was told I wouldn't get it working. And because fiber now reaches my router without passing through any hardware I didn't choose. Nobody decides that for me.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;🖕 Fuck you, Entel. Your bargain-bin devs don't get to decide what I plug into my own network. And you, Movistar — don't get comfortable. I know you run ZTE and you're next on the list.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>networking</category>
      <category>homelab</category>
      <category>linux</category>
      <category>sfp</category>
    </item>
    <item>
      <title>☠️ Cómo conectar tu router directo a la fibra de Entel Chile sin su módem (SFP GPON Hack)</title>
      <dc:creator>Tomás Alegre Sepúlveda</dc:creator>
      <pubDate>Fri, 24 Apr 2026 05:54:04 +0000</pubDate>
      <link>https://forem.com/alpha018/como-conectar-tu-router-directo-a-la-fibra-de-entel-chile-sin-su-modem-sfp-gpon-hack-19nd</link>
      <guid>https://forem.com/alpha018/como-conectar-tu-router-directo-a-la-fibra-de-entel-chile-sin-su-modem-sfp-gpon-hack-19nd</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/alpha018/how-to-connect-your-router-directly-to-fiber-without-your-isps-modem-sfp-gpon-hack-15b2"&gt;🇺🇸 Read this post in English&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Entel Chile me dijo que el bridge mode no existía para clientes empresa. Una semana después, mi router Ubiquiti estaba conectado directo a la fibra sin ningún equipo de Entel de por medio.&lt;/p&gt;

&lt;p&gt;Este post explica cómo: un transceptor SFP GPON (Ubiquiti UF-Instant), acceso por UART, flash directo via U-Boot y configuración de spoofing GPON para que la OLT de Entel acepte el stick como si fuera el ONT Huawei original.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Versión resumida.&lt;/strong&gt; Este post cubre el proceso sin los intentos fallidos ni los callejones sin salida. La versión extendida — con el kernel panic del MC220L, los tres intentos de firmware y la recuperación por TFTP — está en &lt;a href="https://buymeacoffee.com/alpha018/cmo-deshice-del-mdem-de-entel-chile-hackeando-un-transceptor-sfp-gpon" rel="noopener noreferrer"&gt;Buy Me a Coffee&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Disclaimer legal:&lt;/strong&gt; Este contenido es exclusivamente educativo. Modificar firmware de dispositivos de telecomunicaciones puede vulnerar los términos de tu contrato con el ISP y potencialmente regulaciones locales. Consulta la normativa vigente en tu país antes de replicar este procedimiento.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  El problema: vendor lock-in de ISP
&lt;/h2&gt;

&lt;p&gt;Los ISP instalan su propio ONT y se niegan a configurarlo en bridge mode, forzándote a usar su router para PPPoE. Si quieres usar el tuyo, las opciones son doble NAT o bypassar completamente el hardware del ISP.&lt;/p&gt;

&lt;p&gt;Quería dual WAN en mi Ubiquiti UniFi Gateway Fiber con IP estática de Entel. Sin doble NAT. Sin el módem de Entel.&lt;/p&gt;

&lt;h2&gt;
  
  
  El hardware
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Componente&lt;/th&gt;
&lt;th&gt;Descripción&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Transceptor&lt;/td&gt;
&lt;td&gt;Ubiquiti UFiber UF-Instant (RTL9601CI)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Router&lt;/td&gt;
&lt;td&gt;Ubiquiti UniFi Gateway Fiber (Dual WAN, slot SFP+)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ISP&lt;/td&gt;
&lt;td&gt;Entel Chile (OLT Huawei)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ONT original&lt;/td&gt;
&lt;td&gt;Huawei OptiXstar (clonado para spoofing)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Acceso de consola&lt;/td&gt;
&lt;td&gt;Adaptador UART TTL (3.3V obligatorio)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Servidor TFTP&lt;/td&gt;
&lt;td&gt;Mac con tftp-now o tftp-hpa&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Preparación del rootfs&lt;/td&gt;
&lt;td&gt;Docker (necesario en macOS)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="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%2Fl1byf16a7ihoeoss8i5k.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="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%2Fl1byf16a7ihoeoss8i5k.jpeg" alt="UF-Instant junto al adaptador UART TTL — cables soldados a los pads de la PCB" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Ff6cx02igafiw98rl4peg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="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%2Ff6cx02igafiw98rl4peg.jpeg" alt="Setup del experimento — transceptor modificado conectado al switch y hub USB-C" width="720" height="1280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Fnracnn29o2nxcoc7qeql.png" class="article-body-image-wrapper"&gt;&lt;img src="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%2Fnracnn29o2nxcoc7qeql.png" alt="Referencia de pinout del UF-Instant (Rx, Tx, GND, VCC)" width="800" height="191"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Fuente: &lt;a href="https://github.com/stich86/UF-Instant-Mod" rel="noopener noreferrer"&gt;stich86/UF-Instant-Mod&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Acceso por UART: el método principal
&lt;/h2&gt;

&lt;p&gt;Todo el proceso de flash se ejecuta desde aquí. El UF-Instant expone pads UART en la PCB (TX, RX, GND). Adaptador 3.3V, configuración &lt;code&gt;115200 8N1&lt;/code&gt;:&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="c"&gt;# macOS&lt;/span&gt;
screen /dev/tty.usbserial-XXXXX 115200

&lt;span class="c"&gt;# Linux&lt;/span&gt;
screen /dev/ttyUSB0 115200

&lt;span class="c"&gt;# Con minicom&lt;/span&gt;
minicom &lt;span class="nt"&gt;-D&lt;/span&gt; /dev/ttyUSB0 &lt;span class="nt"&gt;-b&lt;/span&gt; 115200 &lt;span class="nt"&gt;-8&lt;/span&gt; &lt;span class="nt"&gt;--noinit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Al encender el stick con UART conectado, tienes el output completo de U-Boot. Presiona cualquier tecla en los primeros segundos para interrumpir el boot y quedar en el prompt &lt;code&gt;9601C#&lt;/code&gt;, desde donde puedes flashear particiones directamente.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mapa de particiones MTD
&lt;/h2&gt;

&lt;p&gt;El UF-Instant usa un sistema de arranque dual. Las particiones relevantes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mtd4  → kernel0 / k0 (3MB)   — Kernel imagen 0 (Ubiquiti original — respaldo)
mtd5  → rootfs0 / r0 (6MB)   — Rootfs imagen 0 (firmware Ubiquiti original)
mtd6  → kernel1 / k1 (3MB)   — Kernel imagen 1 — aquí va el clon
mtd7  → rootfs1 / r1 (4.5MB) — Rootfs imagen 1 — aquí va el MOD stich86
mtd8  → europa.data          — Calibración del láser ⚠️ JAMÁS TOCAR
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Control de arranque:&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="c"&gt;# Imagen activa&lt;/span&gt;
nv getenv sw_active sw_commit

&lt;span class="c"&gt;# Cambiar a imagen 1&lt;/span&gt;
setenv sw_active 1 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; setenv sw_commit 1 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; saveenv &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; boot

&lt;span class="c"&gt;# Volver a imagen 0 (firmware original Ubiquiti)&lt;/span&gt;
setenv sw_active 0 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; setenv sw_commit 0 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; saveenv &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; boot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  El proceso: directo al grano
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;En el host&lt;/strong&gt; (antes de conectar UART): descargar el rootfs MOD y el kernel de Ubiquiti v4.4.2 directamente del repo &lt;a href="https://github.com/stich86/UF-Instant-Mod" rel="noopener noreferrer"&gt;stich86/UF-Instant-Mod&lt;/a&gt; y ponerlos en el directorio del servidor TFTP. El binario squashfs de stich86 se flashea tal como viene — no requiere reprocesado.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;¿Cuándo se necesita Docker?&lt;/strong&gt; Solo si modificas el rootfs (extraer, cambiar archivos y reempaquetar). En macOS, &lt;code&gt;mksquashfs&lt;/code&gt; no puede recrear device nodes sin parámetros explícitos. Para el flujo estándar con el binario de stich86, no hace falta.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Via UART, en U-Boot (&lt;code&gt;9601C#&lt;/code&gt;):&lt;/strong&gt; inicializar flash, clonar el kernel de imagen 0 a imagen 1, y flashear el rootfs MOD:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sf probe 0

&lt;span class="c"&gt;# Clonar kernel Ubiquiti v4.4.2 de imagen 0 → imagen 1&lt;/span&gt;
sf &lt;span class="nb"&gt;read  &lt;/span&gt;0x81000000 0x50000  0x300000   &lt;span class="c"&gt;# leer kernel0 a RAM&lt;/span&gt;
sf erase 0x4e0000 0x300000              &lt;span class="c"&gt;# borrar kernel1 (mtd6)&lt;/span&gt;
sf write 0x81000000 0x4e0000 0x300000  &lt;span class="c"&gt;# escribir en mtd6&lt;/span&gt;

&lt;span class="c"&gt;# Flashear rootfs MOD (stich86) via TFTP&lt;/span&gt;
tftpboot 0x81000000 &lt;span class="o"&gt;[&lt;/span&gt;IP_HOST]:[nombre_rootfs_stich86]
sf erase 0x7e0000 0x480000              &lt;span class="c"&gt;# borrar rootfs1 (mtd7)&lt;/span&gt;
sf write 0x81000000 0x7e0000 &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;filesize&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Activar imagen 1 y arrancar&lt;/span&gt;
setenv sw_active 1
setenv sw_commit 1
saveenv
boot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Via UART, desde Linux&lt;/strong&gt; (tras arrancar imagen 1, credenciales &lt;code&gt;admin&lt;/code&gt;/&lt;code&gt;admin&lt;/code&gt; — algunos builds usan &lt;code&gt;ubnt&lt;/code&gt;/&lt;code&gt;ubnt&lt;/code&gt;): configurar la identidad GPON con los datos del ONT Huawei original y neutralizar el bootlimit:&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="c"&gt;# Identidad GPON — datos del ONT Huawei de tu instalación&lt;/span&gt;
flash &lt;span class="nb"&gt;set &lt;/span&gt;GPON_SN HWTC[XXXXXXXX]          &lt;span class="c"&gt;# SN del ONT: HWTC + 8 hex chars&lt;/span&gt;
flash &lt;span class="nb"&gt;set &lt;/span&gt;PON_VENDOR_ID HWTC
flash &lt;span class="nb"&gt;set &lt;/span&gt;OMCI_OLT_MODE 1
flash &lt;span class="nb"&gt;set &lt;/span&gt;OMCI_FAKE_OK 1
flash &lt;span class="nb"&gt;set &lt;/span&gt;OMCI_SW_VER1 &lt;span class="o"&gt;[&lt;/span&gt;VERSION_SW_ONT]   &lt;span class="c"&gt;# Versión software del ONT&lt;/span&gt;
flash &lt;span class="nb"&gt;set &lt;/span&gt;OMCI_SW_VER2 &lt;span class="o"&gt;[&lt;/span&gt;VERSION_SW_ONT]
flash &lt;span class="nb"&gt;set &lt;/span&gt;HW_HWVER &lt;span class="o"&gt;[&lt;/span&gt;VERSION_HW_ONT]       &lt;span class="c"&gt;# Versión hardware del ONT&lt;/span&gt;
flash &lt;span class="nb"&gt;set &lt;/span&gt;GPON_ONU_MODEL &lt;span class="o"&gt;[&lt;/span&gt;MODELO_ONT]     &lt;span class="c"&gt;# Modelo del ONT&lt;/span&gt;
flash &lt;span class="nb"&gt;set &lt;/span&gt;ELAN_MAC_ADDR &lt;span class="o"&gt;[&lt;/span&gt;MAC_SIN_PUNTOS]  &lt;span class="c"&gt;# MAC del ONT sin dos puntos&lt;/span&gt;

&lt;span class="c"&gt;# Desactivar factory reset automático (bootlimit=10 por defecto)&lt;/span&gt;
nv setenv bootcount 0
nv setenv bootlimit 0

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verificar que funciona
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# O5 = Operation State = la OLT aceptó el stick como ONT válido&lt;/span&gt;
diag gpon get onu-state
&lt;span class="c"&gt;# ONU state: Operation State(O5)&lt;/span&gt;

&lt;span class="c"&gt;# Vendor OLT correcto&lt;/span&gt;
omcicli mib get 131
&lt;span class="c"&gt;# OltVendorId: 0x48575443  ← HWTC (Huawei/Entel Chile) ✓&lt;/span&gt;

&lt;span class="c"&gt;# VLANs provisionadas por la OLT de Entel&lt;/span&gt;
omcicli mib get 84
&lt;span class="c"&gt;# VLAN 3610 → Internet | 3620 → TV | 3630 → VoIP | 3680 → TR-069&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Con el stick en O5: en el router Ubiquiti configurar el puerto SFP+ con VLAN 3610 y PPPoE con las credenciales de Entel (&lt;code&gt;20011279169002&lt;/code&gt; / &lt;code&gt;D4310T&lt;/code&gt; — probadas el 2026-04-23, pueden cambiar).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🎥 &lt;strong&gt;&lt;a href="https://drive.google.com/file/d/1jLBNIA8xwyT7rVpS8N7n40CP2AoTfu8s/view?usp=sharing" rel="noopener noreferrer"&gt;La despedida oficial del módem de Entel&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Trade-offs: cuándo NO hacer esto
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sin experiencia con UART/U-Boot:&lt;/strong&gt; El proceso requiere comandos en el bootloader. Un error de dirección en &lt;code&gt;sf write&lt;/code&gt; puede brick el stick (recuperable via TFTP si U-Boot sigue respondiendo).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Con un media converter (TP-Link MC220L):&lt;/strong&gt; El driver &lt;code&gt;europa_drv&lt;/code&gt; hace kernel panic con ese hardware como intermediario. El UF-Instant debe ir &lt;strong&gt;directamente&lt;/strong&gt; al slot SFP+ del router.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Si tu ISP valida más que el serial GPON:&lt;/strong&gt; Algunos ISPs verifican también el PLOAM password, detalles OMCI, o tienen detección de clones activa.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Si usas TV o VoIP de Entel:&lt;/strong&gt; Funcionan en VLANs separadas (3620 y 3630). Deberás configurarlas también en el router.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Los intentos fallidos, los errores de kernel panic y el diagnóstico del MC220L están documentados en detalle en ☕ &lt;a href="https://buymeacoffee.com/alpha018/cmo-deshice-del-mdem-de-entel-chile-hackeando-un-transceptor-sfp-gpon" rel="noopener noreferrer"&gt;Buy Me a Coffee&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Glosario
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Término&lt;/th&gt;
&lt;th&gt;Qué es&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GPON&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Gigabit Passive Optical Network. Fibra óptica que entrega hasta 2.5 Gbps downstream compartidos entre múltiples usuarios&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ONT&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Optical Network Terminal. El dispositivo en tu casa que convierte la señal óptica a ethernet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OLT&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Optical Line Termination. El equipo del ISP que gestiona todos los ONT del área&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SFP / SFP+&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Small Form-factor Pluggable. Módulo estándar de conectividad. SFP+ soporta hasta 10 Gbps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PLOAM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Physical Layer Operations, Administration and Maintenance. El protocolo de autenticación entre ONT y OLT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OMCI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ONT Management and Control Interface. Canal por donde la OLT gestiona remotamente el ONT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PPPoE&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Point-to-Point Protocol over Ethernet. Protocolo de autenticación de los ISPs para las conexiones de internet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MTD&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Memory Technology Device. Interfaz de Linux para la flash interna del dispositivo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;U-Boot&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Bootloader universal para sistemas embebidos. Permite flashear particiones si el SO no arranca&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;UART&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Universal Asynchronous Receiver-Transmitter. Puerto serie para acceder a la consola antes de que Linux cargue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TFTP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Trivial File Transfer Protocol. Protocolo minimalista para transferir firmware a dispositivos embebidos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Spoofing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Suplantar la identidad de otro dispositivo ante la OLT&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  💌 &lt;strong&gt;Agradecimientos Especiales&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A una chica que, sin saberlo, me empujó a seguir creando y programando en mi tiempo libre. A quien alguna vez fue mi chispa: para “YLP”, con gratitud… y con cicatrices que también enseñan.&lt;/p&gt;




&lt;h2&gt;
  
  
  Recursos y créditos
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/stich86/UF-Instant-Mod" rel="noopener noreferrer"&gt;stich86/UF-Instant-Mod&lt;/a&gt; — MOD rootfs para UF-Instant&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Anime4000/RTL960x" rel="noopener noreferrer"&gt;Anime4000/RTL960x&lt;/a&gt; — Firmware y documentación RTL960x&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hack-gpon.org/" rel="noopener noreferrer"&gt;hack-gpon.org&lt;/a&gt; — Referencia comunitaria de hacks GPON&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://false-balaur-bca.notion.site/MA5671A-Configuration-Guide-HEX-PLOAM-2eee31cf27198055a79afb46d7b17b2d" rel="noopener noreferrer"&gt;MA5671A Config Guide (PLOAM)&lt;/a&gt; — Guía de configuración PLOAM&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Si tienes preguntas o tu ISP usa una OLT diferente, déjalas en los comentarios. Y si ya lo has hecho con otro proveedor, cuéntame qué parámetros cambiaron.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Para quien tampoco aceptó el "no se puede" como respuesta final: este post existe porque me dijeron que no lo lograría. Y porque la fibra llega al router sin pasar por ningún equipo que no sea el mío. Nadie decide eso por mí.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;🖕 Fuck you, Entel. Tus devs de pacotilla no me dicen qué conectar en mi propia red. Y tú, Movistar — estate quieta. Sé que usas ZTE y lo tengo en el radar.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>networking</category>
      <category>homelab</category>
      <category>linux</category>
      <category>sfp</category>
    </item>
    <item>
      <title>🔬 NestJS vs Go: Performance Benchmark (Do you really need to migrate?)</title>
      <dc:creator>Tomás Alegre Sepúlveda</dc:creator>
      <pubDate>Tue, 10 Mar 2026 02:00:34 +0000</pubDate>
      <link>https://forem.com/alpha018/nestjs-vs-go-performance-benchmark-do-you-really-need-to-migrate-26eb</link>
      <guid>https://forem.com/alpha018/nestjs-vs-go-performance-benchmark-do-you-really-need-to-migrate-26eb</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/alpha018/nestjs-vs-go-benchmark-de-rendimiento-realmente-necesitas-migrar-4cd0"&gt;🇪🇸 Lee este post en Español&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hello everyone. I recently conducted an exhaustive benchmark to answer a classic question in backend teams: &lt;strong&gt;does it make sense to migrate to Go, or is NestJS enough?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There is no magic answer, but data helps decide. Here is a summary of the most important findings.&lt;/p&gt;




&lt;h2&gt;
  
  
  🏗️ The Scenario
&lt;/h2&gt;

&lt;p&gt;I compared two typical architectures running in Docker on the same machine (M4 Pro):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;NestJS (Node 22) + Prisma v6&lt;/strong&gt; (Productivity approach)&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Go (1.24) + GORM&lt;/strong&gt; (Performance approach)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both connected to &lt;strong&gt;PostgreSQL 15&lt;/strong&gt; and subjected to stress tests (gRPC and HTTP) simulating a real environment.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧪 Methodology: What did we measure?
&lt;/h2&gt;

&lt;p&gt;To truly understand the differences, I divided the tests into two key scenarios:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Small Data (1x1 Insert):&lt;/strong&gt;&lt;br&gt;
Simulates typical transactional traffic of a REST/gRPC API. We receive an object, validate it, and save it. Here we measure the framework's base latency.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Large Data (Batch Insert of 1000 elements):&lt;/strong&gt;&lt;br&gt;
Simulates bulk load processes, ETLs, or event ingestion. We send arrays of 1000 objects that must be deserialized, validated, and saved in a single transaction. This is where &lt;strong&gt;CPU and Garbage Collector&lt;/strong&gt; suffer the most.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  📊 The Results
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Small Data: The Surprise
&lt;/h3&gt;

&lt;p&gt;In &lt;strong&gt;low load or unit insert&lt;/strong&gt; scenarios, the difference was minimal. Go obtained a latency improvement of just &lt;strong&gt;~10%&lt;/strong&gt; over NestJS.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Conclusion:&lt;/strong&gt; For standard CRUD operations, NestJS is incredibly efficient, and the performance difference rarely justifies losing TypeScript's productivity.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. Large Data: The Breaking Point
&lt;/h3&gt;

&lt;p&gt;The story changes drastically when processing massive batches (arrays of 1000 elements) under high concurrency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario: 100 RPS constant (Bulk Inserts)&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Real Throughput&lt;/th&gt;
&lt;th&gt;P95 Latency&lt;/th&gt;
&lt;th&gt;Success Rate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Go gRPC&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;85 RPS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;71.88 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;100%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NestJS gRPC&lt;/td&gt;
&lt;td&gt;78 RPS&lt;/td&gt;
&lt;td&gt;98.44 ms&lt;/td&gt;
&lt;td&gt;~85%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;What happened here?&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Serialization:&lt;/strong&gt; Node.js (V8) has a higher cost when serializing/deserializing large volumes of JSON compared to Go Structs and Protobuf.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Connection Pool:&lt;/strong&gt; Under extreme pressure, the Prisma connection pool started to saturate (&lt;code&gt;P2024&lt;/code&gt; errors), causing &lt;strong&gt;NestJS to lose ~15% of requests&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Stability:&lt;/strong&gt; Go maintained 100% success and more predictable latencies, thanks to its goroutine management and a more efficient GC for this type of load.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  ⚖️ Conclusion: When to use which?
&lt;/h2&gt;

&lt;p&gt;Based on the data, here is my pragmatic recommendation:&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ Stay with NestJS if:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  You are building an MVP or validating a product.&lt;/li&gt;
&lt;li&gt;  Your team already knows TypeScript and you value development speed.&lt;/li&gt;
&lt;li&gt;  The load is moderate (&amp;lt; 300 RPS) and standard CRUD operations (Small Data).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;BFF (Backend for Frontend):&lt;/strong&gt; If you only need to orchestrate calls to a few services and the serialization load is low.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🚀 Migrate to Go if:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Complex BFF:&lt;/strong&gt; If your BFF needs to call many microservices, add heavy logic, serialize large volumes of data, and perform massive fan-out, Go will handle concurrency much better.&lt;/li&gt;
&lt;li&gt;  Your system constantly performs massive &lt;strong&gt;Bulk Operations / ETL&lt;/strong&gt; (Large Data).&lt;/li&gt;
&lt;li&gt;  You need to handle thousands of concurrent connections (e.g., Gateways, massive Websockets).&lt;/li&gt;
&lt;li&gt;  You have very strict latency SLAs where Node's Garbage Collector spikes are unacceptable.&lt;/li&gt;
&lt;li&gt;  Infrastructure is expensive and you need to squeeze every CPU cycle.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📖 Read the full analysis
&lt;/h2&gt;

&lt;p&gt;This post is a summary. The detailed analysis (with all tables, charts, hardware configuration, and error analysis) is &lt;strong&gt;public and free&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://buymeacoffee.com/alpha018/nestjs-vs-go-what-data-says-use" rel="noopener noreferrer"&gt;Read the full article on Buy Me a Coffee&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;💻 &lt;strong&gt;&lt;a href="https://github.com/Alpha018/grpc-http-performance-lab" rel="noopener noreferrer"&gt;View code on GitHub&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;💌 &lt;strong&gt;Special Acknowledgments&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To a girl who, without knowing it, pushed me to keep creating and programming in my free time. To the one who was once my spark: for "YLP", with gratitude... and with scars that also teach.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you find this analysis useful, any feedback is welcome!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>backend</category>
      <category>nestjs</category>
      <category>performance</category>
    </item>
    <item>
      <title>🔬 NestJS vs Go: Benchmark de Rendimiento (¿Realmente necesitas migrar?)</title>
      <dc:creator>Tomás Alegre Sepúlveda</dc:creator>
      <pubDate>Mon, 09 Mar 2026 00:17:36 +0000</pubDate>
      <link>https://forem.com/alpha018/nestjs-vs-go-benchmark-de-rendimiento-realmente-necesitas-migrar-4cd0</link>
      <guid>https://forem.com/alpha018/nestjs-vs-go-benchmark-de-rendimiento-realmente-necesitas-migrar-4cd0</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/alpha018/nestjs-vs-go-performance-benchmark-do-you-really-need-to-migrate-26eb"&gt;🇺🇸 Read this post in English&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hola a todos. Recientemente realicé un benchmark exhaustivo para responder una pregunta clásica en equipos de backend: &lt;strong&gt;¿tiene sentido migrar a Go, o alcanza con NestJS?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No hay respuesta mágica, pero los datos ayudan a decidir. Aquí les dejo un resumen de los hallazgos más importantes.&lt;/p&gt;




&lt;h2&gt;
  
  
  🏗️ El Escenario
&lt;/h2&gt;

&lt;p&gt;Comparé dos arquitecturas típicas corriendo en Docker sobre la misma máquina (M4 Pro):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;NestJS (Node 22) + Prisma v6&lt;/strong&gt; (Enfoque productividad)&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Go (1.24) + GORM&lt;/strong&gt; (Enfoque rendimiento)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ambos conectados a &lt;strong&gt;PostgreSQL 15&lt;/strong&gt; y sometidos a pruebas de estrés (gRPC y HTTP) simulando un entorno real.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧪 Metodología: ¿Qué medimos?
&lt;/h2&gt;

&lt;p&gt;Para entender realmente las diferencias, dividí las pruebas en dos escenarios clave:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Small Data (1x1 Insert):&lt;/strong&gt;&lt;br&gt;
Simula el tráfico transaccional típico de una API REST/gRPC. Recibimos un objeto, lo validamos y lo guardamos. Aquí medimos la latencia base del framework.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Large Data (Batch Insert de 1000 elementos):&lt;/strong&gt;&lt;br&gt;
Simula procesos de carga masiva, ETLs o ingestión de eventos. Enviamos arrays de 1000 objetos que deben ser deserializados, validados y guardados en una sola transacción. Aquí es donde &lt;strong&gt;la CPU y el Garbage Collector&lt;/strong&gt; sufren más.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  📊 Los Resultados
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Small Data: La sorpresa
&lt;/h3&gt;

&lt;p&gt;En escenarios de &lt;strong&gt;baja carga o inserciones unitarias&lt;/strong&gt;, la diferencia fue mínima. Go obtuvo una mejora de latencia de apenas un &lt;strong&gt;~10%&lt;/strong&gt; sobre NestJS.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Conclusión:&lt;/strong&gt; Para operaciones CRUD estándar, NestJS es increíblemente eficiente y la diferencia de rendimiento rara vez justifica perder la productividad de TypeScript.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. Large Data: El punto de quiebre
&lt;/h3&gt;

&lt;p&gt;La historia cambia drásticamente cuando procesamos lotes masivos (arrays de 1000 elementos) bajo alta concurrencia.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Escenario: 100 RPS constantes (Bulk Inserts)&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Servicio&lt;/th&gt;
&lt;th&gt;Throughput Real&lt;/th&gt;
&lt;th&gt;P95 Latency&lt;/th&gt;
&lt;th&gt;Success Rate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Go gRPC&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;85 RPS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;71.88 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;100%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NestJS gRPC&lt;/td&gt;
&lt;td&gt;78 RPS&lt;/td&gt;
&lt;td&gt;98.44 ms&lt;/td&gt;
&lt;td&gt;~85%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;¿Qué pasó aquí?&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Serialización:&lt;/strong&gt; Node.js (V8) tiene un costo mayor al serializar/deserializar grandes volúmenes de JSON comparado con los Structs de Go y Protobuf.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Connection Pool:&lt;/strong&gt; Bajo presión extrema, el pool de conexiones de Prisma comenzó a saturarse (errores &lt;code&gt;P2024&lt;/code&gt;), provocando que &lt;strong&gt;NestJS perdiera el ~15% de los requests&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Estabilidad:&lt;/strong&gt; Go mantuvo el 100% de éxito y latencias más predecibles, gracias a su manejo de goroutines y un GC más eficiente para este tipo de carga.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  ⚖️ Conclusión: ¿Cuándo usar cuál?
&lt;/h2&gt;

&lt;p&gt;Basado en los datos, aquí está mi recomendación pragmática:&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ Quédate con NestJS si:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  Estás construyendo un MVP o validando producto.&lt;/li&gt;
&lt;li&gt;  Tu equipo ya conoce TypeScript y valoras la velocidad de desarrollo.&lt;/li&gt;
&lt;li&gt;  La carga es moderada (&amp;lt; 300 RPS) y operaciones CRUD estándar (Small Data).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;BFF (Backend for Frontend):&lt;/strong&gt; Si solo necesitas orquestar llamadas a pocos servicios y la carga de serialización es baja.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🚀 Migra a Go si:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;BFF Complejo:&lt;/strong&gt; Si tu BFF necesita llamar a muchos microservicios, agregar lógica pesada, serializar grandes volúmenes de datos y hacer fan-out masivo, Go manejará la concurrencia mucho mejor.&lt;/li&gt;
&lt;li&gt;  Tu sistema realiza &lt;strong&gt;Bulk Operations / ETL&lt;/strong&gt; masivos constantemente (Large Data).&lt;/li&gt;
&lt;li&gt;  Necesitas manejar miles de conexiones concurrentes (ej. Gateways, Websockets masivos).&lt;/li&gt;
&lt;li&gt;  Tienes SLAs de latencia muy estrictos donde los picos del Garbage Collector de Node son inaceptables.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📖 Lee el análisis completo
&lt;/h2&gt;

&lt;p&gt;Este post es un resumen. El análisis detallado (con todas las tablas, gráficos, configuración de hardware y análisis de errores) es &lt;strong&gt;público y gratuito&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://buymeacoffee.com/alpha018/nestjs-vs-go-lo-que-los-datos-dicen-sobre-cundo-usar-cada-uno" rel="noopener noreferrer"&gt;Leer el artículo completo en Buy Me a Coffee&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;💻 &lt;strong&gt;&lt;a href="https://github.com/Alpha018/grpc-http-performance-lab" rel="noopener noreferrer"&gt;Ver el código en GitHub&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  💌 &lt;strong&gt;Agradecimientos Especiales&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A una chica que, sin saberlo, me empujó a seguir creando y programando en mi tiempo libre. A quien alguna vez fue mi chispa: para “YLP”, con gratitud… y con cicatrices que también enseñan.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Si te sirve este tipo de análisis, ¡cualquier feedback es bienvenido!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>backend</category>
      <category>nestjs</category>
      <category>performance</category>
    </item>
    <item>
      <title>🐳 Minecraft + Docker: My personal config to stop suffering with Java</title>
      <dc:creator>Tomás Alegre Sepúlveda</dc:creator>
      <pubDate>Thu, 05 Feb 2026 04:19:41 +0000</pubDate>
      <link>https://forem.com/alpha018/minecraft-docker-my-personal-config-to-stop-suffering-with-java-35a7</link>
      <guid>https://forem.com/alpha018/minecraft-docker-my-personal-config-to-stop-suffering-with-java-35a7</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/alpha018/minecraft-docker-mi-configuracion-personal-para-no-sufrir-con-java-27k"&gt;🇪🇸 Lee este post en Español&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm no Java guru, and I'll be honest: managing Minecraft servers has always felt like a weird puzzle to me. Even though Minecraft might look like a "random" game about placing blocks, the technical management behind a server (threads, memory, garbage collection) is a fascinating optimization challenge.&lt;/p&gt;

&lt;p&gt;Back in the day, I tried creating plugins (with... "interesting" results), but I learned the hard way that the real battle is almost always the configuration.&lt;/p&gt;

&lt;p&gt;Has this happened to you? You want to play Survival with friends, you search for "best paper 1.21 config", and you end up with 20 open tabs and three wikis that contradict each other.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Initiative: Enough Chaos
&lt;/h2&gt;

&lt;p&gt;Tired of copying and pasting the same configuration lines over and over again, I decided to build &lt;strong&gt;&lt;a href="https://github.com/Alpha018/paper-config-optimized" rel="noopener noreferrer"&gt;Paper Config Optimized&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I didn't want to make "just another guide". I wanted a place to truly centralize the community's best practices into ready-to-use files. In fact, I've become such a fan of this "infrastructure as code" approach that I recently made my first contribution to the official Docker image repository we use (PR #3076 in &lt;code&gt;itzg/docker-minecraft-server&lt;/code&gt;) and plan to keep contributing to improve the ecosystem.&lt;/p&gt;

&lt;h2&gt;
  
  
  "The Recipe" (Configuration)
&lt;/h2&gt;

&lt;p&gt;Here is the configuration I always use. It's basically my "copy, paste, and forget" template.&lt;/p&gt;

&lt;p&gt;The important thing here is that we are telling Docker two key things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;"Use Aikar's Flags"&lt;/strong&gt;: So Java doesn't get picky with memory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Download configs from the repo"&lt;/strong&gt;: So the game runs smoothly without having to touch anything.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;mc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;itzg/minecraft-server&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;paper-lite&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;25565:25565"&lt;/span&gt;

    &lt;span class="c1"&gt;# Interactive console (never hurts to see what's happening)&lt;/span&gt;
    &lt;span class="na"&gt;stdin_open&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;tty&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Mojang bureaucracy and software choice (Paper flies)&lt;/span&gt;
      &lt;span class="na"&gt;EULA&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TRUE"&lt;/span&gt;
      &lt;span class="na"&gt;TYPE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PAPER"&lt;/span&gt;

      &lt;span class="c1"&gt;# Always the latest. Living on the edge.&lt;/span&gt;
      &lt;span class="na"&gt;VERSION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LATEST"&lt;/span&gt;

      &lt;span class="c1"&gt;# We activate Aikar's flags.&lt;/span&gt;
      &lt;span class="c1"&gt;# Trust me, you don't want to manage the Garbage Collector manually.&lt;/span&gt;
      &lt;span class="na"&gt;USE_AIKAR_FLAGS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;

      &lt;span class="c1"&gt;# Timezone (change to yours so logs make sense)&lt;/span&gt;
      &lt;span class="na"&gt;TZ&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;America/Santiago"&lt;/span&gt;

      &lt;span class="c1"&gt;# ✨ THE MAGIC:&lt;/span&gt;
      &lt;span class="c1"&gt;# Automatically injects optimized configs from the repo at startup.&lt;/span&gt;
      &lt;span class="na"&gt;PAPER_CONFIG_REPO&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://raw.githubusercontent.com/Alpha018/paper-config-optimized/main"&lt;/span&gt;

    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./minecraft-data:/data&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How to run this?
&lt;/h3&gt;

&lt;p&gt;It's as simple as putting that into a &lt;code&gt;docker-compose.yml&lt;/code&gt; file, opening a terminal in the folder, and throwing in a:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  ⚖️ Balance: Pros and Cons
&lt;/h2&gt;

&lt;p&gt;As with everything in engineering, there are no silver bullets. This configuration is my favorite, but we have to be transparent:&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ The Good (Pros)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Speed&lt;/strong&gt;: Go from "zero" to "optimized server" in 2 minutes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintenance&lt;/strong&gt;: By using &lt;code&gt;PAPER_CONFIG_REPO&lt;/code&gt;, if performance improvements come out tomorrow, you just have to restart the container to apply them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stability&lt;/strong&gt;: Using &lt;em&gt;Aikar's Flags&lt;/em&gt; eliminates 90% of lag spikes caused by Java.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ❌ The Bad (Cons)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Black Box&lt;/strong&gt;: If you like manually adjusting every millimeter of &lt;code&gt;view-distance&lt;/code&gt;, you might feel like you're losing some control (although you can always overwrite the files).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependency&lt;/strong&gt;: You depend on my repository (or your own fork) being online to download the configs at startup.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🚀 For Advanced Users
&lt;/h2&gt;

&lt;p&gt;This configuration is ideal for what I call "Lite Technical Survival". But if you are planning to set up a massive public community (50+ players) or want to understand why 12GB of RAM is my magic number for large servers, I wrote a much deeper technical guide.&lt;/p&gt;

&lt;p&gt;☕ &lt;strong&gt;&lt;a href="https://buymeacoffee.com/alpha018/ultra-optimized-papermc-docker-a-technical-deep-dive" rel="noopener noreferrer"&gt;Check out the Advanced Technical Guide on Buy Me a Coffee&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  💌 Special Acknowledgments
&lt;/h3&gt;

&lt;p&gt;To a girl who, without knowing it, pushed me to keep creating and coding in my free time. To the one who was once my spark: for "YLP", with gratitude... and with scars that also teach.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>minecraft</category>
      <category>performance</category>
      <category>java</category>
    </item>
    <item>
      <title>🐳 Minecraft + Docker: Mi configuración personal para no sufrir con Java</title>
      <dc:creator>Tomás Alegre Sepúlveda</dc:creator>
      <pubDate>Thu, 05 Feb 2026 04:18:11 +0000</pubDate>
      <link>https://forem.com/alpha018/minecraft-docker-mi-configuracion-personal-para-no-sufrir-con-java-27k</link>
      <guid>https://forem.com/alpha018/minecraft-docker-mi-configuracion-personal-para-no-sufrir-con-java-27k</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/alpha018/minecraft-docker-my-personal-config-to-stop-suffering-with-java-35a7"&gt;🇺🇸 Read this post in English&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No soy ningún gurú de Java, y seré honesto: gestionar servidores de Minecraft siempre me pareció un rompecabezas extraño. A pesar de que Minecraft pueda parecer un juego "random" de poner cubitos, la gestión técnica detrás de un servidor (hilos, memoria, recolección de basura) es un reto de optimización fascinante.&lt;/p&gt;

&lt;p&gt;En su momento intenté crear plugins (con resultados... "interesantes"), pero aprendí a la mala que la verdadera batalla casi siempre es la configuración.&lt;/p&gt;

&lt;p&gt;¿Te ha pasado? Quieres jugar un Survival con amigos, buscas "mejor configuración paper 1.21" y terminas con 20 pestañas abiertas y tres wikis que se contradicen.&lt;/p&gt;

&lt;h2&gt;
  
  
  La Iniciativa: Basta de Caos
&lt;/h2&gt;

&lt;p&gt;Cansado de copiar y pegar las mismas líneas de configuración una y otra vez, se me ocurrió armar &lt;strong&gt;&lt;a href="https://github.com/Alpha018/paper-config-optimized" rel="noopener noreferrer"&gt;Paper Config Optimized&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;No quería hacer "otra guía más". Quería un lugar donde centralizar de verdad las mejores prácticas de la comunidad en archivos listos para usar. De hecho, me he vuelto tan fan de esta "infraestructura como código" que hace poco hice mi primera contribución al repositorio oficial de la imagen de Docker que usamos (PR #3076 en &lt;code&gt;itzg/docker-minecraft-server&lt;/code&gt;) y planeo seguir aportando para mejorar el ecosistema.&lt;/p&gt;

&lt;h2&gt;
  
  
  "La Receta" (Configuración)
&lt;/h2&gt;

&lt;p&gt;Te dejo por aquí la configuración que uso siempre. Es básicamente mi plantilla de "copiar, pegar y olvidarse".&lt;/p&gt;

&lt;p&gt;Lo importante aquí es que le estamos diciendo a Docker dos cosas clave:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;"Usa Aikar's Flags"&lt;/strong&gt;: Para que Java no se ponga caprichoso con la memoria.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Descargar las configs del repositorio"&lt;/strong&gt;: Para que el juego corra fluido sin tener que tocar nada.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;mc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;itzg/minecraft-server&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;paper-lite&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;25565:25565"&lt;/span&gt;

    &lt;span class="c1"&gt;# Consola interactiva (nunca está de más para ver qué pasa)&lt;/span&gt;
    &lt;span class="na"&gt;stdin_open&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;tty&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Papeleo burocrático de Mojang y elección del software (Paper vuela)&lt;/span&gt;
      &lt;span class="na"&gt;EULA&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TRUE"&lt;/span&gt;
      &lt;span class="na"&gt;TYPE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PAPER"&lt;/span&gt;

      &lt;span class="c1"&gt;# Siempre a la última. Vivir al límite.&lt;/span&gt;
      &lt;span class="na"&gt;VERSION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LATEST"&lt;/span&gt;

      &lt;span class="c1"&gt;# Activamos las flags de Aikar.&lt;/span&gt;
      &lt;span class="c1"&gt;# Créeme, no quieres gestionar el Garbage Collector manualmente.&lt;/span&gt;
      &lt;span class="na"&gt;USE_AIKAR_FLAGS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;

      &lt;span class="c1"&gt;# Zona horaria (cámbiala a la tuya para no volverte loco con los logs)&lt;/span&gt;
      &lt;span class="na"&gt;TZ&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;America/Santiago"&lt;/span&gt;

      &lt;span class="c1"&gt;# ✨ LA MAGIA:&lt;/span&gt;
      &lt;span class="c1"&gt;# Inyecta las configs optimizadas del repositorio automáticamente al iniciar.&lt;/span&gt;
      &lt;span class="na"&gt;PAPER_CONFIG_REPO&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://raw.githubusercontent.com/Alpha018/paper-config-optimized/main"&lt;/span&gt;

    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./minecraft-data:/data&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ¿Cómo correr esto?
&lt;/h3&gt;

&lt;p&gt;Es tan simple como meter eso en un archivo &lt;code&gt;docker-compose.yml&lt;/code&gt;, abrir una terminal en la carpeta y tirar un:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  ⚖️ Balance: Pros y Contras
&lt;/h2&gt;

&lt;p&gt;Como en todo en ingeniería, no hay balas de plata. Esta configuración es mi favorita, pero hay que ser transparentes:&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ Lo Bueno (Pros)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Velocidad&lt;/strong&gt;: Pasas de "cero" a "servidor optimizado" en 2 minutos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mantenimiento&lt;/strong&gt;: Al usar &lt;code&gt;PAPER_CONFIG_REPO&lt;/code&gt;, si mañana salen nuevas mejoras de rendimiento, solo tienes que reiniciar el contenedor para aplicarlas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Estabilidad&lt;/strong&gt;: Usar las &lt;em&gt;Aikar's Flags&lt;/em&gt; elimina el 90% de los tirones de lag por culpa de Java.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ❌ Lo Malo (Contras)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Caja Negra&lt;/strong&gt;: Si te gusta ajustar cada milímetro de la &lt;code&gt;view-distance&lt;/code&gt; a mano, sentirás que pierdes algo de control (aunque siempre puedes sobrescribir los archivos).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependencia&lt;/strong&gt;: Dependes de que mi repositorio (o el fork que te hagas) esté en línea para descargar las configs al inicio.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🚀 Para Usuarios Avanzados
&lt;/h2&gt;

&lt;p&gt;Esta configuración es ideal para lo que llamo "Survival Técnico Lite". Pero si estás planeando montar una comunidad pública masiva (50+ jugadores) o quieres entender por qué 12GB de RAM es mi número mágico para servidores grandes, escribí una guía técnica mucho más profunda.&lt;/p&gt;

&lt;p&gt;☕ &lt;strong&gt;&lt;a href="https://buymeacoffee.com/alpha018/papermc-ultra-optimizado-con-docker-una-inmersin-tcnica" rel="noopener noreferrer"&gt;Échale un ojo a la Guía Técnica Avanzada en Buy Me a Coffee&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  💌 Agradecimientos Especiales
&lt;/h3&gt;

&lt;p&gt;A una chica que, sin saberlo, me empujó a seguir creando y programando en mi tiempo libre. A quien alguna vez fue mi chispa: para “YLP”, con gratitud… y con cicatrices que también enseñan.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>minecraft</category>
      <category>performance</category>
      <category>java</category>
    </item>
    <item>
      <title>🚀 Supercarga la Autenticación en NestJS con Firebase</title>
      <dc:creator>Tomás Alegre Sepúlveda</dc:creator>
      <pubDate>Sun, 04 Jan 2026 09:13:33 +0000</pubDate>
      <link>https://forem.com/alpha018/supercarga-la-autenticacion-en-nestjs-con-firebase-1kc0</link>
      <guid>https://forem.com/alpha018/supercarga-la-autenticacion-en-nestjs-con-firebase-1kc0</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;🇺🇸 &lt;a href="https://dev.to/alpha018/integrating-firebase-authentication-into-nestjs-with-nestjs-firebase-auth-55m6"&gt;Read this post in English&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Si vienes del mundo de &lt;strong&gt;NestJS&lt;/strong&gt;, te encantan los decoradores, la inyección de dependencias y el tipado fuerte. Pero cuando se trata de integrar &lt;strong&gt;Firebase Authentication&lt;/strong&gt;, a menudo se siente como una tarea pesada: &lt;em&gt;guards repetitivos&lt;/em&gt;, verificación manual de tokens y manejo de roles &lt;em&gt;desordenado&lt;/em&gt; que ensucia nuestros controladores.&lt;/p&gt;

&lt;p&gt;Hoy quiero reintroducir &lt;strong&gt;&lt;code&gt;nestjs-firebase-auth&lt;/code&gt;&lt;/strong&gt;, una librería diseñada para cerrar esa brecha y tratar la autenticación de Firebase con el respeto que merece en una arquitectura moderna de NestJS.&lt;/p&gt;




&lt;h2&gt;
  
  
  🤔 ¿Por qué creé esto?
&lt;/h2&gt;

&lt;p&gt;Firebase Authentication es robusto, pero el SDK Admin oficial es solo eso: un SDK. No sabe nada sobre Guards, Decoradores o Inyección de Dependencias de NestJS.&lt;/p&gt;

&lt;p&gt;No deberías tener que escribir middleware personalizado para decodificar tokens o verificar claims manualmente en cada controlador. &lt;code&gt;nestjs-firebase-auth&lt;/code&gt; cierra esta brecha, dándote una API simplificada y declarativa para asegurar tu aplicación.&lt;/p&gt;

&lt;p&gt;Con el último lanzamiento &lt;strong&gt;v1.9.0&lt;/strong&gt;, he pulido la experiencia aún más con decoradores más limpios y una mejor integración con otros servicios de Firebase como Firestore.&lt;/p&gt;

&lt;h3&gt;
  
  
  Instalación
&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; @alpha018/nestjs-firebase-auth firebase-admin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🛠️ Profundizando en el Código
&lt;/h2&gt;

&lt;p&gt;Veamos ejemplos más realistas de lo que puedes lograr.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Autenticación Declarativa &lt;code&gt;(@Auth)&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Olvídate de importar y vincular manualmente los guards con &lt;code&gt;@UseGuards(FirebaseGuard)&lt;/code&gt;. Ahora tienes un decorador &lt;code&gt;@Auth()&lt;/code&gt; limpio que hace clara tu intención.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Auth&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@alpha018/nestjs-firebase-auth&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="nd"&gt;Auth&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// 🛡️ Protege todo este controlador&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UsersController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;profile&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;getProfile&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Estos datos son seguros.&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Control de Acceso Basado en Roles (RBAC)
&lt;/h3&gt;

&lt;p&gt;Manejar roles no debería ser complicado. Utilizo los Custom Claims de Firebase para almacenar los roles del usuario, y el decorador &lt;code&gt;@Roles&lt;/code&gt; para hacerlos cumplir sin escribir una sola línea de lógica de validación.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Auth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Roles&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@alpha018/nestjs-firebase-auth&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="nd"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AdminController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Auth&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Roles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ADMIN&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SUPERUSER&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// ⛔ Solo permite usuarios con estos roles&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;getDashboard&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Datos de administración ultra secretos&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nota:&lt;/strong&gt; El decorador &lt;code&gt;@Roles&lt;/code&gt; activa automáticamente la lógica de validación de roles subyacente cuando se usa con &lt;code&gt;@Auth&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3. Integrando con otros servicios de Firebase
&lt;/h3&gt;

&lt;p&gt;¿Necesitas hablar con Firestore o enviar un mensaje FCM específico? Siempre ha sido posible, pero quiero destacar lo fácil que es acceder a la instancia inicializada de &lt;code&gt;admin.app.App&lt;/code&gt; directamente desde mi provider.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;FirebaseProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@alpha018/nestjs-firebase-auth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getFirestore&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;firebase-admin/firestore&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="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserDataService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;firebaseProvider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FirebaseProvider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;saveUserData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 🔥 Usa la instancia de app del módulo de auth para Firestore&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firestore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getFirestore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firebaseProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;firestore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;set&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🎯 Mejores Casos de Uso
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Aplicaciones SaaS&lt;/strong&gt;: Donde necesitas autenticación robusta y segura con configuración mínima.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Microservicios&lt;/strong&gt;: Compartir lógica de autenticación a través de múltiples servicios NestJS fácilmente usando un enfoque de librería compartida.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sistemas RBAC&lt;/strong&gt;: Apps que requieren control granular sobre quién puede hacer qué (Admins vs. Usuarios).&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  ⚠️ Trade-offs (Honestidad Ante Todo)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Validación Remota&lt;/strong&gt;: Por defecto, verifico los roles obteniendo el registro de usuario desde Firebase. Esto asegura que los roles estén &lt;em&gt;siempre&lt;/em&gt; actualizados (sin tokens obsoletos), pero incurre en una pequeña latencia de red.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validación Local&lt;/strong&gt;: Puedes optar por &lt;code&gt;useLocalRoles: true&lt;/code&gt; para validar contra los claims del token ID directamente (cero latencia), pero debes aceptar que los cambios de roles efectivamente se actualizan solo cuando el usuario refresca su token.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💌 Agradecimientos Especiales
&lt;/h2&gt;

&lt;p&gt;Este proyecto ha sido construido con muchas horas de dedicación y amor por el código.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Para una chica que siempre me inspira en silencio, y su sola existencia me incentiva a seguir programando en mis tiempos libres, gracias "YLP".&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  ☕ Apoya el Proyecto
&lt;/h2&gt;

&lt;p&gt;Si encontraste este proyecto útil o interesante, por favor considera apoyarlo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⭐️ &lt;strong&gt;Estrella en GitHub&lt;/strong&gt;: Es gratis y ayuda a otros a encontrar la librería.&lt;/li&gt;
&lt;li&gt;☕ &lt;strong&gt;Cómprame un Café&lt;/strong&gt;: Si quieres ir un paso más allá, puedes hacerlo en &lt;a href="https://buymeacoffee.com/alpha018" rel="noopener noreferrer"&gt;buymeacoffee.com/alpha018&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔗 Pruébalo y Contribuye
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repo&lt;/strong&gt;: &lt;a href="https://github.com/Alpha018/nestjs-firebase-auth" rel="noopener noreferrer"&gt;github.com/Alpha018/nestjs-firebase-auth&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Autor&lt;/strong&gt;: &lt;a href="https://dev.to/alpha018"&gt;dev.to/alpha018&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;¿Qué piensas? ¿Estás listo para simplificar tu flujo de autenticación? 👇&lt;/p&gt;

</description>
      <category>firebase</category>
      <category>typescript</category>
      <category>nestjs</category>
      <category>security</category>
    </item>
    <item>
      <title>🚀 NestJS + RedisOM: Elevando el manejo de Estado a otro nivel</title>
      <dc:creator>Tomás Alegre Sepúlveda</dc:creator>
      <pubDate>Sat, 03 Jan 2026 21:19:40 +0000</pubDate>
      <link>https://forem.com/alpha018/nestjs-redisom-elevando-el-manejo-de-estado-a-otro-nivel-46bo</link>
      <guid>https://forem.com/alpha018/nestjs-redisom-elevando-el-manejo-de-estado-a-otro-nivel-46bo</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;🇺🇸 &lt;a href="https://dev.to/alpha018/nestjs-redisom-taking-state-management-to-the-next-level-5gli"&gt;Read this post in English&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Si vienes del mundo de &lt;strong&gt;NestJS&lt;/strong&gt;, amas los decoradores, la inyección de dependencias y el tipado fuerte. Pero cuando toca integrar &lt;strong&gt;Redis&lt;/strong&gt;, a menudo sentimos que retrocedemos 10 años: &lt;em&gt;magic strings&lt;/em&gt;, parsers manuales de JSON y comandos &lt;em&gt;low-level&lt;/em&gt; que ensucian nuestra lógica de negocio.&lt;/p&gt;

&lt;p&gt;Hoy quiero presentarles &lt;strong&gt;&lt;code&gt;nestjs-redisom&lt;/code&gt;&lt;/strong&gt;, una librería open-source diseñada para cerrar esa brecha y tratar a Redis con el respeto que se merece en una arquitectura empresarial moderna.&lt;/p&gt;




&lt;h2&gt;
  
  
  🤔 ¿Por qué creé esto?
&lt;/h2&gt;

&lt;p&gt;Redis ha evolucionado. Ya no es solo un caché key-value. Con &lt;strong&gt;Redis Stack&lt;/strong&gt; (RedisJSON + RediSearch), se ha convertido en una base de datos de documentos en tiempo real increíblemente potente.&lt;/p&gt;

&lt;p&gt;Sin embargo, usar estas features desde Node.js suele ser verboso. &lt;strong&gt;Redis OM&lt;/strong&gt; (la librería oficial) ayuda, pero integrarla en NestJS requería mucho &lt;em&gt;boilerplate&lt;/em&gt;. &lt;code&gt;nestjs-redisom&lt;/code&gt; elimina esa fricción, dándote una experiencia nativa.&lt;/p&gt;

&lt;h3&gt;
  
  
  Instalación
&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;nestjs-redisom redis-om redis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🛠️ Profundizando en el Código
&lt;/h2&gt;

&lt;p&gt;Veamos ejemplos más realistas de lo que puedes lograr.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Modelado de Datos Complejos (Anidados)
&lt;/h3&gt;

&lt;p&gt;No estamos limitados a strings planos. Podemos modelar objetos complejos con validación implícita.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 1. Definimos una clase anidada&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Address&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Prop&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;indexed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Indexado para buscar "Usuarios en Madrid"&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Prop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;zipCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Definimos la Entidad Principal&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserProfile&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BaseEntity&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Prop&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;textSearch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;bio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Prop&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="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Address&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="c1"&gt;// Mapeo anidado&lt;/span&gt;
  &lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Prop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt; &lt;span class="c1"&gt;// Arrays de strings&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Búsquedas Avanzadas (Fluent API)
&lt;/h3&gt;

&lt;p&gt;Aquí es donde &lt;code&gt;nestjs-redisom&lt;/code&gt; brilla. Olvídate de aprender la sintaxis de comandos de RediSearch (&lt;code&gt;FT.SEARCH index "@field:value ..."&lt;/code&gt;). Usa esta API fluida y tipada.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Encuentra usuarios que vivan en 'New York', tengan el rol 'admin' y cuya bio mencione 'typescript'"&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&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;userRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;address_city&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;New York&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Busca en objeto anidado&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;roles&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;contain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;            &lt;span class="c1"&gt;// Busca en Array&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bio&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;typescript&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;         &lt;span class="c1"&gt;// Full-Text Search&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="nf"&gt;page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                      &lt;span class="c1"&gt;// Paginación nativa&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Expiración de Documentos (TTL)
&lt;/h3&gt;

&lt;p&gt;Manejar sesiones o caché temporal es trivial. No necesitas scripts de Lua ni nada por el estilo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Crea una sesión y haz que expire en 1 hora&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;createSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&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;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessionRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessionRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expire&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessionRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;idField&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;session&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;
  
  
  🎯 Mejores Casos de Uso
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;High-Speed Objects&lt;/strong&gt;: Carritos de compra, Perfiles de Jugador, Sesiones. Datos que lees/escribes 1000 veces por segundo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caché con "Poderes"&lt;/strong&gt;: A veces necesitas invalidar caché por criterio (&lt;em&gt;"borrar caché de todos los productos de la categoría X"&lt;/em&gt;). Con Redis normal es imposible (o lento con &lt;code&gt;KEYS *&lt;/code&gt;). Con RedisOM es una &lt;em&gt;query&lt;/em&gt; de O(1) o O(N log N).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Geo-Spatial Features&lt;/strong&gt;: "Encuentra conductores cerca de mi", usando la potencia nativa de Redis para geo-hashing.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  ⚠️ Trade-offs (Honestidad ante todo)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;RAM es Dinero&lt;/strong&gt;: Redis guarda todo en memoria. No uses esto para guardar logs de auditoría de 5 años.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Eventual Consistency&lt;/strong&gt;: Los índices de búsqueda se actualizan asíncronamente (milisegundos). No es ACID estricto para búsquedas inmediatas post-escritura.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💌 Agradecimientos Especiales
&lt;/h2&gt;

&lt;p&gt;Este proyecto ha sido construido con muchas horas de dedicación y amor por el código.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Para una chica que siempre me inspira en silencio, y su sola existencia me incentiva a seguir programando en mis tiempos libres, gracias "YLP".&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  ☕ Apoya el Proyecto
&lt;/h2&gt;

&lt;p&gt;Si este proyecto te ha sido útil o te parece interesante, considera apoyarlo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⭐️ &lt;strong&gt;Star en GitHub&lt;/strong&gt;: Es gratis y ayuda a que más gente lo encuentre.&lt;/li&gt;
&lt;li&gt;☕ &lt;strong&gt;Invítame un Café&lt;/strong&gt;: Si quieres ir un paso más allá, puedes hacerlo en &lt;a href="https://buymeacoffee.com/alpha018" rel="noopener noreferrer"&gt;buymeacoffee.com/alpha018&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔗 Pruébalo y Contribuye
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repo&lt;/strong&gt;: &lt;a href="https://github.com/Alpha018/nestjs-redisom" rel="noopener noreferrer"&gt;github.com/Alpha018/nestjs-redisom&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Autor&lt;/strong&gt;: &lt;a href="https://dev.to/alpha018"&gt;dev.to/alpha018&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;¿Qué opinan? ¿Le darán una oportunidad a Redis como base de datos de documentos? 👇&lt;/p&gt;

</description>
      <category>redis</category>
      <category>typescript</category>
      <category>node</category>
      <category>nestjs</category>
    </item>
    <item>
      <title>🚀 NestJS + RedisOM: Taking State Management to the Next Level</title>
      <dc:creator>Tomás Alegre Sepúlveda</dc:creator>
      <pubDate>Fri, 02 Jan 2026 01:39:59 +0000</pubDate>
      <link>https://forem.com/alpha018/nestjs-redisom-taking-state-management-to-the-next-level-5gli</link>
      <guid>https://forem.com/alpha018/nestjs-redisom-taking-state-management-to-the-next-level-5gli</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;🇪🇸 &lt;a href="https://dev.to/alpha018/nestjs-redisom-elevando-el-manejo-de-estado-a-otro-nivel-46bo"&gt;Lee este post en Español&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you come from the &lt;strong&gt;NestJS&lt;/strong&gt; world, you love decorators, dependency injection, and strong typing. But when it comes to integrating &lt;strong&gt;Redis&lt;/strong&gt;, it often feels like we're stepping back 10 years: &lt;em&gt;magic strings&lt;/em&gt;, manual JSON parsers, and &lt;em&gt;low-level&lt;/em&gt; commands that clutter our business logic.&lt;/p&gt;

&lt;p&gt;Today I want to introduce &lt;strong&gt;&lt;code&gt;nestjs-redisom&lt;/code&gt;&lt;/strong&gt;, an open-source library designed to bridge that gap and treat Redis with the respect it deserves in a modern enterprise architecture.&lt;/p&gt;




&lt;h2&gt;
  
  
  🤔 Why did I create this?
&lt;/h2&gt;

&lt;p&gt;Redis has evolved. It is no longer just a key-value cache. With &lt;strong&gt;Redis Stack&lt;/strong&gt; (RedisJSON + RediSearch), it has become an incredibly powerful real-time document database.&lt;/p&gt;

&lt;p&gt;However, using these features from Node.js is often verbose. &lt;strong&gt;Redis OM&lt;/strong&gt; (the official library) helps, but integrating it into NestJS required a lot of &lt;em&gt;boilerplate&lt;/em&gt;. &lt;code&gt;nestjs-redisom&lt;/code&gt; removes that friction, giving you a native experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation
&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;nestjs-redisom redis-om redis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🛠️ Deep Dive into the Code
&lt;/h2&gt;

&lt;p&gt;Let's look at more realistic examples of what you can achieve.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Complex Data Modeling (Nested)
&lt;/h3&gt;

&lt;p&gt;We aren't limited to flat strings. We can model complex objects with implicit validation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 1. Define a Nested Class&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Address&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Prop&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;indexed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Indexed to search "Users in Madrid"&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Prop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;zipCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Define the Main Entity&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserProfile&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BaseEntity&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Prop&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;textSearch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;bio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Prop&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="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Address&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="c1"&gt;// Nested mapping&lt;/span&gt;
  &lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Prop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt; &lt;span class="c1"&gt;// Array of strings&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Advanced Searching (Fluent API)
&lt;/h3&gt;

&lt;p&gt;This is where &lt;code&gt;nestjs-redisom&lt;/code&gt; shines. Forget about learning RediSearch command syntax (&lt;code&gt;FT.SEARCH index "@field:value ..."&lt;/code&gt;). Use this fluent, typed API.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Find users living in 'New York', having the role 'admin', and whose bio mentions 'typescript'"&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&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;userRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;address_city&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;New York&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Search in nested object&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;roles&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;contain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;            &lt;span class="c1"&gt;// Search in Array&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bio&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;typescript&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;         &lt;span class="c1"&gt;// Full-Text Search&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="nf"&gt;page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                      &lt;span class="c1"&gt;// Native pagination&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Document Expiration (TTL)
&lt;/h3&gt;

&lt;p&gt;Handling sessions or temporary cache is trivial. You don't need Lua scripts or anything like that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Create a session and make it expire in 1 hour&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;createSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&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;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessionRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessionRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expire&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessionRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;idField&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;session&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;
  
  
  🎯 Best Use Cases
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;High-Speed Objects&lt;/strong&gt;: Shopping Carts, Player Profiles, Sessions. Data you read/write 1000 times per second.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Powered-Up Cache&lt;/strong&gt;: Sometimes you need to invalidate cache by criteria (&lt;em&gt;"clear cache for all products in category X"&lt;/em&gt;). With standard Redis, this is impossible (or slow with &lt;code&gt;KEYS *&lt;/code&gt;). With RedisOM, it's an O(1) or O(N log N) &lt;em&gt;query&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Geo-Spatial Features&lt;/strong&gt;: "Find drivers near me", using Redis native power for geo-hashing.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  ⚠️ Trade-offs (Honesty First)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;RAM is Money&lt;/strong&gt;: Redis stores everything in memory. Don't use this to store 5 years of audit logs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Eventual Consistency&lt;/strong&gt;: Search indexes update asynchronously (milliseconds). It is not strict ACID for immediate post-write searches.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💌 Special Acknowledgments
&lt;/h2&gt;

&lt;p&gt;This project has been built with many hours of dedication and love for code.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;To a girl who always inspires me in silence, and whose mere existence incentivizes me to keep programming in my free time, thank you "YLP".&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  ☕ Support the Project
&lt;/h2&gt;

&lt;p&gt;If you found this project useful or interesting, please consider supporting it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⭐️ &lt;strong&gt;Star on GitHub&lt;/strong&gt;: It's free and helps others find the library.&lt;/li&gt;
&lt;li&gt;☕ &lt;strong&gt;Buy me a Coffee&lt;/strong&gt;: If you want to go the extra mile, you can do so at &lt;a href="https://buymeacoffee.com/alpha018" rel="noopener noreferrer"&gt;buymeacoffee.com/alpha018&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔗 Try it and Contribute
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repo&lt;/strong&gt;: &lt;a href="https://github.com/Alpha018/nestjs-redisom" rel="noopener noreferrer"&gt;github.com/Alpha018/nestjs-redisom&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Author&lt;/strong&gt;: &lt;a href="https://dev.to/alpha018"&gt;dev.to/alpha018&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What do you think? Will you give Redis a chance as a document database? 👇&lt;/p&gt;

</description>
      <category>redis</category>
      <category>typescript</category>
      <category>node</category>
      <category>nestjs</category>
    </item>
    <item>
      <title>🚀 Supercharge NestJS Authentication with Firebase</title>
      <dc:creator>Tomás Alegre Sepúlveda</dc:creator>
      <pubDate>Tue, 10 Sep 2024 22:56:14 +0000</pubDate>
      <link>https://forem.com/alpha018/integrating-firebase-authentication-into-nestjs-with-nestjs-firebase-auth-55m6</link>
      <guid>https://forem.com/alpha018/integrating-firebase-authentication-into-nestjs-with-nestjs-firebase-auth-55m6</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;🇪🇸 &lt;a href="https://dev.to/alpha018/supercarga-la-autenticacion-en-nestjs-con-firebase-1kc0"&gt;Lee este post en Español&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you're building a modern application with NestJS, you probably love its modularity, decorators, and type safety. But when it comes to authentication, integrating external providers often feels like a chore—boilerplate guards, manual token verification, and messy role handling.&lt;/p&gt;

&lt;p&gt;Today, I want to re-introduce &lt;code&gt;nestjs-firebase-auth&lt;/code&gt;, a library designed to make Firebase Authentication feel native to the NestJS ecosystem.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤔 Why this library?
&lt;/h2&gt;

&lt;p&gt;Firebase Authentication is robust, but the official Admin SDK is just that—an SDK. It doesn't know about NestJS Guards, Decorators, or Dependency Injection.&lt;/p&gt;

&lt;p&gt;You shouldn't have to write custom middleware to decode tokens or manually check claims in every controller. &lt;code&gt;nestjs-firebase-auth&lt;/code&gt; bridges this gap, giving you a simplified, declarative API for securing your application.&lt;/p&gt;

&lt;p&gt;With the latest &lt;strong&gt;v1.9.0&lt;/strong&gt; release, I've polished the experience even further with cleaner decorators and better integration with other Firebase services like Firestore.&lt;/p&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;npm &lt;span class="nb"&gt;install&lt;/span&gt; @alpha018/nestjs-firebase-auth firebase-admin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🛠️ Deep Dive into the Code
&lt;/h2&gt;

&lt;p&gt;Let's look at how this changes your daily workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Declarative Authentication (&lt;code&gt;@Auth&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Forget about manually importing and binding guards with &lt;code&gt;@UseGuards(FirebaseGuard)&lt;/code&gt;. You now have a clean &lt;code&gt;@Auth()&lt;/code&gt; decorator that makes your intention clear.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Auth&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@alpha018/nestjs-firebase-auth&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="nd"&gt;Auth&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// 🛡️ Protects this entire controller&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UsersController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;profile&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;getProfile&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;This data is secure.&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Role-Based Access Control (RBAC)
&lt;/h3&gt;

&lt;p&gt;Managing roles shouldn't be complicated. I use Firebase Custom Claims to store user roles, and the &lt;code&gt;@Roles&lt;/code&gt; decorator to enforce them without writing a single line of validation logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Auth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Roles&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@alpha018/nestjs-firebase-auth&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="nd"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AdminController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Auth&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Roles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ADMIN&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SUPERUSER&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// ⛔ Only allows users with these roles&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;getDashboard&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Top secret admin data&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The &lt;code&gt;@Roles&lt;/code&gt; decorator automatically triggers the underlying role validation logic when used with &lt;code&gt;@Auth&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3. Type-Safe User Access
&lt;/h3&gt;

&lt;p&gt;Accessing the current user's data is straightforward and type-safe using &lt;code&gt;@FirebaseUser&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;me&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;getProfile&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;FirebaseUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DecodedIdToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// `user` is the standard Firebase DecodedIdToken&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="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Integrating with other Firebase Services
&lt;/h3&gt;

&lt;p&gt;Need to talk to Firestore or send a clear-cut FCM message? It has always been possible, but I want to highlight how easy it is to access the initialized &lt;code&gt;admin.app.App&lt;/code&gt; instance directly from my provider.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;FirebaseProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@alpha018/nestjs-firebase-auth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getFirestore&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;firebase-admin/firestore&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="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserDataService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;firebaseProvider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FirebaseProvider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;saveUserData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 🔥 Use the auth module's app instance for Firestore&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firestore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getFirestore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firebaseProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;firestore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;set&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🎯 Best Use Cases
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SaaS Applications&lt;/strong&gt;: Where you need robust, secure authentication with minimal setup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Microservices&lt;/strong&gt;: Sharing authentication logic across multiple NestJS services easily using a shared library approach.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RBAC Systems&lt;/strong&gt;: Apps that require granular control over who can do what (Admins vs. Users).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ⚠️ Trade-offs (Honesty First)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Remote Validation&lt;/strong&gt;: By default, I verify roles by fetching the user record from Firebase. This ensures roles are &lt;em&gt;always&lt;/em&gt; up to date (no stale tokens), but it incurs a small network latency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local Validation&lt;/strong&gt;: You can opt-in to &lt;code&gt;useLocalRoles: true&lt;/code&gt; to validate against the ID token's claims directly (zero latency), but you must accept that role changes effectively update only when the user refreshes their token.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  💌 Special Acknowledgments
&lt;/h2&gt;

&lt;p&gt;This project has been built with many hours of dedication and love for code.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;To a girl who always inspires me in silence, and whose mere existence incentivizes me to keep programming in my free time, thank you "YLP".&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  ☕ Support the Project
&lt;/h2&gt;

&lt;p&gt;If you found this project useful or interesting, please consider supporting it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⭐️ &lt;strong&gt;Star on GitHub&lt;/strong&gt;: It's free and helps others find the library.&lt;/li&gt;
&lt;li&gt;☕ &lt;strong&gt;Buy me a Coffee&lt;/strong&gt;: If you want to go the extra mile, you can do so at &lt;a href="https://buymeacoffee.com/alpha018" rel="noopener noreferrer"&gt;buymeacoffee.com/alpha018&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔗 Try it and Contribute
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repo&lt;/strong&gt;: &lt;a href="https://github.com/Alpha018/nestjs-firebase-auth" rel="noopener noreferrer"&gt;github.com/Alpha018/nestjs-firebase-auth&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Author&lt;/strong&gt;: &lt;a href="https://dev.to/alpha018"&gt;dev.to/alpha018&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What do you think? Are you ready to simplify your authentication flow? 👇&lt;/p&gt;

</description>
      <category>firebase</category>
      <category>typescript</category>
      <category>nestjs</category>
      <category>security</category>
    </item>
  </channel>
</rss>
