<?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: Rents Argentina</title>
    <description>The latest articles on Forem by Rents Argentina (@rentsarg).</description>
    <link>https://forem.com/rentsarg</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%2F3931604%2F907620d3-88ab-4e12-87b4-7c353858b0d3.png</url>
      <title>Forem: Rents Argentina</title>
      <link>https://forem.com/rentsarg</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/rentsarg"/>
    <language>en</language>
    <item>
      <title>Cómo construimos Rents: gestión de alquileres para el mercado argentino con Expo + Node.js</title>
      <dc:creator>Rents Argentina</dc:creator>
      <pubDate>Thu, 14 May 2026 21:03:18 +0000</pubDate>
      <link>https://forem.com/rentsarg/como-construimos-rents-gestion-de-alquileres-para-el-mercado-argentino-con-expo-nodejs-e5o</link>
      <guid>https://forem.com/rentsarg/como-construimos-rents-gestion-de-alquileres-para-el-mercado-argentino-con-expo-nodejs-e5o</guid>
      <description>&lt;p&gt;El mercado de alquileres en Argentina es único: inflación alta, contratos ajustados por índices oficiales (ICL, IPC, CER), operaciones en doble moneda (ARS/USD), y regulación cambiante. Construir una app para propietarios argentinos requirió resolver estos desafíos específicos.&lt;/p&gt;

&lt;h2&gt;
  
  
  El problema
&lt;/h2&gt;

&lt;p&gt;Los propietarios con 2-10 propiedades en alquiler necesitan:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Controlar cuándo vence cada contrato&lt;/li&gt;
&lt;li&gt;Calcular el próximo ajuste según ICL&lt;/li&gt;
&lt;li&gt;Registrar cobros en pesos y dólares&lt;/li&gt;
&lt;li&gt;Enviar recordatorios a inquilinos por WhatsApp&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No existía ninguna herramienta diseñada para esta realidad.&lt;/p&gt;

&lt;h2&gt;
  
  
  La solución: Rents
&lt;/h2&gt;

&lt;p&gt;Construimos &lt;a href="https://rents.ar" rel="noopener noreferrer"&gt;Rents&lt;/a&gt; — una plataforma cross-platform (iOS, Android, Web) que automatiza la gestión de alquileres con lógica específica para Argentina.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stack tecnológico
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Frontend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Expo (React Native) + TypeScript&lt;/span&gt;
&lt;span class="na"&gt;Backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  &lt;span class="s"&gt;Node.js + Hono + TypeScript&lt;/span&gt;
&lt;span class="na"&gt;Database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PostgreSQL + Drizzle ORM&lt;/span&gt;
&lt;span class="na"&gt;Cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;    &lt;span class="s"&gt;Redis&lt;/span&gt;
&lt;span class="na"&gt;Storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  &lt;span class="s"&gt;Cloudflare R2&lt;/span&gt;
&lt;span class="na"&gt;Auth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;     &lt;span class="s"&gt;Lucia Auth + JWT&lt;/span&gt;
&lt;span class="na"&gt;Deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;   &lt;span class="s"&gt;Kamal (Docker) en VPS&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Decisiones técnicas clave
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Monorepo con Turborepo
&lt;/h3&gt;

&lt;p&gt;Usamos un monorepo con pnpm workspaces para compartir tipos y schemas de validación entre frontend y backend:&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;// packages/validation/src/schemas/contract.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createContractSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;propertyId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;tenantId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;startDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;monthlyRent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;positive&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ARS&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;USD&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
  &lt;span class="na"&gt;adjustmentIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ICL&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;IPC&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;CER&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;NONE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El mismo schema se usa tanto en el formulario de React Native como en la validación del endpoint de Hono.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Lógica de ajuste por índices
&lt;/h3&gt;

&lt;p&gt;El cálculo de ajuste de alquiler según ICL es complejo: cada período tiene una fecha de inicio, un índice base y uno de comparación. Lo modelamos como un scheduler de ajustes:&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;// Genera todos los períodos de ajuste automáticamente&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createAdjustmentSchedule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Contract&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;indexData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ICLData&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;periods&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generateAdjustmentPeriods&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;endDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;adjustmentFrequency&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;periods&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;period&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;contractId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;periodStart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;period&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;periodEnd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;period&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;baseIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getIndexForDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;indexData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;period&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;newIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getIndexForDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;indexData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;period&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;adjustmentFactor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// se calcula cuando llega la fecha&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;
  
  
  3. Multi-moneda sin complejidad extra
&lt;/h3&gt;

&lt;p&gt;Los contratos en dólares se registran en USD pero se puede llevar el tracking en ambas monedas. La clave fue no intentar convertir en tiempo real — simplemente guardamos la moneda del pago junto al monto.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Expo para cross-platform real
&lt;/h3&gt;

&lt;p&gt;Con Expo Router, el mismo código corre en iOS, Android y Web. La lógica de negocio está completamente separada de la UI, lo que facilita testear la parte crítica (cálculos financieros) sin el overhead de React Native Testing Library.&lt;/p&gt;

&lt;h2&gt;
  
  
  Desafíos del mercado argentino
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Cambios legales frecuentes&lt;/strong&gt;: La Ley de Alquileres cambió en 2023 y otra vez en 2024. Necesitábamos que los ajustes sean configurables, no hardcodeados.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ICL mensual&lt;/strong&gt;: El índice se publica el primer día hábil de cada mes. Tuvimos que construir un job que lo consulta y actualiza automáticamente los contratos pendientes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WhatsApp como canal primario&lt;/strong&gt;: Los propietarios argentinos no mandan emails — mandan WhatsApp. Integramos envío de recordatorios via API de WhatsApp Business directamente desde la plataforma.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resultado
&lt;/h2&gt;

&lt;p&gt;Una plataforma que entiende que &lt;code&gt;$ 150.000&lt;/code&gt; y &lt;code&gt;u$s 500&lt;/code&gt; son dos realidades distintas que coexisten en el mismo portfolio.&lt;/p&gt;

&lt;p&gt;Pueden probarla en &lt;strong&gt;&lt;a href="https://rents.ar" rel="noopener noreferrer"&gt;rents.ar&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;¿Tenés experiencia construyendo apps financieras para mercados con alta inflación? Me interesa el intercambio.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>reactnative</category>
      <category>showdev</category>
      <category>spanish</category>
    </item>
  </channel>
</rss>
