<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ramses Mata</title>
    <description>The latest articles on DEV Community by Ramses Mata (@ramtoearth).</description>
    <link>https://dev.to/ramtoearth</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%2F3524997%2Ff8c6e426-3d60-45d2-96cc-1c72e96aee0f.jpg</url>
      <title>DEV Community: Ramses Mata</title>
      <link>https://dev.to/ramtoearth</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ramtoearth"/>
    <language>en</language>
    <item>
      <title>Tiempo real en aplicaciones web: Polling vs Subscriptions</title>
      <dc:creator>Ramses Mata</dc:creator>
      <pubDate>Mon, 30 Mar 2026 18:48:03 +0000</pubDate>
      <link>https://dev.to/aws/tiempo-real-en-aplicaciones-web-polling-vs-subscriptions-2790</link>
      <guid>https://dev.to/aws/tiempo-real-en-aplicaciones-web-polling-vs-subscriptions-2790</guid>
      <description>&lt;p&gt;En este artículo vas a entender las dos estrategias principales para mantener datos frescos en tu aplicación: &lt;strong&gt;Polling&lt;/strong&gt; (tu app pregunta constantemente al servidor) y &lt;strong&gt;Subscriptions&lt;/strong&gt; (el servidor avisa a tu app cuando hay cambios). Vamos a compararlas lado a lado y darte un framework para elegir la correcta según tu caso de uso. Sin atarnos a ninguna tecnología específica, los conceptos aplican ya sea que uses GraphQL, REST, WebSockets, o cualquier otro protocolo o estilo de API.&lt;/p&gt;

&lt;p&gt;Imagina que estás usando una app de chat. Envías un mensaje y... ¿cómo sabe tu app que la otra persona ya respondió? ¿Cómo aparecen las notificaciones sin que tú hagas nada? ¿Cómo se actualizan los marcadores de un partido en vivo?&lt;/p&gt;

&lt;p&gt;Todas estas experiencias tienen algo en común: necesitan datos frescos sin que el usuario tenga que recargar la página. A esto le llamamos &lt;strong&gt;tiempo real&lt;/strong&gt; (o algo muy cercano a ello). Y hay dos formas principales de lograrlo: que tu app le pregunte al servidor constantemente si hay algo nuevo (&lt;strong&gt;Polling&lt;/strong&gt;), o que el servidor le avise a tu app en el momento que algo cambia (&lt;strong&gt;Subscriptions&lt;/strong&gt;).&lt;/p&gt;




&lt;h2&gt;
  
  
  ¿Qué es Polling?
&lt;/h2&gt;

&lt;p&gt;Polling es la estrategia más simple: tu aplicación le pregunta al servidor "¿hay algo nuevo?" cada cierto tiempo.&lt;/p&gt;

&lt;p&gt;Piénsalo como cuando eras niño en un viaje largo por carretera y le preguntabas a tus papás cada 5 minutos: "¿Ya llegamos? ¿Ya llegamos? ¿Ya llegamos?" No importa si la respuesta cambió o no, tú sigues preguntando.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Cómo funciona?
&lt;/h3&gt;

&lt;p&gt;Si eres más visual, este diagrama te puede ayudar a entender el flujo:&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%2Fzepv05hpl46foz76d53h.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%2Fzepv05hpl46foz76d53h.png" alt="Diagrama: Cómo funciona Polling - timeline de peticiones" width="800" height="1008"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Tu app envía una petición al servidor (por ejemplo, cada 5 segundos)&lt;/li&gt;
&lt;li&gt;El servidor responde con los datos más recientes&lt;/li&gt;
&lt;li&gt;Tu app actualiza la interfaz si hay cambios&lt;/li&gt;
&lt;li&gt;Espera el intervalo definido y repite&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;En JavaScript se ve algo así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Polling: preguntar cada 5 segundos&lt;/span&gt;
&lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;datos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/mensajes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;actualizarInterfaz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;datos&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple, ¿verdad? Y esa es precisamente su mayor fortaleza.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ventajas y desventajas del Polling
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ventajas&lt;/th&gt;
&lt;th&gt;Desventajas&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Fácil de implementar&lt;/strong&gt;: Es una petición HTTP normal en un loop&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Desperdicio de recursos&lt;/strong&gt;: Si no hay datos nuevos, la petición fue innecesaria&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Funciona con cualquier infraestructura&lt;/strong&gt;: No necesitas nada especial en el servidor&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Latencia&lt;/strong&gt;: Hay un retraso entre que el dato cambia y tu app se entera. Con un intervalo de 5s, en el peor caso tu usuario ve el dato 5 segundos tarde. Con subscriptions, la latencia típica es de ~50-150ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Stateless&lt;/strong&gt;: Cada petición es independiente, no hay conexiones persistentes&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Carga en el servidor&lt;/strong&gt;: Muchos clientes haciendo polling = muchas peticiones constantes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Fácil de debuggear&lt;/strong&gt;: Puedes ver cada petición en las herramientas de desarrollo del navegador&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Tradeoff del intervalo&lt;/strong&gt;: Intervalo corto = más carga, intervalo largo = más retraso&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Tip importante:&lt;/strong&gt; El intervalo de polling es una decisión de diseño clave. Un dashboard que muestra métricas cada minuto no necesita polling cada segundo. Ajusta el intervalo a lo que tu usuario realmente necesita ver.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  ¿Qué son las Subscriptions?
&lt;/h2&gt;

&lt;p&gt;Las Subscriptions invierten el modelo: en lugar de que tu app pregunte constantemente, el servidor le &lt;strong&gt;avisa&lt;/strong&gt; cuando hay algo nuevo.&lt;/p&gt;

&lt;p&gt;Siguiendo con la analogía del viaje: en vez de preguntar "¿ya llegamos?" cada 5 minutos, le dices a tus papás "avísenme cuando lleguemos" y te pones a hacer otra cosa. Ellos te avisan solo cuando es necesario.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Cómo funcionan?
&lt;/h3&gt;

&lt;p&gt;Si eres más visual, este diagrama te puede ayudar a ver la diferencia con polling:&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%2Fhuh65pppcm6w524je05r.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%2Fhuh65pppcm6w524je05r.png" alt="Diagrama: Cómo funcionan las Subscriptions - timeline de eventos" width="800" height="841"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Tu app establece una &lt;strong&gt;conexión persistente&lt;/strong&gt; con el servidor (generalmente usando &lt;a href="https://developer.mozilla.org/es/docs/Web/API/WebSockets_API" rel="noopener noreferrer"&gt;WebSockets&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Tu app le dice al servidor "avísame cuando cambien los mensajes"&lt;/li&gt;
&lt;li&gt;El servidor mantiene esa conexión abierta&lt;/li&gt;
&lt;li&gt;Cuando hay datos nuevos, el servidor los envía inmediatamente por esa conexión&lt;/li&gt;
&lt;li&gt;Tu app recibe los datos y actualiza la interfaz&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;En JavaScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Subscription: el servidor te avisa&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;conexion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/mensajes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;conexion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nuevoDato&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="nx"&gt;datos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;actualizarInterfaz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;datos&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;Fíjate en la diferencia: no hay intervalo, no hay loop. Tu app simplemente reacciona cuando llegan datos nuevos.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ventajas y desventajas de las Subscriptions
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ventajas&lt;/th&gt;
&lt;th&gt;Desventajas&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Tiempo real verdadero&lt;/strong&gt;: Los datos llegan al instante, sin retraso&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Más complejo de implementar&lt;/strong&gt;: Necesitas manejar conexiones persistentes, reconexiones, y estado&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Eficiente&lt;/strong&gt;: Solo se transfieren datos cuando realmente hay cambios&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Infraestructura más sofisticada&lt;/strong&gt;: El servidor necesita soportar WebSockets o protocolos similares&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Menos carga innecesaria&lt;/strong&gt;: No hay peticiones vacías cuando nada cambió&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Stateful&lt;/strong&gt;: El servidor mantiene el estado de cada conexión activa&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Mejor experiencia de usuario&lt;/strong&gt;: La app se siente viva y responsiva&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Escalabilidad diferente&lt;/strong&gt;: Miles de conexiones abiertas simultáneas requieren planificación&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Tip importante:&lt;/strong&gt; Las subscriptions no son magia, por detrás y mientras no vemos a simple vista, tecnologías como &lt;a href="https://developer.mozilla.org/es/docs/Web/API/WebSockets_API" rel="noopener noreferrer"&gt;WebSockets&lt;/a&gt; mantienen una conexión &lt;a href="https://www.fortinet.com/lat/resources/cyberglossary/tcp-ip" rel="noopener noreferrer"&gt;TCP&lt;/a&gt; abierta entre el cliente y el servidor. Esto permite comunicación bidireccional, pero también significa que el servidor necesita gestionar el estado de cada conexión. La buena noticia es que no estamos hablando de tecnología experimental, WebSockets es &lt;a href="https://caniuse.com/websockets" rel="noopener noreferrer"&gt;soportado por más del 97% de los navegadores&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔗 Conexión con nuestra serie:&lt;/strong&gt; Si vienes siguiendo la serie de GraphQL, te adelanto algo: cuando implementemos subscriptions con &lt;a href="https://aws.amazon.com/appsync/" rel="noopener noreferrer"&gt;AWS AppSync&lt;/a&gt;, no vamos a tener que preocuparnos por manejar WebSockets directamente. AppSync se encarga de toda esa infraestructura por nosotros: conexiones, reconexiones, escalabilidad. Nosotros solo definimos &lt;em&gt;qué&lt;/em&gt; datos queremos escuchar.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Polling vs Subscriptions: Comparación lado a lado
&lt;/h2&gt;

&lt;p&gt;Este diagrama muestra ambos modelos con el mismo escenario para que veas la diferencia de un vistazo:&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%2Fnjxyd1gtj5ywcavvbgpc.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%2Fnjxyd1gtj5ywcavvbgpc.png" alt="Diagrama: Polling vs Subscriptions lado a lado" width="800" height="766"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspecto&lt;/th&gt;
&lt;th&gt;Polling&lt;/th&gt;
&lt;th&gt;Subscriptions&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dirección&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cliente pregunta → Servidor responde&lt;/td&gt;
&lt;td&gt;Servidor avisa → Cliente recibe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Latencia&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Depende del intervalo (segundos)&lt;/td&gt;
&lt;td&gt;Casi instantánea (milisegundos)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Carga en servidor&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Constante (aunque no haya cambios)&lt;/td&gt;
&lt;td&gt;Solo cuando hay datos nuevos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Complejidad&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Baja&lt;/td&gt;
&lt;td&gt;Media-Alta&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Infraestructura&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;HTTP estándar&lt;/td&gt;
&lt;td&gt;WebSockets u otro protocolo persistente&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Estado&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stateless&lt;/td&gt;
&lt;td&gt;Stateful&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Escalabilidad&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Más peticiones = más carga&lt;/td&gt;
&lt;td&gt;Más conexiones = más memoria&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Debugging&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fácil (peticiones HTTP normales)&lt;/td&gt;
&lt;td&gt;Más complejo (conexiones persistentes)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Para ponerlo en perspectiva: si tienes 1,000 usuarios conectados con polling cada 5 segundos, tu servidor recibe 12,000 peticiones por minuto aunque no haya cambiado absolutamente nada. Con subscriptions, esas 12,000 peticiones se convierten en 0 hasta que realmente haya datos nuevos.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Cuándo gana Polling?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Datos que cambian con poca frecuencia (dashboard de métricas diarias)&lt;/li&gt;
&lt;li&gt;Infraestructura simple sin soporte para WebSockets&lt;/li&gt;
&lt;li&gt;Cuando la latencia de unos segundos es aceptable&lt;/li&gt;
&lt;li&gt;Prototipos rápidos donde la simplicidad importa más&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ¿Cuándo ganan las Subscriptions?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Datos que cambian constantemente (chat, colaboración en tiempo real)&lt;/li&gt;
&lt;li&gt;La latencia baja es crítica (trading, juegos, notificaciones)&lt;/li&gt;
&lt;li&gt;Muchos eventos por segundo donde polling sería ineficiente&lt;/li&gt;
&lt;li&gt;Experiencias donde el usuario espera actualizaciones instantáneas&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Tip:&lt;/strong&gt; La complejidad de implementar subscriptions se reduce significativamente cuando usas servicios administrados que manejan la infraestructura de WebSockets por ti.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  ¿Cuándo usar Polling y cuándo usar Subscriptions?
&lt;/h2&gt;

&lt;p&gt;No hay un ganador universal. La mejor opción depende de tu caso de uso específico. Aquí tienes algunos ejemplos para guiarte:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Caso de uso&lt;/th&gt;
&lt;th&gt;Enfoque recomendado&lt;/th&gt;
&lt;th&gt;¿Por qué?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Chat en tiempo real&lt;/td&gt;
&lt;td&gt;Subscriptions&lt;/td&gt;
&lt;td&gt;Los usuarios esperan ver mensajes al instante&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dashboard de analytics&lt;/td&gt;
&lt;td&gt;Polling&lt;/td&gt;
&lt;td&gt;Los datos se actualizan cada minutos u horas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Feed de redes sociales&lt;/td&gt;
&lt;td&gt;Polling (o híbrido)&lt;/td&gt;
&lt;td&gt;No necesitas ver cada like al instante&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Marcadores deportivos en vivo&lt;/td&gt;
&lt;td&gt;Subscriptions&lt;/td&gt;
&lt;td&gt;Cada segundo cuenta para la experiencia&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Notificaciones&lt;/td&gt;
&lt;td&gt;Subscriptions&lt;/td&gt;
&lt;td&gt;El usuario espera recibirlas inmediatamente&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reporte de inventario&lt;/td&gt;
&lt;td&gt;Polling&lt;/td&gt;
&lt;td&gt;Se consulta periódicamente, no en tiempo real&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Editor colaborativo (tipo Google Docs)&lt;/td&gt;
&lt;td&gt;Subscriptions&lt;/td&gt;
&lt;td&gt;Cada keystroke necesita sincronizarse&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  El enfoque híbrido
&lt;/h3&gt;

&lt;p&gt;Algo que vale la pena mencionar: no siempre tienes que elegir uno u otro. Muchas aplicaciones usan &lt;strong&gt;ambos enfoques&lt;/strong&gt; dependiendo de la funcionalidad.&lt;/p&gt;

&lt;p&gt;Por ejemplo, una app de delivery podría usar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Subscriptions&lt;/strong&gt; para el tracking en tiempo real del repartidor&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Polling&lt;/strong&gt; para actualizar el catálogo de restaurantes cada cierto tiempo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La clave es preguntarte: &lt;strong&gt;¿Qué tan rápido necesita mi usuario ver este cambio?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Si la respuesta es "inmediatamente" → Subscriptions&lt;/li&gt;
&lt;li&gt;Si la respuesta es "en los próximos segundos o minutos está bien" → Polling&lt;/li&gt;
&lt;li&gt;Si no estás seguro → Empieza con polling (es más simple) y migra a subscriptions si la experiencia lo requiere&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Preguntas frecuentes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ¿Qué es long polling?
&lt;/h3&gt;

&lt;p&gt;Long polling es un punto intermedio entre polling y subscriptions. En vez de preguntar cada X segundos y recibir una respuesta inmediata (aunque esté vacía), tu app hace una petición y el servidor &lt;strong&gt;la mantiene abierta&lt;/strong&gt; hasta que tenga datos nuevos. Cuando responde, tu app inmediatamente hace otra petición. Es más eficiente que polling tradicional pero no tan eficiente como subscriptions con WebSockets.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Las subscriptions consumen más recursos que polling?
&lt;/h3&gt;

&lt;p&gt;Depende. Las subscriptions mantienen conexiones abiertas, lo que consume memoria en el servidor por cada cliente conectado. Polling consume ancho de banda y CPU por cada petición repetida. Con pocos usuarios y cambios frecuentes, subscriptions es más eficiente. Con muchos usuarios y cambios poco frecuentes, polling puede ser más simple de escalar.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Se pueden combinar polling y subscriptions?
&lt;/h3&gt;

&lt;p&gt;Sí, y es más común de lo que parece. Muchas aplicaciones usan subscriptions para funcionalidades que necesitan tiempo real (como chat o notificaciones) y polling para datos que cambian con menos frecuencia (como un catálogo de productos). No tienes que elegir uno para toda tu aplicación, puedes usar el enfoque que mejor se adapte a cada funcionalidad.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusión
&lt;/h2&gt;

&lt;p&gt;Recapitulemos lo que aprendimos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Polling&lt;/strong&gt; = tu app pregunta constantemente. Simple, stateless, pero puede ser ineficiente&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscriptions&lt;/strong&gt; = el servidor te avisa. Eficiente y en tiempo real, pero más complejo&lt;/li&gt;
&lt;li&gt;No hay un "ganador" cada enfoque tiene su lugar dependiendo de tu caso de uso&lt;/li&gt;
&lt;li&gt;Los enfoques híbridos son completamente válidos y muy comunes en aplicaciones reales&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La próxima vez que estés diseñando una funcionalidad que necesite datos frescos, ya tienes las herramientas conceptuales para tomar una decisión informada.&lt;/p&gt;

&lt;p&gt;En el próximo artículo de la serie, vamos a poner estos conceptos en práctica implementando &lt;strong&gt;subscriptions en nuestra API de GraphQL con AWS AppSync&lt;/strong&gt;. ¿Recuerdas toda la complejidad que mencionamos sobre manejar WebSockets, conexiones persistentes y reconexiones? AppSync se encarga de todo eso por nosotros, por debajo usa los mismos WebSockets que explicamos aquí, pero nosotros solo necesitamos definir qué datos queremos escuchar. Vamos a ver cómo pasar de la teoría a código funcionando.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>beginners</category>
      <category>spanish</category>
    </item>
    <item>
      <title>AppSync: Tu primer Resolver de GraphQL</title>
      <dc:creator>Ramses Mata</dc:creator>
      <pubDate>Wed, 18 Mar 2026 22:18:04 +0000</pubDate>
      <link>https://dev.to/aws/appsync-tu-primer-resolver-de-graphql-2dej</link>
      <guid>https://dev.to/aws/appsync-tu-primer-resolver-de-graphql-2dej</guid>
      <description>&lt;p&gt;En el &lt;a href="https://dev.to/ramtoearth/appsync-creando-y-desplegando-tu-api-en-menos-de-10-minutos-951"&gt;artículo anterior&lt;/a&gt;, desplegamos nuestro esquema en AWS AppSync. Creamos una &lt;a href="https://dev.to/aws/que-pasa-realmente-cuando-una-app-pide-algo-19g0"&gt;API&lt;/a&gt; de GraphQL completa con tipos, queries y mutations perfectamente definidos. Incluso exploramos la documentación que AppSync generó automáticamente, pero hay un problema.&lt;/p&gt;

&lt;p&gt;Si intentas hacer una query en el playground de AppSync ahora mismo, no obtendrás datos. Tu API tiene un esquema bastante cool, aunque hay un detalle; es como un restaurante con un menú increíble pero sin cocina. El menú promete platillos deliciosos, pero nadie sabe cómo prepararlos.&lt;/p&gt;

&lt;p&gt;Hoy vamos a construir esa cocina. Vamos a implementar &lt;strong&gt;resolvers&lt;/strong&gt; - las funciones que realmente responden a tus queries y mutations con datos reales.&lt;/p&gt;

&lt;p&gt;Al final de este artículo, tendrás:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Un resolver funcionando para consultar usuarios por ID&lt;/li&gt;
&lt;li&gt;✅ Un resolver funcionando para crear nuevos usuarios&lt;/li&gt;
&lt;li&gt;✅ Entendimiento completo del patrón para implementar cualquier resolver&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Y lo mejor: haremos todo en la consola de AWS. Sin configuración local, sin instalar nada, sin complicaciones.&lt;/p&gt;

&lt;p&gt;Si eres más visual, este diagrama te puede ayudar a entender el panorama completo de lo que construiremos:&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%2Fso481y329b3u58gl6egb.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%2Fso481y329b3u58gl6egb.png" alt="Diagrama mental model: Cliente → AppSync → Lambda → Respuesta"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Antes de empezar
&lt;/h2&gt;

&lt;p&gt;Para seguir este tutorial necesitas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tu API de AppSync del artículo anterior&lt;/li&gt;
&lt;li&gt;Una cuenta de AWS (&lt;a href="https://aws.amazon.com/free/" rel="noopener noreferrer"&gt;el free tier&lt;/a&gt; es suficiente)&lt;/li&gt;
&lt;li&gt;Un navegador web&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Parte 1: Entendiendo los Resolvers
&lt;/h2&gt;

&lt;p&gt;Antes de escribir código, entendamos qué son los resolvers y por qué los necesitamos.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Qué es un resolver?
&lt;/h3&gt;

&lt;p&gt;Un resolver es una función que "resuelve" (responde) una query o mutation de GraphQL.&lt;/p&gt;

&lt;p&gt;Piénsalo así:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tu &lt;strong&gt;esquema&lt;/strong&gt; es el menú del restaurante (qué platillos ofreces)&lt;/li&gt;
&lt;li&gt;Los &lt;strong&gt;resolvers&lt;/strong&gt; son las recetas (cómo preparar cada platillo)&lt;/li&gt;
&lt;li&gt;La &lt;strong&gt;fuente de datos&lt;/strong&gt; son los ingredientes (de dónde vienen los datos)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cuando un cliente hace una query como &lt;code&gt;usuario(id: "123")&lt;/code&gt;, GraphQL necesita saber:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;¿Esta query existe en mi esquema? ✅ (validación)&lt;/li&gt;
&lt;li&gt;¿Cómo obtengo los datos para responderla? ← Aquí entra el resolver&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  ¿Por qué AWS Lambda?
&lt;/h3&gt;

&lt;p&gt;Vamos a usar &lt;a href="https://aws.amazon.com/lambda/" rel="noopener noreferrer"&gt;AWS Lambda&lt;/a&gt; para escribir nuestros resolvers por estas razones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Serverless&lt;/strong&gt; - No tienes que manejar servidores, actualizaciones, o escalamiento&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pay-per-use&lt;/strong&gt; - Solo pagas por el tiempo que tu código se ejecuta. El &lt;a href="https://aws.amazon.com/free/" rel="noopener noreferrer"&gt;free tier&lt;/a&gt; incluye $100 USD en créditos que puedes usar en Lambda y otros servicios. Ana Cunha escribió una &lt;a href="https://builder.aws.com/content/38U7Xaj766nhoifwnvLlfwsIxiY/como-empezar-con-aws-sin-gastar-nada-todo-lo-que-necesitas-saber-del-nuevo-free-tier" rel="noopener noreferrer"&gt;guía completa sobre cómo funciona el nuevo Free Tier&lt;/a&gt; si quieres entender más sobre los créditos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integración directa&lt;/strong&gt; - Lambda se conecta perfectamente con AppSync&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Perfecto para aprender&lt;/strong&gt; - Te enfocas en la lógica de tu resolver, no en infraestructura&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Queries vs Mutations
&lt;/h3&gt;

&lt;p&gt;Hoy implementaremos dos tipos de resolvers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Query resolver&lt;/strong&gt; (&lt;code&gt;usuario(id)&lt;/code&gt;) - Para &lt;strong&gt;leer&lt;/strong&gt; datos. No cambia nada en tu sistema.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mutation resolver&lt;/strong&gt; (&lt;code&gt;crearUsuario&lt;/code&gt;) - Para &lt;strong&gt;escribir&lt;/strong&gt; datos. Cada ejecución crea algo nuevo.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Tip importante:&lt;/strong&gt; Usaremos datos mock (hardcodeados) en lugar de una base de datos real. Esto nos permite enfocarnos en entender cómo funcionan los resolvers sin la complejidad de configurar DynamoDB. El patrón que aprendas hoy funciona igual con una base de datos real - solo cambias de dónde vienen los datos.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Parte 2: Creando tu primer Lambda Function (Query Resolver)
&lt;/h2&gt;

&lt;p&gt;Vamos a crear el resolver para la query &lt;code&gt;usuario(id)&lt;/code&gt;. Este resolver recibirá un ID y devolverá los datos de ese usuario.&lt;/p&gt;

&lt;p&gt;En este video, creamos la función Lambda desde cero en la consola de AWS, escribimos el código del resolver y lo desplegamos:&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/OFDExZTP8aY"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;Aquí está el código del resolver para que lo copies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Datos mock - en una aplicación real, estos vendrían de una base de datos&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;usuarios&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;nombre&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Ana García&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ana@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;fotoPerfil&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://example.com/ana.jpg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;creadoEn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2024-01-15T10:30:00Z&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="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;nombre&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Carlos López&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;carlos@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;fotoPerfil&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://example.com/carlos.jpg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;creadoEn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2024-02-20T14:20:00Z&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// AppSync envía los argumentos de la query en event.arguments&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Buscamos el usuario por ID&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;usuario&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;usuarios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Retornamos el usuario encontrado, o null si no existe&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;usuario&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="kc"&gt;null&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;
  
  
  Entendiendo el código
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Los datos mock:&lt;/strong&gt; Creamos un array con dos usuarios. En producción, esto vendría de DynamoDB, RDS, o cualquier base de datos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;export const handler:&lt;/strong&gt; Esta es la función que Lambda ejecuta cuando se invoca. El &lt;code&gt;async&lt;/code&gt; significa que puede hacer operaciones asíncronas (como llamar a una base de datos).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;event.arguments:&lt;/strong&gt; AppSync empaqueta los argumentos de tu query (en este caso, el &lt;code&gt;id&lt;/code&gt;) en &lt;code&gt;event.arguments&lt;/code&gt;. Así es como tu resolver sabe qué usuario está pidiendo el cliente.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;return usuario || null:&lt;/strong&gt; Si encontramos el usuario, lo retornamos. Si no, retornamos &lt;code&gt;null&lt;/code&gt;. En GraphQL, retornar &lt;code&gt;null&lt;/code&gt; para datos que no existen es normal y esperado.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Tip importante:&lt;/strong&gt; Cuando alguien hace la query &lt;code&gt;usuario(id: "1")&lt;/code&gt;, AppSync automáticamente toma ese &lt;code&gt;id: "1"&lt;/code&gt; y lo pone en &lt;code&gt;event.arguments.id&lt;/code&gt;. No tienes que hacer nada especial - AppSync lo hace por ti.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Parte 3: Conectando Lambda a AppSync
&lt;/h2&gt;

&lt;p&gt;Ahora que tenemos nuestra función Lambda lista, necesitamos conectarla a AppSync. Para esto necesitamos tres cosas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Un rol de IAM&lt;/strong&gt; - para darle permiso a AppSync de invocar Lambda&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Un Data Source&lt;/strong&gt; - para conectar AppSync con Lambda&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Un Resolver&lt;/strong&gt; - para decirle a AppSync "cuando alguien pida usuario, usa este data source"&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Creando el rol de IAM
&lt;/h3&gt;

&lt;p&gt;AWS usa roles para controlar qué servicios pueden hacer qué. Necesitamos crear un rol que le dé permiso a AppSync para invocar nuestras funciones Lambda.&lt;/p&gt;

&lt;p&gt;En este video, creamos el rol de IAM y le adjuntamos los permisos necesarios:&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/SD0VNcOiPuo"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;¿Por qué necesitamos un rol?&lt;/strong&gt; Los servicios de AWS no pueden simplemente llamarse entre sí - necesitan permiso explícito. Este rol le da a AppSync permiso para invocar tus funciones Lambda. Lo bueno es que puedes reutilizar este mismo rol para todas tus funciones Lambda de GraphQL.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Creando el Data Source
&lt;/h3&gt;

&lt;p&gt;Un data source es la forma en que AppSync se conecta a donde viven tus datos. En nuestro caso, es Lambda.&lt;/p&gt;

&lt;p&gt;En este video, agregamos nuestra función Lambda como data source en AppSync:&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/GUKBiPpnu5M"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;¿Qué es un Data Source?&lt;/strong&gt; Un data source puede ser Lambda, DynamoDB, un endpoint HTTP, o incluso otra API de GraphQL. AppSync usa el data source para saber a dónde ir a buscar los datos cuando recibe una query.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Adjuntando el Resolver a la Query
&lt;/h3&gt;

&lt;p&gt;Ahora le decimos a AppSync que cuando alguien haga la query &lt;code&gt;usuario(id)&lt;/code&gt;, use nuestro data source de Lambda.&lt;/p&gt;

&lt;p&gt;En este video, adjuntamos el resolver a la query usuario en el esquema. Usamos &lt;strong&gt;VTL (Velocity Template Language)&lt;/strong&gt; como runtime y configuramos los mapping templates:&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/sRKvP0mia1w"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;Aquí están los mapping templates para que los copies:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Request mapping template:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "version": "2018-05-29",
    "operation": "Invoke",
    "payload": {
        "arguments": $util.toJson($context.arguments)
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Response mapping template:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$util.toJson($context.result)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;¿Qué son los mapping templates?&lt;/strong&gt; Son plantillas que transforman datos entre AppSync y tu data source. El request template empaqueta los argumentos de la query (como el &lt;code&gt;id&lt;/code&gt;) en un formato que Lambda entiende. El response template toma lo que Lambda retorna y lo formatea para GraphQL.&lt;/p&gt;
&lt;/blockquote&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%2Fkrvb6pwo4tbrdhmse7k6.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%2Fkrvb6pwo4tbrdhmse7k6.png" alt="Diagrama mapping templates: flujo de datos entre Cliente, AppSync, Request Template, Lambda, Response Template"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;¿Unit resolver vs Pipeline resolver?&lt;/strong&gt; Un unit resolver trabaja con una sola fuente de datos (nuestro caso). Un pipeline resolver puede combinar múltiples fuentes de datos en secuencia. Para este tutorial, unit resolver es perfecto.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Parte 4: Probando tu Primer Resolver
&lt;/h2&gt;

&lt;p&gt;¡Es momento de ver tu resolver en acción! Vamos a hacer una query real y ver datos reales de vuelta.&lt;/p&gt;

&lt;p&gt;Copia esta query en el playground de AppSync:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GetUsuario&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;usuario&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;nombre&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;fotoPerfil&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;creadoEn&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En este video, ejecutamos la query y experimentamos con diferentes IDs y campos:&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/6G911g2bec0"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;Fíjate en algo interesante: cuando pides un usuario que no existe (como &lt;code&gt;id: "999"&lt;/code&gt;), recibes &lt;code&gt;null&lt;/code&gt; en lugar de un error.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;¿Por qué null y no un error?&lt;/strong&gt; En GraphQL, retornar &lt;code&gt;null&lt;/code&gt; para datos que no existen es normal y esperado. Los errores son para cuando algo sale MAL (como si Lambda fallara o no tuviera permisos), no para "dato no encontrado". Esta es una distinción importante.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Parte 5: Segundo Resolver - Mutation (crearUsuario)
&lt;/h2&gt;

&lt;p&gt;Ya sabes cómo crear un resolver para leer datos. Ahora vamos a crear uno para &lt;strong&gt;escribir&lt;/strong&gt; datos. El proceso es el mismo patrón que ya aprendiste:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Crear función Lambda&lt;/li&gt;
&lt;li&gt;Crear data source en AppSync&lt;/li&gt;
&lt;li&gt;Adjuntar resolver con mapping templates&lt;/li&gt;
&lt;li&gt;Probar&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Aquí está el código del resolver para la mutation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// En una aplicación real, esto guardaría en una base de datos&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;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Las mutations envían los datos en event.arguments.input&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;nombre&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="nx"&gt;fotoPerfil&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Generamos un ID simple (en producción, usarías UUID o un ID de base de datos)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Creamos el objeto del nuevo usuario&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nuevoUsuario&lt;/span&gt; &lt;span class="o"&gt;=&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="nx"&gt;nombre&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="na"&gt;fotoPerfil&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fotoPerfil&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;creadoEn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="c1"&gt;// En una aplicación real, aquí guardarías en la base de datos&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Usuario creado:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nuevoUsuario&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Retornamos el usuario creado&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;nuevoUsuario&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;Los mapping templates son los mismos que usamos para la query:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Request mapping template:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "version": "2018-05-29",
    "operation": "Invoke",
    "payload": {
        "arguments": $util.toJson($context.arguments)
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Response mapping template:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$util.toJson($context.result)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Y esta es la mutation para probar en el playground:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;mutation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CrearUsuario&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;crearUsuario&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;nombre&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"María Rodríguez"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"maria@example.com"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;fotoPerfil&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/maria.jpg"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;nombre&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;creadoEn&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En este video, creamos la función Lambda, la conectamos a AppSync (reutilizando el rol de IAM) y probamos la mutation:&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/t9Shv-Djov8"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;¿Por qué event.arguments.input?&lt;/strong&gt; Las mutations usan Input Types para agrupar todos los datos que se envían. En lugar de &lt;code&gt;event.arguments.nombre&lt;/code&gt;, &lt;code&gt;event.arguments.email&lt;/code&gt;, etc., todo viene agrupado en &lt;code&gt;event.arguments.input&lt;/code&gt;. Esto mantiene las mutations limpias y organizadas.&lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;¿Por qué los datos no se guardan?&lt;/strong&gt; Recuerda que estamos usando datos mock para aprender. Cada invocación de Lambda es independiente - no hay base de datos guardando los usuarios. En futuros artículos podríamos agregar DynamoDB para hacerlo persistente.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Parte 6: Reto Práctico
&lt;/h2&gt;

&lt;p&gt;Ya conoces el patrón completo para crear resolvers. Ahora es tu turno de practicar.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tu reto:&lt;/strong&gt; Implementa los resolvers restantes usando el mismo patrón que aprendiste:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resolvers de query:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;publicacion(id: ID!): Publicacion&lt;/code&gt; - Crea datos mock con 2-3 publicaciones, busca por ID&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;publicaciones: [Publicacion!]!&lt;/code&gt; - Retorna todas las publicaciones (no necesita argumentos)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Resolver de mutation:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;crearPublicacion(input: CrearPublicacionInput!): Publicacion&lt;/code&gt; - Extrae titulo, contenido, autorId del input, genera ID y creadoEn&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Tips para completar el reto:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sigue exactamente el mismo patrón: Lambda → Data Source → Resolver → Test&lt;/li&gt;
&lt;li&gt;Empieza con &lt;code&gt;publicaciones&lt;/code&gt; (es la más simple, no tiene argumentos)&lt;/li&gt;
&lt;li&gt;Reutiliza el rol &lt;code&gt;AppSyncLambdaRole&lt;/code&gt; para todos los data sources&lt;/li&gt;
&lt;li&gt;Los mapping templates son los mismos para todos los resolvers&lt;/li&gt;
&lt;li&gt;Prueba cada resolver en el playground antes de pasar al siguiente&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;¿Te atoras?&lt;/strong&gt; Es normal. Revisa los pasos que seguimos para &lt;code&gt;usuario&lt;/code&gt; y &lt;code&gt;crearUsuario&lt;/code&gt;. El patrón es idéntico, solo cambian los nombres de los campos y la estructura de los datos.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Conclusión
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Lo que lograste hoy:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Creaste tu primera función Lambda para un resolver de GraphQL&lt;/li&gt;
&lt;li&gt;✅ Conectaste Lambda a AppSync como data source&lt;/li&gt;
&lt;li&gt;✅ Implementaste un resolver de query (&lt;code&gt;usuario&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;✅ Implementaste un resolver de mutation (&lt;code&gt;crearUsuario&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;✅ Probaste ambos en el playground de GraphQL&lt;/li&gt;
&lt;li&gt;✅ Aprendiste un patrón que puedes repetir para cualquier resolver&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;La idea clave:&lt;/strong&gt; Los resolvers son simplemente funciones que retornan datos en la forma que tu esquema define. Ya sea que uses datos mock, una base de datos, o una API externa, el patrón es el mismo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Qué sigue?&lt;/strong&gt; Tu API funciona, pero ¿qué pasa cuando los datos cambian? ¿Tu app debería preguntar por actualizaciones cada cierto tiempo (polling)? ¿O el servidor debería enviar actualizaciones automáticamente (subscriptions)? En el próximo artículo exploraremos ambos enfoques y te ayudaremos a elegir el correcto para tu caso de uso.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>webdev</category>
      <category>spanish</category>
    </item>
    <item>
      <title>Planificar antes de codear: Spec Driven Development en práctica</title>
      <dc:creator>Ramses Mata</dc:creator>
      <pubDate>Sat, 14 Mar 2026 00:24:47 +0000</pubDate>
      <link>https://dev.to/aws/del-caso-de-uso-a-produccion-k6p</link>
      <guid>https://dev.to/aws/del-caso-de-uso-a-produccion-k6p</guid>
      <description>&lt;p&gt;Cuando estaba en la universidad, lo primero que hacía cuando tenía una idea nueva para un proyecto era abrir VS Code y empezaba a escribir código. ¿Te suena familiar?&lt;/p&gt;

&lt;p&gt;El problema es que mis proyectos crecían sin dirección, cambiaba cosas constantemente, nunca sabía cuándo estaba "listo", hacía código que ni yo entendía dos semanas después y terminaba como un proyecto más que no volvía a retomar. Y desde mi punto de vista no era un problema de habilidad técnica puesto que sabía hacer las cosas, tiempo después entendí que era un problema de proceso.&lt;/p&gt;

&lt;p&gt;Si vas comenzando tu carrera en tecnología y te pasa algo parecido, está bien, todos empezamos así. Pero hay una manera que puede funcionarte mejor.&lt;/p&gt;

&lt;p&gt;En este artículo vamos a usar &lt;a href="https://kiro.dev?trk=3030e60a-17b3-4fdb-9862-d65f29e1a10c&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Kiro&lt;/a&gt; para llevar una idea desde casos de uso hasta código funcional. Si es tu primera vez con Kiro y prefieres empezar con algo más introductorio, Ana Cunha escribió una &lt;a href="https://builder.aws.com/content/34X2JujaGkTJed5N2KeMYt1Mz9m/como-comenzar-con-kiro-tu-guia-de-primeros-pasos?trk=3030e60a-17b3-4fdb-9862-d65f29e1a10c&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;guía de primeros pasos&lt;/a&gt; que te puede servir como punto de partida.&lt;/p&gt;

&lt;h2&gt;
  
  
  El verdadero problema no es el código
&lt;/h2&gt;

&lt;p&gt;En mi experiencia, muchos de mis proyectos no fallaron por código malo. Fallaron porque nunca definí bien el problema que quería resolver.&lt;/p&gt;

&lt;p&gt;Imagina que quieres construir una aplicación que compare tu CV con una oferta de trabajo usando IA y te diga qué te falta. ¿Por dónde empiezas? ¿El frontend? ¿La base de datos? ¿La integración con inteligencia artificial?&lt;/p&gt;

&lt;p&gt;En algún punto de mi carrera fui freelancer y en esa experiencia fue cuando le di valor a mis clases de ingeniería de software. Aprendí algo que cambió mi forma de construir: el código no es el punto de partida, es la consecuencia. Antes de escribir una sola línea, necesitas entender qué estás construyendo y para quién.&lt;/p&gt;

&lt;h2&gt;
  
  
  Casos de uso: Tu plano antes de construir
&lt;/h2&gt;

&lt;p&gt;Un caso de uso es simplemente una descripción de cómo un usuario interactúa con tu sistema. Piénsalo como los planos de un arquitecto, no construyes una casa sin ellos, y no deberías empezar a codear sin entender qué estás construyendo.&lt;/p&gt;

&lt;p&gt;Si quieres profundizar en la definición formal, hay &lt;a href="https://en.wikipedia.org/wiki/Use_case" rel="noopener noreferrer"&gt;documentación excelente&lt;/a&gt; al respecto. Pero para lo práctico, lo que necesitas son tres cosas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Historias de usuario&lt;/strong&gt;: Qué quiere hacer el usuario y por qué&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Criterios de aceptación&lt;/strong&gt;: Cómo sabes que funciona correctamente&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Entidades y relaciones&lt;/strong&gt;: Qué "cosas" existen en tu sistema y cómo se conectan&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Volvamos al ejemplo del comparador de CVs. Una idea vaga como "quiero una app que analice mi CV" se convierte en algo concreto cuando la escribes como historia de usuario:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Como&lt;/strong&gt; persona que busca trabajo, &lt;strong&gt;quiero&lt;/strong&gt; subir mi CV en formato PDF o texto, &lt;strong&gt;para&lt;/strong&gt; que el sistema pueda analizarlo y compararlo con una oferta de trabajo.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;De repente, ya sabes varias cosas: necesitas soportar PDF y texto, necesitas una descripción de trabajo como segunda entrada, y el resultado es una comparación. Eso es mucho más útil que "hacer una app de CVs".&lt;/p&gt;

&lt;p&gt;Y cuando agregas criterios de aceptación, las cosas se ponen aún más claras:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CUANDO un usuario sube un PDF, EL SISTEMA DEBE extraer el contenido correctamente&lt;/li&gt;
&lt;li&gt;CUANDO un usuario intenta subir un formato no soportado, EL SISTEMA DEBE mostrar un error con los formatos válidos&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ahora no solo sabes &lt;strong&gt;qué&lt;/strong&gt; construir, sino &lt;strong&gt;cuándo&lt;/strong&gt; está "listo". Eso es algo que como estudiante nunca tenía claro.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spec Driven Development: El puente entre la idea y el código
&lt;/h2&gt;

&lt;p&gt;Definir casos de uso es el primer paso. Pero, ¿cómo pasas de esos requisitos a código real sin perderte en el camino?&lt;/p&gt;

&lt;p&gt;Aquí es donde entra el concepto de &lt;a href="https://kiro.dev/blog/kiro-and-the-future-of-software-development/?trk=3030e60a-17b3-4fdb-9862-d65f29e1a10c&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Spec Driven Development&lt;/a&gt;, un enfoque donde primero diseñas las especificaciones de lo que vas a construir y después implementas. Esta metodología se divide en cuatro fases:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Capturar la intención&lt;/strong&gt;: Defines qué quieres construir en lenguaje natural. Aquí es donde entran las historias de usuario y los criterios de aceptación que vimos antes. El objetivo es que cualquier persona (técnica o no) entienda qué se va a construir.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Traducir la intención en un blueprint del sistema&lt;/strong&gt;: Tomas esos requisitos y los conviertes en un diseño técnico: qué componentes necesitas, cómo se comunican entre sí, qué tecnologías vas a usar. Es el plano de tu arquitectura.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Generar unidades de trabajo&lt;/strong&gt;: Divides ese diseño en tareas concretas e incrementales. Cada tarea debe ser pequeña, clara y debe resultar en algo funcional que puedas probar.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Implementar y validar&lt;/strong&gt;: Ahora sí, escribes código. Pero cada tarea que implementas se valida contra los criterios de aceptación que definiste en la primera fase. Así sabes que lo que construyes es lo que necesitas.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Nota:&lt;/strong&gt; Este no es un proceso que haces una sola vez. Cada que agregas una nueva feature a tu proyecto, vuelves a recorrer las cuatro fases. Defines la intención de esa feature, actualizas el blueprint, generas las unidades de trabajo y las implementas. Es un ciclo que se repite, y cada iteración se vuelve más natural.&lt;/p&gt;
&lt;/blockquote&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%2Fw3ubu4ghf36gzr123wvh.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%2Fw3ubu4ghf36gzr123wvh.png" alt="SpedDrivenDevelopment"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lo interesante de este enfoque es que no depende de ninguna herramienta específica, puedes hacerlo con un documento de texto, una pizarra, o una servilleta. Lo importante es el proceso de pensar antes de codear.&lt;/p&gt;

&lt;p&gt;Dicho esto, herramientas como &lt;a href="https://kiro.dev?trk=3030e60a-17b3-4fdb-9862-d65f29e1a10c&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Kiro&lt;/a&gt; implementan esta metodología y te ayudan a recorrer las fases de manera estructurada. Kiro sigue un flujo de tres fases (&lt;code&gt;1. requirements&lt;/code&gt;, &lt;code&gt;2. design&lt;/code&gt; y &lt;code&gt;3. tasks&lt;/code&gt;) que se mapea directamente con lo que acabamos de ver:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Fase de SDD&lt;/th&gt;
&lt;th&gt;Qué pasa&lt;/th&gt;
&lt;th&gt;En Kiro&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Capturar la intención&lt;/td&gt;
&lt;td&gt;Describes tu idea en lenguaje natural&lt;/td&gt;
&lt;td&gt;Le dices a Kiro qué quieres construir&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blueprint del sistema&lt;/td&gt;
&lt;td&gt;Requisitos + Diseño + Tareas&lt;/td&gt;
&lt;td&gt;Kiro genera &lt;code&gt;requirements.md&lt;/code&gt;, &lt;code&gt;design.md&lt;/code&gt; y &lt;code&gt;tasks.md&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unidades de trabajo&lt;/td&gt;
&lt;td&gt;Ejecutar las tareas&lt;/td&gt;
&lt;td&gt;Corres cada task desde el IDE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Implementar y validar&lt;/td&gt;
&lt;td&gt;Código + validación&lt;/td&gt;
&lt;td&gt;Kiro implementa y tú validas contra los criterios de aceptación&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Pero la metodología funciona con o sin herramientas. Kiro simplemente hace el proceso más rápido.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Tip importante:&lt;/strong&gt; Las herramientas de IA no reemplazan tu pensamiento crítico. Lo amplifican. No se trata de documentar por documentar, se trata de pensar antes de codear. La documentación es el resultado de ese pensamiento.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  SDD en acción: Construyendo el comparador de CVs
&lt;/h2&gt;

&lt;p&gt;Para que esto no se quede en teoría, veamos cómo se ve este proceso aplicado al proyecto del comparador de CVs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fase 1: Capturar la intención
&lt;/h3&gt;

&lt;p&gt;Aquí es donde todo lo que vimos antes cobra sentido. En lugar de abrir Kiro con un prompt vago como "hazme una app de CVs", primero escribí un archivo con las historias de usuario y criterios de aceptación que ya había definido el contexto del proyecto, quién es el usuario, qué necesita hacer y cómo sabemos que funciona. Algo así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Contexto del Proyecto&lt;/span&gt;
&lt;span class="gs"&gt;**Usuario principal:**&lt;/span&gt; Personas que buscan trabajo activamente
&lt;span class="gs"&gt;**Objetivo:**&lt;/span&gt; Mejorar la calidad del CV para fortalecer el perfil profesional

&lt;span class="gu"&gt;### Historia 1: Subir CV para análisis&lt;/span&gt;
&lt;span class="gs"&gt;**Como**&lt;/span&gt; persona que busca trabajo
&lt;span class="gs"&gt;**Quiero**&lt;/span&gt; subir mi CV en formato PDF o texto
&lt;span class="gs"&gt;**Para**&lt;/span&gt; que el sistema pueda analizarlo y compararlo con ofertas de trabajo

&lt;span class="gs"&gt;**Criterios de aceptación:**&lt;/span&gt;
CUANDO un usuario sube un archivo CV en formato PDF
EL SISTEMA DEBE extraer el contenido del documento correctamente
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ese archivo fue el punto de partida para Kiro. El trabajo previo de pensar el problema es lo que le da a la herramienta el contexto necesario para generar algo útil.&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/y2u8vSV6rxA"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;h3&gt;
  
  
  Fase 2: Blueprint del sistema
&lt;/h3&gt;

&lt;p&gt;A partir de los casos de uso, Kiro generó tres documentos que juntos forman el blueprint completo del proyecto:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;requirements.md&lt;/code&gt;&lt;/strong&gt;: Kiro tomó las historias de usuario y las expandió en un documento de requisitos formal. Agregó un glosario con los términos del dominio (como "Brecha_Crítica" o "Generador_Recomendaciones"), detalló los criterios de aceptación de cada historia, e incluso identificó requisitos técnicos que no estaban explícitos en las historias originales, como la extracción de texto de PDFs y el análisis estructurado de descripciones de trabajo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;design.md&lt;/code&gt;&lt;/strong&gt;: Con los requisitos claros, Kiro generó el diseño técnico: un diagrama de arquitectura con todos los componentes, los flujos de datos (cómo viaja un PDF desde el navegador hasta el análisis con IA), las interfaces entre componentes (qué recibe y qué retorna cada Lambda), y una estrategia de manejo de errores. Aquí es donde la idea se convirtió en un plano técnico concreto.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;tasks.md&lt;/code&gt;&lt;/strong&gt;: Finalmente, Kiro descompuso el diseño en tareas incrementales. Cada tarea referencia los requisitos que implementa, tiene subtareas claras, y construye sobre la anterior. Por ejemplo: primero la infraestructura (SAM template), después la Lambda de presigned URLs, luego la extracción de PDF, y así sucesivamente hasta tener la aplicación completa.&lt;/p&gt;

&lt;p&gt;Un detalle interesante: Kiro inicialmente me sugirió usar Python para las funciones Lambda. Pero a mí personalmente me gusta mucho Rust, así que le pedí que cambiara el diseño para usar Rust en el backend. Y lo hizo sin problema, ajustó la arquitectura, las dependencias y las tareas. Esto es importante: la herramienta propone, pero tú decides. Si hay algo que quieres hacer diferente, experimentar con un lenguaje que te interesa o usar una tecnología que quieres aprender, puedes hacerlo. El blueprint se adapta a tus decisiones, no al revés.&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/mAQNdIk74Xo"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;El resultado fue una arquitectura clara:&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%2F5gr5x2hm9qvaeojz0anp.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%2F5gr5x2hm9qvaeojz0anp.png" alt="Diagrama2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Un frontend en &lt;a href="https://astro.build/" rel="noopener noreferrer"&gt;Astro&lt;/a&gt; (un framework web moderno), funciones serverless en Rust desplegadas con &lt;a href="https://aws.amazon.com/lambda/?trk=3030e60a-17b3-4fdb-9862-d65f29e1a10c&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;AWS Lambda&lt;/a&gt; (funciones que se ejecutan bajo demanda sin manejar servidores), análisis de CVs con &lt;a href="https://aws.amazon.com/bedrock/?trk=3030e60a-17b3-4fdb-9862-d65f29e1a10c&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Amazon Bedrock&lt;/a&gt; (acceso a modelos de IA generativa) y almacenamiento temporal en &lt;a href="https://aws.amazon.com/s3/?trk=3030e60a-17b3-4fdb-9862-d65f29e1a10c&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Amazon S3&lt;/a&gt; (almacenamiento de objetos en la nube). Todo definido antes de escribir la primera línea de código.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fase 3: Ejecutar unidades de trabajo
&lt;/h3&gt;

&lt;p&gt;Con el blueprint listo, pasé a ejecutar las tareas directamente desde el IDE de Kiro. En el video ejecuto la primera tarea. Kiro creó el monorepo, inicializó el frontend en Astro, configuró el workspace de Rust y generó el &lt;code&gt;template.yaml&lt;/code&gt; base para SAM.&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/0y2ldtZtlR8"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;A partir de ahí, el plan de implementación tenía 21 tareas que iban construyendo la aplicación de forma incremental: primero la infraestructura en AWS (S3, Cognito, API Gateway, Lambdas), después las funciones backend una por una (presigned URLs, extracción de PDF, integración con Bedrock, parseo de respuestas), luego todo el frontend (autenticación, carga de CV, página de resultados), y al final el despliegue. Cada tarea referenciaba los requisitos que implementaba, así que en todo momento sabías &lt;strong&gt;qué&lt;/strong&gt; estabas construyendo y &lt;strong&gt;por qué&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;El resultado: una aplicación funcional donde subes tu CV, pegas una oferta de trabajo, y el sistema te dice qué tan compatible eres y qué te falta. Todo construido a partir de seis historias de usuario escritas en un archivo de texto.&lt;/p&gt;

&lt;h2&gt;
  
  
  El cambio que haría si pudiera volver
&lt;/h2&gt;

&lt;p&gt;Si pudiera regresar a cuando comencé a construir mis primeras aplicaciones, lo primero que cambiaría es esto: dejaría de abrir el editor como primer paso.&lt;/p&gt;

&lt;p&gt;No porque codear esté mal, obvio; al final es lo que nos apasiona. Sino porque dedicar aunque sea 30 minutos a pensar en &lt;strong&gt;qué&lt;/strong&gt; vas a construir antes de &lt;strong&gt;cómo&lt;/strong&gt;, te ahorra horas de frustración después.&lt;/p&gt;

&lt;p&gt;Si este enfoque te resulta útil y quieres platicar sobre cómo aplicarlo en tu proyecto, encuéntrame en &lt;a href="https://www.linkedin.com/in/ramses-mata/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Recursos
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kiro.dev/blog/kiro-and-the-future-of-software-development/?trk=3030e60a-17b3-4fdb-9862-d65f29e1a10c&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Spec Driven Development - Kiro&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://builder.aws.com/content/34X2JujaGkTJed5N2KeMYt1Mz9m/como-comenzar-con-kiro-tu-guia-de-primeros-pasos?trk=3030e60a-17b3-4fdb-9862-d65f29e1a10c&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Cómo comenzar con Kiro: Tu guía de primeros pasos&lt;/a&gt; - Si quieres un tutorial más paso a paso para empezar con Kiro, Ana Cunha escribió esta guía&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kiro.dev?trk=3030e60a-17b3-4fdb-9862-d65f29e1a10c&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Kiro&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/free/?trk=3030e60a-17b3-4fdb-9862-d65f29e1a10c&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;AWS Free Tier&lt;/a&gt; - Empieza sin gastar nada&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>spanish</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>AppSync: Creando y desplegando tu API en menos de 10 minutos</title>
      <dc:creator>Ramses Mata</dc:creator>
      <pubDate>Wed, 04 Mar 2026 18:13:37 +0000</pubDate>
      <link>https://dev.to/aws/appsync-creando-y-desplegando-tu-api-en-menos-de-10-minutos-951</link>
      <guid>https://dev.to/aws/appsync-creando-y-desplegando-tu-api-en-menos-de-10-minutos-951</guid>
      <description>&lt;p&gt;En el &lt;a href="https://dev.to/ramtoearth/graphql-disenando-tu-primer-esquema-505p"&gt;artículo anterior&lt;/a&gt;, diseñamos un esquema completo para nuestra API de red social. Definimos tipos para Usuario, Publicacion y Comentario, estructuramos nuestras queries y mutations, y creamos un contrato claro de lo que nuestra API puede hacer.&lt;/p&gt;

&lt;p&gt;Pero ese esquema está solo en un archivo en tu computadora. Hoy vamos a cambiar eso.&lt;/p&gt;

&lt;p&gt;En este artículo, vas a desplegar tu esquema en &lt;a href="https://aws.amazon.com/appsync/" rel="noopener noreferrer"&gt;AWS AppSync&lt;/a&gt; (un servicio administrado de GraphQL) y verlo funcionando en vivo. Al final, tendrás:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Una &lt;a href="https://dev.to/aws/que-pasa-realmente-cuando-una-app-pide-algo-19g0"&gt;API&lt;/a&gt; de GraphQL desplegada en la nube&lt;/li&gt;
&lt;li&gt;✅ Un endpoint que puedes compartir&lt;/li&gt;
&lt;li&gt;✅ Documentación generada automáticamente&lt;/li&gt;
&lt;li&gt;✅ Un playground para probar queries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Y lo mejor: lo haremos en menos de 10 minutos, sin escribir código de backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Antes de empezar
&lt;/h2&gt;

&lt;p&gt;Para seguir este tutorial necesitas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tu archivo &lt;code&gt;schema.graphql&lt;/code&gt; del artículo anterior&lt;/li&gt;
&lt;li&gt;Una cuenta de AWS (&lt;a href="https://aws.amazon.com/free/" rel="noopener noreferrer"&gt;el free tier&lt;/a&gt; es suficiente)&lt;/li&gt;
&lt;li&gt;Un navegador web&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;¿No tienes cuenta de AWS?&lt;/strong&gt; No te preocupes, crearla es gratis y toma solo unos minutos. Ana Cunha escribió una &lt;a href="https://builder.aws.com/content/38U7Xaj766nhoifwnvLlfwsIxiY/como-empezar-con-aws-sin-gastar-nada-todo-lo-que-necesitas-saber-del-nuevo-free-tier" rel="noopener noreferrer"&gt;guía completa sobre cómo empezar con AWS sin gastar nada&lt;/a&gt; que te explica todo sobre el Free Tier.&lt;/p&gt;




&lt;h2&gt;
  
  
  Parte 1: Entendiendo tus opciones
&lt;/h2&gt;

&lt;p&gt;Antes de desplegar, hablemos brevemente de las diferentes maneras de correr una API de GraphQL. No vamos a profundizar en cada una, pero es importante que sepas qué opciones existen.&lt;/p&gt;

&lt;h3&gt;
  
  
  Opciones self-hosted (tú manejas el servidor)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Apollo Server&lt;/strong&gt; y &lt;strong&gt;GraphQL Yoga&lt;/strong&gt; son las opciones más populares. Tú instalas el servidor, lo configuras, y lo despliegas donde quieras (AWS, Google Cloud, tu propia computadora, etc.).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ventajas:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Control total sobre todo&lt;/li&gt;
&lt;li&gt;Puedes correrlo en cualquier lugar&lt;/li&gt;
&lt;li&gt;El software es open source y gratuito&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Desventajas:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tú manejas el servidor, las actualizaciones, el escalamiento&lt;/li&gt;
&lt;li&gt;Necesitas conocimientos de DevOps&lt;/li&gt;
&lt;li&gt;Más tiempo de setup&lt;/li&gt;
&lt;li&gt;Pagas por la infraestructura donde lo despliegues&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Opciones managed o serverless (alguien más maneja el servidor)
&lt;/h3&gt;

&lt;p&gt;En este caso, un proveedor de nube (como AWS, Google Cloud, etc.) se encarga de toda la infraestructura por ti. Tú solo te enfocas en tu código y lógica de negocio.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS AppSync&lt;/strong&gt;, &lt;strong&gt;Hasura&lt;/strong&gt;, y &lt;strong&gt;Stepzen&lt;/strong&gt; son servicios que manejan toda la infraestructura por ti. Tú solo subes tu esquema y defines tu lógica.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ventajas:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sin servidores que mantener&lt;/li&gt;
&lt;li&gt;Escalamiento automático&lt;/li&gt;
&lt;li&gt;Features incluidos (real-time, caching, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Desventajas:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Menos control&lt;/li&gt;
&lt;li&gt;Dependes del proveedor&lt;/li&gt;
&lt;li&gt;El modelo de precios varía según el proveedor (revisa la documentación de precios de cada servicio para tu caso específico)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ¿Por qué AppSync para esta serie?
&lt;/h3&gt;

&lt;p&gt;Elegimos AWS AppSync por tres razones:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Serverless&lt;/strong&gt; - No tienes que preocuparte por servidores&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Perfecto para aprender&lt;/strong&gt; - Te enfocas en GraphQL, no en infraestructura&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-time incluido&lt;/strong&gt; - Lo usaremos en un próximo artículo para subscriptions&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Tip importante:&lt;/strong&gt; No existe una opción "mejor" para todos los casos. AppSync es excelente tanto para aprender como para producción. Tu elección entre opciones self-hosted o managed dependerá de tus necesidades específicas de control, experiencia del equipo y preferencias de arquitectura.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Parte 2: ¿Qué es AWS AppSync?
&lt;/h2&gt;

&lt;p&gt;Como mencioné brevemente, AWS AppSync es un servicio administrado de GraphQL. Ahora veamos con más detalle qué significa esto y qué hace por ti.&lt;/p&gt;

&lt;p&gt;Piénsalo como un "GraphQL-as-a-Service": tú defines el esquema y la lógica, AppSync maneja todo lo demás.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lo que AppSync hace por ti
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hosting del esquema&lt;/strong&gt; - Tu esquema vive en la nube, siempre disponible&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ejecución de queries&lt;/strong&gt; - Procesa las queries que llegan a tu API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebSockets para real-time&lt;/strong&gt; - Subscriptions funcionan automáticamente&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caching&lt;/strong&gt; - Respuestas más rápidas sin configuración extra&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Endpoint de API&lt;/strong&gt; - Una URL que tus clientes pueden usar&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Lo que tú controlas
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tu esquema&lt;/strong&gt; - Defines qué datos existen y cómo se relacionan&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tus resolvers&lt;/strong&gt; - La lógica que obtiene los datos (lo haremos en el artículo 4)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cómo retornas datos&lt;/strong&gt; - Usaremos mock data para aprender&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  El flujo de trabajo con AppSync
&lt;/h3&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%2Falkitftgqismuhe4x43z.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%2Falkitftgqismuhe4x43z.png" alt="FlujoAppSync" width="800" height="487"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hoy nos enfocaremos en los pasos 1 y 2. Los resolvers los dejamos para el siguiente artículo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Parte 3: Desplegando tu esquema
&lt;/h2&gt;

&lt;p&gt;Ahora sí, manos a la obra. Vamos a desplegar tu esquema paso a paso.&lt;/p&gt;

&lt;h3&gt;
  
  
  Paso 1: Abre la consola de AWS AppSync
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Ve a &lt;a href="https://console.aws.amazon.com/appsync" rel="noopener noreferrer"&gt;https://console.aws.amazon.com/appsync&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Si te pide iniciar sesión, usa tu cuenta de AWS&lt;/li&gt;
&lt;li&gt;Asegúrate de estar en la región que prefieras (yo uso &lt;code&gt;us-east-1&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&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%2Fv0lj7fyzmdy82lhepxkp.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%2Fv0lj7fyzmdy82lhepxkp.png" alt="Consola de AWS AppSync - Vista inicial" width="800" height="579"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Primera vez en AWS?&lt;/strong&gt; La interfaz puede parecer abrumadora, pero no te preocupes. Solo vamos a usar AppSync, nada más.&lt;/p&gt;

&lt;h3&gt;
  
  
  Paso 2: Crea tu API
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Haz clic en el botón naranja &lt;strong&gt;"Create API"&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Verás dos opciones: &lt;strong&gt;GraphQL API&lt;/strong&gt; y &lt;strong&gt;Events API&lt;/strong&gt;. Selecciona &lt;strong&gt;"GraphQL API"&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&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%2Feje7r1fj59c3yusrx080.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%2Feje7r1fj59c3yusrx080.png" alt="Selección de tipo de API" width="800" height="655"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ahora verás una sección llamada &lt;strong&gt;"API type"&lt;/strong&gt; con dos opciones:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GraphQL APIs&lt;/strong&gt; ← Selecciona esta&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Merged APIs&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&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%2Faxa5yhi1q389pjzxcj9z.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%2Faxa5yhi1q389pjzxcj9z.png" alt="Selección de GraphQL APIs" width="800" height="577"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Después de seleccionar "GraphQL APIs", verás opciones de templates. Selecciona &lt;strong&gt;"Design from scratch"&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Haz clic en &lt;strong&gt;"Next"&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;💡 &lt;strong&gt;¿Por qué GraphQL API y no Events API?&lt;/strong&gt; Events API es para casos de uso específicos de eventos. Nosotros queremos una API de GraphQL tradicional.&lt;/li&gt;
&lt;li&gt;💡 &lt;strong&gt;¿Por qué GraphQL APIs y no Merged APIs?&lt;/strong&gt; Merged APIs es para combinar múltiples APIs en una sola. Como estamos empezando, queremos una API simple.&lt;/li&gt;
&lt;li&gt;💡 &lt;strong&gt;¿Por qué "Build from scratch"?&lt;/strong&gt; Porque ya tenemos nuestro esquema del artículo anterior. Las otras opciones son templates que AppSync ofrece, pero nosotros queremos usar el nuestro.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Paso 3: Configura tu API
&lt;/h3&gt;

&lt;p&gt;Ahora verás un formulario con varias secciones. Vamos paso a paso:&lt;/p&gt;

&lt;h4&gt;
  
  
  Sección 1: API details
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;API name:&lt;/strong&gt; &lt;code&gt;Social-Media-API&lt;/code&gt; (o el nombre que prefieras)&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%2F3sgy01lem3halercq8yg.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%2F3sgy01lem3halercq8yg.png" alt="Formulario de configuración de API" width="800" height="749"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Qué poner en API name?&lt;/strong&gt; Usa algo descriptivo. Este nombre solo lo verás tú en la consola, no afecta el funcionamiento de tu API.&lt;/p&gt;

&lt;p&gt;Deja todo lo demás como está y haz clic en &lt;strong&gt;"Next"&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Sección 2: Specify GraphQL resources
&lt;/h4&gt;

&lt;p&gt;Aquí AppSync te pregunta si quieres conectar una fuente de datos ahora o después. Verás un mensaje que dice:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Your API can connect to one or more data sources like Amazon DynamoDB, Amazon OpenSearch, Amazon Aurora, AWS Lambda, Amazon EventBridge, or any HTTP API. You can add your data source later or optionally use this step to generate a GraphQL type backed by a DynamoDB table."&lt;/p&gt;
&lt;/blockquote&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%2Ft63vwxhqt02zreut4qsg.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%2Ft63vwxhqt02zreut4qsg.png" alt="Specify GraphQL resources" width="800" height="473"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vamos a elegir &lt;code&gt;Create GraphQL resources later&lt;/code&gt; ya que nosotros tenemos nuestro esquema diseñado del artículo anterior. No necesitamos que AppSync genere uno por nosotros. Además, en esta serie usaremos mock data, no DynamoDB.&lt;/p&gt;

&lt;p&gt;Haz clic en &lt;strong&gt;"Next"&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Sección 3: Review and create
&lt;/h4&gt;

&lt;p&gt;Ahora verás un resumen de todo lo que configuraste:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API type: GraphQL APIs&lt;/li&gt;
&lt;li&gt;API name: Social-Media-API&lt;/li&gt;
&lt;li&gt;GraphQL resources: Create later&lt;/li&gt;
&lt;/ul&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%2Fkbr7mwc1212qmrkyqodc.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%2Fkbr7mwc1212qmrkyqodc.png" alt="Review and create" width="800" height="738"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Revisa que todo esté correcto y haz clic en &lt;strong&gt;"Create API"&lt;/strong&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%2Fuf96zmoca68a5bkbcky4.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%2Fuf96zmoca68a5bkbcky4.png" alt="API creada exitosamente" width="800" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¡Felicidades!&lt;/strong&gt; Acabas de crear tu primera API de GraphQL en AppSync. Ahora vamos a subirle el esquema.&lt;/p&gt;

&lt;h3&gt;
  
  
  Paso 4: Sube tu esquema
&lt;/h3&gt;

&lt;p&gt;Ahora estás en el dashboard de tu API. Verás dos secciones principales con información sobre los próximos pasos.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;En el menú lateral izquierdo, haz clic en &lt;strong&gt;"Schema"&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&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%2Fnp92da69kusz39v9kp9v.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%2Fnp92da69kusz39v9kp9v.png" alt="Menú lateral con Schema" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Verás un editor de texto grande con algunos comentarios por defecto. Aquí es donde va tu esquema.&lt;/li&gt;
&lt;/ol&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%2F78fjx4c20qsvkvhixbb0.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%2F78fjx4c20qsvkvhixbb0.png" alt="Editor de schema vacío" width="800" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Abre tu archivo &lt;code&gt;schema.graphql&lt;/code&gt; del artículo anterior&lt;/li&gt;
&lt;li&gt;Borra los comentarios que están por defecto en el editor de AppSync&lt;/li&gt;
&lt;li&gt;Copia &lt;strong&gt;todo&lt;/strong&gt; el contenido del archivo&lt;/li&gt;
&lt;li&gt;Pégalo en el editor de AppSync&lt;/li&gt;
&lt;li&gt;Haz clic en el botón naranja &lt;strong&gt;"Save Schema"&lt;/strong&gt; en la esquina superior derecha&lt;/li&gt;
&lt;/ol&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%2Fdyi6k9hef9b5o88bytox.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%2Fdyi6k9hef9b5o88bytox.png" alt="Botón Save Schema" width="800" height="739"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;¿Qué pasa si ves errores?&lt;/strong&gt; AppSync valida tu esquema automáticamente. Si hay algún error de sintaxis, te lo mostrará en rojo. Revisa que hayas copiado todo el esquema correctamente.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;Si todo está bien, verás un mensaje de confirmación: &lt;strong&gt;"Schema saved successfully"&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;¡Excelente!&lt;/strong&gt; Tu esquema ya está desplegado en la nube. AppSync lo validó, lo procesó, y ahora está listo para usarse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Parte 4: Explorando tu API desplegada
&lt;/h2&gt;

&lt;p&gt;Ahora que tu esquema está en AppSync, vamos a explorar qué generó automáticamente para ti.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tu endpoint de API
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;En el menú lateral, haz clic en &lt;strong&gt;"Settings"&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Busca la sección &lt;strong&gt;"API URL"&lt;/strong&gt; o &lt;strong&gt;"GraphQL endpoint"&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Verás una URL que se ve algo así: &lt;code&gt;https://xxxxxxxxxx.appsync-api.us-east-1.amazonaws.com/graphql&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&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%2Fisx0f2yoct6325j4efhb.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%2Fisx0f2yoct6325j4efhb.png" alt="API endpoint en Settings" width="800" height="602"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Esta es la URL de tu API.&lt;/strong&gt; Cualquier cliente de GraphQL puede usarla para hacer queries y mutations. Por ahora, solo tú tienes acceso (hablaremos de autenticación en futuros artículos).&lt;/p&gt;

&lt;h3&gt;
  
  
  Documentación auto-generada
&lt;/h3&gt;

&lt;p&gt;Volvamos a explorar algo increíble de GraphQL: la documentación se genera automáticamente desde tu esquema.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;En el menú lateral, haz clic en &lt;strong&gt;"Queries"&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Verás un editor de queries (lo usaremos en un momento)&lt;/li&gt;
&lt;li&gt;En la esquina superior derecha del editor, busca un botón o ícono que diga &lt;strong&gt;"Docs"&lt;/strong&gt; o tenga un símbolo de libro 📖&lt;/li&gt;
&lt;/ol&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%2Fpbvy7mw5x5iqiaen751k.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%2Fpbvy7mw5x5iqiaen751k.png" alt="Botón de documentación" width="800" height="764"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Haz clic en "Docs" y se abrirá un panel lateral&lt;/li&gt;
&lt;/ol&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%2Fy6cyee666i61tekk7c44.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%2Fy6cyee666i61tekk7c44.png" alt="Panel de documentación" width="800" height="638"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Todos tus tipos: &lt;code&gt;Usuario&lt;/code&gt;, &lt;code&gt;Publicacion&lt;/code&gt;, &lt;code&gt;Comentario&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Tus queries: &lt;code&gt;usuario&lt;/code&gt;, &lt;code&gt;publicacion&lt;/code&gt;, &lt;code&gt;publicaciones&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Tus mutations: &lt;code&gt;crearUsuario&lt;/code&gt;, &lt;code&gt;crearPublicacion&lt;/code&gt;, &lt;code&gt;crearComentario&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Los campos de cada tipo con sus tipos de datos&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Esto es introspección en acción.&lt;/strong&gt; GraphQL permite que cualquier cliente "pregunte" sobre el esquema. Así es como herramientas como este playground saben qué campos existen y pueden darte autocompletado.&lt;/p&gt;

&lt;h3&gt;
  
  
  Explorando un tipo
&lt;/h3&gt;

&lt;p&gt;En el panel de documentación:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Haz clic en el tipo &lt;strong&gt;"Usuario"&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Verás todos sus campos: &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;nombre&lt;/code&gt;, &lt;code&gt;email&lt;/code&gt;, &lt;code&gt;fotoPerfil&lt;/code&gt;, &lt;code&gt;publicaciones&lt;/code&gt;, &lt;code&gt;creadoEn&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Nota que cada campo muestra su tipo: &lt;code&gt;String!&lt;/code&gt;, &lt;code&gt;[Publicacion!]!&lt;/code&gt;, etc.&lt;/li&gt;
&lt;/ol&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%2F7elkajv9v131aqc62whc.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%2F7elkajv9v131aqc62whc.png" alt="Documentación del tipo Usuario" width="800" height="638"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Por qué esto es importante?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No necesitas documentación separada que se desactualice&lt;/li&gt;
&lt;li&gt;El esquema ES la documentación&lt;/li&gt;
&lt;li&gt;Cualquier cambio en el esquema se refleja inmediatamente aquí&lt;/li&gt;
&lt;li&gt;Los desarrolladores que usen tu API pueden explorarla sin preguntarte&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Parte 5: ¿Qué sigue?
&lt;/h2&gt;

&lt;p&gt;Has llegado lejos. Veamos qué tienes ahora y qué nos falta.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lo que ya tienes ✅
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Esquema desplegado&lt;/strong&gt; - Tu contrato de API está en la nube&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Endpoint de API&lt;/strong&gt; - Una URL que los clientes pueden usar&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentación automática&lt;/strong&gt; - Generada desde tu esquema&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Playground para probar&lt;/strong&gt; - El editor de queries está listo&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Lo que nos falta ⏳
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Resolvers&lt;/strong&gt; - La lógica que retorna datos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Datos para consultar&lt;/strong&gt; - Por ahora, las queries retornarían &lt;code&gt;null&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;¿Por qué no probamos queries ahora?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Porque sin resolvers, todas las queries retornarían &lt;code&gt;null&lt;/code&gt; o errores. Sería como tener un restaurante con menú pero sin cocina. El menú (esquema) está listo, pero necesitamos la cocina (resolvers) para servir comida (datos).&lt;/p&gt;

&lt;h3&gt;
  
  
  En el próximo artículo
&lt;/h3&gt;

&lt;p&gt;En la &lt;strong&gt;Parte 4 de esta serie&lt;/strong&gt;, vamos a:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Entender qué son los resolvers&lt;/strong&gt; - El puente entre tu esquema y tus datos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implementar resolvers con TypeScript&lt;/strong&gt; - Código con type safety&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Usar mock data&lt;/strong&gt; - Datos de prueba para ver resultados inmediatos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Probar queries reales&lt;/strong&gt; - Ver tu API funcionando de verdad&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implementar mutations&lt;/strong&gt; - Crear usuarios, publicaciones y comentarios&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Después de ese artículo, podrás hacer queries como esta y obtener resultados reales:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;publicaciones&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;contenido&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;autor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;nombre&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;comentarios&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;contenido&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Antes del próximo artículo
&lt;/h3&gt;

&lt;p&gt;Te recomiendo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Explora la documentación&lt;/strong&gt; - Familiarízate con el panel de Docs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Revisa tu esquema&lt;/strong&gt; - Asegúrate de que tiene todo lo que necesitas&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Guarda tu API endpoint&lt;/strong&gt; - Lo necesitarás después&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Conclusión
&lt;/h2&gt;

&lt;p&gt;Hoy diste un paso importante: llevaste tu esquema de un archivo local a una API desplegada en la nube.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lo que lograste
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;✅ Entendiste las opciones para correr GraphQL APIs&lt;/li&gt;
&lt;li&gt;✅ Conociste AWS AppSync y qué hace por ti&lt;/li&gt;
&lt;li&gt;✅ Creaste tu primera API en AppSync&lt;/li&gt;
&lt;li&gt;✅ Desplegaste tu esquema&lt;/li&gt;
&lt;li&gt;✅ Exploraste la documentación auto-generada&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  El viaje hasta ahora
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Parte 1:&lt;/strong&gt; Introducción a GraphQL - Entendimos qué es y por qué usarlo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parte 2:&lt;/strong&gt; Diseño del esquema - Creamos nuestro contrato de API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parte 3:&lt;/strong&gt; Deployment (este artículo) - Desplegamos el esquema en AppSync&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Próximo:&lt;/strong&gt; Implementación de resolvers - Haremos que la API funcione&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tu API está lista para el siguiente paso
&lt;/h3&gt;

&lt;p&gt;Tienes una API de GraphQL desplegada con:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Un esquema validado&lt;/li&gt;
&lt;li&gt;Documentación automática&lt;/li&gt;
&lt;li&gt;Un endpoint público (protegido por ahora)&lt;/li&gt;
&lt;li&gt;Todo listo para agregar resolvers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;En el próximo artículo, le daremos vida a esta API. Implementaremos los resolvers que harán que tus queries y mutations realmente funcionen.&lt;/p&gt;

&lt;p&gt;Por ahora, celebra este logro. Pasaste de un archivo de texto a una API en la nube. Eso no es poca cosa. 🚀&lt;/p&gt;




&lt;h2&gt;
  
  
  Recursos
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/ramtoearth/graphql-una-introduccion-para-nuevos-desarrolladores-1bfg"&gt;Parte 1: GraphQL - Una introducción&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/ramtoearth/graphql-disenando-tu-primer-esquema-505p"&gt;Parte 2: Diseñando tu primer esquema&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/appsync/" rel="noopener noreferrer"&gt;AWS AppSync Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://graphql.org/learn/introspection/" rel="noopener noreferrer"&gt;GraphQL Introspection&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>graphql</category>
      <category>spanish</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>GraphQL: Diseñando tu primer esquema</title>
      <dc:creator>Ramses Mata</dc:creator>
      <pubDate>Wed, 25 Feb 2026 17:07:04 +0000</pubDate>
      <link>https://dev.to/aws/graphql-disenando-tu-primer-esquema-505p</link>
      <guid>https://dev.to/aws/graphql-disenando-tu-primer-esquema-505p</guid>
      <description>&lt;p&gt;GraphQL ha sido una tecnología que ha revolucionado el mundo de las APIs y muchas empresas están usando esta tecnología. Si no sabes qué es GraphQL, en mi &lt;a href="https://dev.to/ramtoearth/graphql-una-introduccion-para-nuevos-desarrolladores-1bfg"&gt;artículo anterior&lt;/a&gt; hablé de esta tecnología, qué es, qué pretende resolver e introduje un pequeño ejemplo que vamos a ver más a detalle en esta serie.&lt;/p&gt;

&lt;p&gt;En esta serie de seis artículos, vamos a construir una API de GraphQL completa desde cero. Dividiremos el proceso en las siguientes partes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://dev.to/ramtoearth/graphql-una-introduccion-para-nuevos-desarrolladores-1bfg"&gt;GraphQL: Una introducción para nuevos desarrolladores&lt;/a&gt;&lt;/strong&gt; - Qué es GraphQL y qué problemas resuelve&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Diseño del esquema&lt;/strong&gt; (este artículo) - Diseñaremos el contrato de nuestra API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Creando y desplegando tu API&lt;/strong&gt; - Desplegaremos el schema en AWS AppSync&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implementando resolvers con TypeScript&lt;/strong&gt; - Construiremos la lógica de queries y mutations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-time en aplicaciones web: Polling vs Subscriptions&lt;/strong&gt; - Entenderemos las opciones para datos en tiempo real&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implementando Subscriptions con AppSync&lt;/strong&gt; - Agregaremos actualizaciones en vivo a nuestra API&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Hoy nos enfocaremos en la parte más fundamental: el diseño del esquema. Como aprendiste en el artículo anterior, el esquema es como un contrato entre el cliente y el servidor que define qué datos están disponibles y cómo están estructurados. Un buen diseño de esquema es crucial para crear una API que sea fácil de usar, mantener y escalar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trabajando hacia atrás desde los requisitos del negocio
&lt;/h2&gt;

&lt;p&gt;Antes de escribir una sola línea de código, necesitamos entender el problema que estamos tratando de resolver. Podemos pensar nuestro esquema de GraphQL como un lenguaje compartido entre los miembros de tu equipo, para construir un buen esquema, piensa en el lenguaje que usas para describir tu negocio.&lt;/p&gt;

&lt;p&gt;Sigamos con el ejemplo del artículo anterior, en el cual estábamos construyendo una red social. Si fuésemos un usuario de una red social como las que usamos a diario probablemente definiríamos nuestra app de la siguiente manera:&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%2Fksvdzes2ggn00imo25op.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%2Fksvdzes2ggn00imo25op.png" alt="User Stories" width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nuestra aplicación necesita por lo menos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Perfiles de usuario con información básica&lt;/li&gt;
&lt;li&gt;Publicaciones que los usuarios pueden crear&lt;/li&gt;
&lt;li&gt;Comentarios en las publicaciones&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Basándonos en esto podemos deducir algunas &lt;strong&gt;entidades&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Usuario&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Publicación&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Comentario&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Y también las &lt;strong&gt;relaciones&lt;/strong&gt; que pueden tener entre ellos.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Un &lt;strong&gt;Usuario&lt;/strong&gt; puede crear muchas Publicaciones&lt;/li&gt;
&lt;li&gt;Un &lt;strong&gt;Usuario&lt;/strong&gt; puede crear muchos Comentarios&lt;/li&gt;
&lt;li&gt;Una &lt;strong&gt;Publicación&lt;/strong&gt; puede tener muchos Comentarios&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Si eres más visual, este diagrama te puede ayudar a entender mejor cómo se relacionan estas entidades:&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%2Fmhr30a3b4e8fswatr9p9.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%2Fmhr30a3b4e8fswatr9p9.png" alt="Entity Relation" width="800" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ahora que entendemos nuestro negocio, las entidades involucradas y cómo se relacionan entre ellas, podemos comenzar a pensar en cómo representarlas en GraphQL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Diseño del esquema y mejores prácticas
&lt;/h2&gt;

&lt;p&gt;Cada entidad principal será un &lt;a href="https://graphql.org/learn/schema/#object-types-and-fields" rel="noopener noreferrer"&gt;Type&lt;/a&gt;, las acciones (crear una publicación, comentar, dar me gusta) serán &lt;a href="https://graphql.org/learn/mutations/" rel="noopener noreferrer"&gt;Mutations&lt;/a&gt;, las consultas (obtener el perfil, feed de publicaciones) serán &lt;a href="https://graphql.org/learn/queries/" rel="noopener noreferrer"&gt;Queries&lt;/a&gt; y las actualizaciones en tiempo real serán &lt;a href="https://graphql.org/learn/subscriptions/" rel="noopener noreferrer"&gt;Subscriptions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Antes de sumergirnos en la implementación, vamos a revisar algunas buenas prácticas que nos ayudarán a crear esquemas robustos y mantenibles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Usa &lt;a href="https://wiki.c2.com/?PascalCase" rel="noopener noreferrer"&gt;PascalCase&lt;/a&gt; para tipos, interfaces y uniones (ejemplo: &lt;code&gt;Usuario&lt;/code&gt;, &lt;code&gt;Publicacion&lt;/code&gt;, &lt;code&gt;Comentario&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Usa &lt;a href="https://wiki.c2.com/?CamelCase" rel="noopener noreferrer"&gt;camelCase&lt;/a&gt; para campos y argumentos (ejemplo: &lt;code&gt;fotoPerfil&lt;/code&gt;, &lt;code&gt;creadoEn&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Marca campos como no nulos con &lt;code&gt;!&lt;/code&gt; solo cuando realmente lo sean&lt;/li&gt;
&lt;li&gt;Usa tipos &lt;a href="https://graphql.org/learn/schema/#scalar-types" rel="noopener noreferrer"&gt;escalares&lt;/a&gt; apropiados (&lt;code&gt;Int&lt;/code&gt;, &lt;code&gt;Float&lt;/code&gt;, &lt;code&gt;String&lt;/code&gt;, &lt;code&gt;Boolean&lt;/code&gt;, &lt;code&gt;ID&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ahora definamos nuestro esquema GraphQL. Pondremos todo en un solo archivo, lo cual funciona bien para proyectos de este tamaño. Conforme un esquema crece, es común dividirlo en varios archivos para mejor organización — eso lo veremos en futuros blog posts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Construyendo nuestro esquema paso a paso
&lt;/h2&gt;

&lt;p&gt;Antes de empezar, una nota práctica: los esquemas de GraphQL se escriben en archivos con extensión &lt;code&gt;.graphql&lt;/code&gt; usando algo llamado &lt;strong&gt;&lt;a href="https://www.prisma.io/blog/graphql-sdl-schema-definition-language-6755bcb9ce51" rel="noopener noreferrer"&gt;SDL (Schema Definition Language)&lt;/a&gt;&lt;/strong&gt;. Es un lenguaje específico para definir esquemas, similar a cómo JSON tiene su propia sintaxis. &lt;/p&gt;

&lt;p&gt;Para este proyecto, pondremos todo en un solo archivo llamado &lt;code&gt;schema.graphql&lt;/code&gt;. Conforme tu esquema crezca, podrás dividirlo en múltiples archivos organizados por dominio o funcionalidad — pero para aprender, un solo archivo es perfecto.&lt;/p&gt;

&lt;p&gt;Ahora sí, empecemos con nuestro primer tipo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Paso 1: Definiendo el tipo Usuario
&lt;/h3&gt;

&lt;p&gt;Empecemos con la entidad más básica: el usuario. Recordemos que en nuestros requisitos dijimos que un usuario necesita tener un perfil con nombre, foto y biografía.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Usuario&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;nombre&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;fotoPerfil&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;creadoEn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Tip importante:&lt;/strong&gt; Usa &lt;code&gt;!&lt;/code&gt; solo cuando estés 100% seguro de que el campo siempre tendrá un valor. Si tienes dudas, déjalo opcional. Es más fácil hacer un campo obligatorio después que lidiar con errores porque marcaste algo como obligatorio cuando no debía serlo.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Pero espera... ¿no dijimos que un usuario puede tener muchas publicaciones? Agreguemos esa relación:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Usuario&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;nombre&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;fotoPerfil&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;publicaciones&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Publicacion&lt;/span&gt;&lt;span class="p"&gt;!]!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;creadoEn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aquí pasa algo interesante con &lt;code&gt;publicaciones: [Publicacion!]!&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;publicaciones&lt;/code&gt; es un campo que retorna una lista de publicaciones&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[Publicacion!]!&lt;/code&gt; significa: "una lista que nunca es nula, que contiene Publicaciones que nunca son nulas"&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Paso 2: Definiendo el tipo Publicacion
&lt;/h3&gt;

&lt;p&gt;Ahora que tenemos usuarios, necesitamos que puedan crear publicaciones. Una publicación necesita contenido, saber quién la escribió, y poder tener comentarios.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Publicacion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;contenido&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;autor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Usuario&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;comentarios&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Comentario&lt;/span&gt;&lt;span class="p"&gt;!]!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;creadoEn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;¿Recuerdas el &lt;a href="https://dev.to/ramtoearth/graphql-una-introduccion-para-nuevos-desarrolladores-1bfg"&gt;problema N+1 del artículo anterior&lt;/a&gt;?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Con REST teníamos que hacer:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;GET /api/usuarios/123&lt;/code&gt; - Obtener el usuario&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /api/usuarios/123/publicaciones&lt;/code&gt; - Obtener sus publicaciones&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /api/publicaciones/456/comentarios&lt;/code&gt; - Obtener comentarios de cada publicación&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /api/usuarios/789&lt;/code&gt; - Obtener info del autor de cada comentario&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Con GraphQL, gracias a que &lt;code&gt;autor&lt;/code&gt; retorna un &lt;code&gt;Usuario&lt;/code&gt; completo y &lt;code&gt;comentarios&lt;/code&gt; retorna &lt;code&gt;[Comentario!]!&lt;/code&gt;, podemos hacer todo en una sola query. Ya veremos cómo en un momento.&lt;/p&gt;

&lt;h3&gt;
  
  
  Paso 3: Definiendo el tipo Comentario
&lt;/h3&gt;

&lt;p&gt;Los comentarios son la última pieza de nuestras entidades principales. Un comentario necesita saber qué dice, quién lo escribió, y a qué publicación pertenece.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Comentario&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;contenido&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;autor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Usuario&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;publicacion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Publicacion&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;creadoEn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Navegación bidireccional&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Fíjate en algo interesante:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Publicacion&lt;/code&gt; tiene un campo &lt;code&gt;comentarios&lt;/code&gt; que retorna &lt;code&gt;[Comentario!]!&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Comentario&lt;/code&gt; tiene un campo &lt;code&gt;publicacion&lt;/code&gt; que retorna &lt;code&gt;Publicacion!&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Esto significa que puedes navegar en ambas direcciones. Si tienes una publicación, puedes obtener sus comentarios. Si tienes un comentario, puedes obtener su publicación. Esta flexibilidad es uno de los superpoderes de GraphQL.&lt;/p&gt;

&lt;h3&gt;
  
  
  Paso 4: Input Types - Preparando para las Mutations
&lt;/h3&gt;

&lt;p&gt;Ahora que tenemos nuestras entidades, necesitamos definir cómo se crearán. Para esto, GraphQL usa algo llamado &lt;strong&gt;Input Types&lt;/strong&gt;. Son como moldes que definen qué información necesitas enviar cuando quieres crear o modificar algo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CrearUsuarioInput&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;nombre&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;fotoPerfil&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CrearPublicacionInput&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;contenido&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;autorId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CrearComentarioInput&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;contenido&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;publicacionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;autorId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;¿Por qué necesitamos Input Types separados?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Fíjate que &lt;code&gt;CrearUsuarioInput&lt;/code&gt; es diferente a &lt;code&gt;Usuario&lt;/code&gt;. ¿Por qué no usar el mismo tipo?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cuando &lt;strong&gt;creas&lt;/strong&gt; un usuario, no tienes &lt;code&gt;id&lt;/code&gt; ni &lt;code&gt;creadoEn&lt;/code&gt; (el servidor los genera)&lt;/li&gt;
&lt;li&gt;Cuando &lt;strong&gt;consultas&lt;/strong&gt; un usuario, sí necesitas esos campos&lt;/li&gt;
&lt;li&gt;Los Input Types solo incluyen lo que el cliente debe enviar&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Nota importante:&lt;/strong&gt; En el próximo artículo veremos cómo usar estos Input Types en Mutations para crear usuarios, publicaciones y comentarios. Por ahora, solo estamos definiendo la estructura.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Paso 5: Definiendo las Queries - ¿Qué podemos consultar?
&lt;/h3&gt;

&lt;p&gt;Las Queries son las operaciones de &lt;strong&gt;lectura&lt;/strong&gt; de tu API. Son como las preguntas que puedes hacerle a tu sistema: "¿Quién es este usuario?" o "¿Cuáles son las últimas publicaciones?"&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;usuario&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Usuario&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;publicacion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Publicacion&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;publicaciones&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Publicacion&lt;/span&gt;&lt;span class="p"&gt;!]!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;¿Qué estamos definiendo?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;usuario(id: ID!): Usuario&lt;/code&gt; - Buscar un usuario específico por su ID. Puede retornar &lt;code&gt;null&lt;/code&gt; si no existe.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;publicacion(id: ID!): Publicacion&lt;/code&gt; - Buscar una publicación específica por su ID.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;publicaciones: [Publicacion!]!&lt;/code&gt; - Obtener todas las publicaciones. Siempre retorna una lista (aunque esté vacía).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;¿Por qué &lt;code&gt;usuario&lt;/code&gt; puede ser null pero &lt;code&gt;publicaciones&lt;/code&gt; no?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cuando buscas un usuario por ID, puede que no exista, entonces retornas &lt;code&gt;null&lt;/code&gt;. Pero cuando pides todas las publicaciones, siempre puedes retornar una lista (aunque esté vacía &lt;code&gt;[]&lt;/code&gt;). Es más predecible para quien usa tu API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Paso 6: Definiendo las Mutations - ¿Qué podemos modificar?
&lt;/h3&gt;

&lt;p&gt;Las Mutations son las operaciones de &lt;strong&gt;escritura&lt;/strong&gt; de tu API. Son las acciones que cambian el estado de tu sistema: crear usuarios, publicar contenido, comentar.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Mutation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;crearUsuario&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CrearUsuarioInput&lt;/span&gt;&lt;span class="p"&gt;!):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Usuario&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;crearPublicacion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CrearPublicacionInput&lt;/span&gt;&lt;span class="p"&gt;!):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Publicacion&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;crearComentario&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CrearComentarioInput&lt;/span&gt;&lt;span class="p"&gt;!):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Comentario&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;¿Por qué las Mutations retornan el objeto creado?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Esto es una convención muy útil en GraphQL. Cuando creas algo, inmediatamente obtienes el objeto completo de vuelta, incluyendo campos que el servidor generó (como &lt;code&gt;id&lt;/code&gt; y &lt;code&gt;creadoEn&lt;/code&gt;). Así el cliente no necesita hacer una query adicional para obtener esa información.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Por qué usamos &lt;code&gt;input&lt;/code&gt; en lugar de parámetros sueltos?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Compara estas dos opciones:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="c"&gt;# ❌ Parámetros sueltos (difícil de mantener)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;crearUsuario(nombre:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;email:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;fotoPerfil:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;String):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Usuario&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# ✅ Input Type (fácil de extender)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;crearUsuario(&lt;/span&gt;&lt;span class="k"&gt;input&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CrearUsuarioInput&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="err"&gt;):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Usuario&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si mañana necesitas agregar un campo nuevo (como &lt;code&gt;biografia&lt;/code&gt;), con Input Types solo modificas &lt;code&gt;CrearUsuarioInput&lt;/code&gt; sin romper la firma de la mutation.&lt;/p&gt;

&lt;h2&gt;
  
  
  El esquema completo
&lt;/h2&gt;

&lt;p&gt;Ahora que hemos construido cada pieza paso a paso, veamos cómo luce nuestro esquema completo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Usuario&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;nombre&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;fotoPerfil&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;publicaciones&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Publicacion&lt;/span&gt;&lt;span class="p"&gt;!]!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;creadoEn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Publicacion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;contenido&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;autor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Usuario&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;comentarios&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Comentario&lt;/span&gt;&lt;span class="p"&gt;!]!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;creadoEn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Comentario&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;contenido&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;autor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Usuario&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;publicacion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Publicacion&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;creadoEn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CrearUsuarioInput&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;nombre&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;fotoPerfil&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CrearPublicacionInput&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;contenido&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;autorId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CrearComentarioInput&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;contenido&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;publicacionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;autorId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;usuario&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Usuario&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;publicacion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Publicacion&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;publicaciones&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Publicacion&lt;/span&gt;&lt;span class="p"&gt;!]!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Mutation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;crearUsuario&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CrearUsuarioInput&lt;/span&gt;&lt;span class="p"&gt;!):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Usuario&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;crearPublicacion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CrearPublicacionInput&lt;/span&gt;&lt;span class="p"&gt;!):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Publicacion&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;crearComentario&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CrearComentarioInput&lt;/span&gt;&lt;span class="p"&gt;!):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Comentario&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusión
&lt;/h2&gt;

&lt;p&gt;Hemos diseñado un esquema completo de GraphQL trabajando hacia atrás desde nuestros requisitos de negocio. Empezamos identificando las entidades principales (Usuario, Publicacion, Comentario), definimos sus relaciones, y estructuramos las operaciones que nuestra API necesita soportar.&lt;/p&gt;

&lt;p&gt;Lo más importante que aprendimos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;El esquema es un contrato&lt;/strong&gt; - Define exactamente qué puede hacer tu API antes de escribir una línea de código de implementación&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Piensa en relaciones, no en IDs&lt;/strong&gt; - GraphQL brilla cuando retornas objetos completos en lugar de solo identificadores&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Usa &lt;code&gt;!&lt;/code&gt; con intención&lt;/strong&gt; - Solo marca campos como obligatorios cuando realmente lo sean&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Input Types separan entrada de salida&lt;/strong&gt; - Facilitan la evolución de tu API sin romper clientes existentes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;En el próximo artículo de esta serie, vamos a desplegar este esquema en AWS AppSync y lo veremos funcionando con datos de prueba. Podrás hacer queries reales y ver tu API en acción, todo sin escribir una sola línea de código de backend todavía.&lt;/p&gt;

&lt;p&gt;Por ahora, tienes un esquema sólido que sirve como documentación viva de lo que tu API puede hacer. Y eso, en sí mismo, ya es un gran paso.&lt;/p&gt;

</description>
      <category>api</category>
      <category>beginners</category>
      <category>graphql</category>
      <category>spanish</category>
    </item>
    <item>
      <title>GraphQL: Una introducción para nuevos desarrolladores.</title>
      <dc:creator>Ramses Mata</dc:creator>
      <pubDate>Wed, 18 Feb 2026 18:06:04 +0000</pubDate>
      <link>https://dev.to/aws/graphql-una-introduccion-para-nuevos-desarrolladores-1bfg</link>
      <guid>https://dev.to/aws/graphql-una-introduccion-para-nuevos-desarrolladores-1bfg</guid>
      <description>&lt;h2&gt;
  
  
  ¿Qué es GraphQL?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://graphql.org/" rel="noopener noreferrer"&gt;GraphQL&lt;/a&gt; es una herramienta con la cual aplicaciones pueden comunicarse entre ellas, estableciendo de manera concreta, reglas y patrones que le permiten saber a las dos partes (&lt;a href="https://www-freecodecamp-org.translate.goog/news/how-the-web-works-part-ii-client-server-model-the-structure-of-a-web-application-735b4b6d76e3/?_x_tr_sl=en&amp;amp;_x_tr_tl=es&amp;amp;_x_tr_hl=es&amp;amp;_x_tr_pto=tc" rel="noopener noreferrer"&gt;cliente y servidor&lt;/a&gt;) qué información puede ser transferida, en qué formato, tipos de datos, etc.&lt;/p&gt;

&lt;p&gt;Este lenguaje de consultas lo desarrolló Meta (antes Facebook) originalmente, aunque ahora es totalmente Open Source, es desarrollado y ampliamente usado por una gran comunidad de desarrolladores.&lt;/p&gt;

&lt;h2&gt;
  
  
  La solución tradicional: REST APIs
&lt;/h2&gt;

&lt;p&gt;Una &lt;a href="https://dev.to/aws/que-pasa-realmente-cuando-una-app-pide-algo-19g0"&gt;API&lt;/a&gt; (Application Programming Interface) es la parte de una aplicación que le permite comunicarse con otra diferente. REST (Representational State Transfer) es una manera de diseñar APIs y ha sido un estándar por más de una década. En REST todo se trata como si fuera un recurso (un usuario, un post o un producto) y utilizamos métodos HTTP (GET, POST, PUT, DELETE) para interactuar con esos recursos.&lt;/p&gt;

&lt;p&gt;Por ejemplo, imagina que estuvieras construyendo una red social y necesitaras la siguiente información:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Detalles del usuario (nombre, foto de perfil)&lt;/li&gt;
&lt;li&gt;  Sus últimas 3 publicaciones&lt;/li&gt;
&lt;li&gt;  De cada publicación los top 2 comentarios&lt;/li&gt;
&lt;li&gt;  Por cada comentario, el nombre y la foto de perfil del usuario que comenta&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Para obtener esta información (generalmente y dependiendo de la implementación) tendríamos que hacerlo en múltiples pasos.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.  Primero, obtener la información del usuario
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Petición: &lt;code&gt;GET /api/usuarios/123&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Respuesta:&lt;/li&gt;
&lt;/ol&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%2Fnpm42c618pnt0bs4pe9u.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%2Fnpm42c618pnt0bs4pe9u.png" alt="REST API Response 1" width="800" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2.  Después obtendríamos sus publicaciones:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Petición: &lt;code&gt;GET /api/usuarios/123/publicaciones?limit=3&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Respuesta:&lt;/li&gt;
&lt;/ol&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%2Fdozktjtfb0zm5rx6z9b2.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%2Fdozktjtfb0zm5rx6z9b2.png" alt="REST API Response 2" width="800" height="767"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3.  Por cada publicación tendríamos que obtener sus comentarios:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;GET /api/publicaciones/456/comentarios?limit=2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /api/publicaciones/457/comentarios?limit=&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /api/publicaciones/458/comentarios?limit=2&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  4.  Por cada comentario tendríamos, que obtener la información de cada usuario para desplegar su foto y su nombre
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;GET /api/usuarios/456&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /api/usuarios/789&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;...&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;El problema de este ejemplo es conocido como el problema n + 1 y es un reto clásico cuando se trabaja con REST APIs. El hacer muchas peticiones no solo puede traer problemas de rendimiento, sino también de documentación.&lt;/p&gt;

&lt;p&gt;Ahora veamos cómo GraphQL funciona y cómo puede ayudar a resolver estos problemas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Entendiendo GraphQL
&lt;/h2&gt;

&lt;p&gt;Podemos decir que GraphQL es una manera más precisa de comunicarse con APIs, en el ejemplo de un restaurante en lugar de ordenar platillos en la carta, tú le dices al chef exactamente que ingredientes quieres en tu platillo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conceptos clave
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1.    Esquema (Schema)&lt;/strong&gt;: Toda GraphQL API empieza con un esquema, el esquema lo podemos ver como un contrato que define qué datos están disponibles y cómo están estructurados.&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%2Fanbpfwny2wcoohmpf6kj.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%2Fanbpfwny2wcoohmpf6kj.png" alt="Defining GraphQL Schema" width="620" height="680"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.    Querys&lt;/strong&gt;: Las Querys de GraphQL lucen muy parecido a la información que tú esperas recibir, por ejemplo si hicieras la siguiente query:&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%2F3tg7hmwdmb6hqmx7vdv4.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%2F3tg7hmwdmb6hqmx7vdv4.png" alt="Simple GraphQL Query" width="470" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Esto regresaría el siguiente JSON con exactamente esa información:&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%2F617p4nwfh2egendmsdlf.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%2F617p4nwfh2egendmsdlf.png" alt="GraphQL Query Response" width="672" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.    Querys dentro de otras querys&lt;/strong&gt;: Un súper poder de GraphQL es su habilidad para pedir información de tipos de datos que están dentro de otros tipos.&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%2Fe8tksjltnpjgw1elrm4n.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%2Fe8tksjltnpjgw1elrm4n.png" alt="Nested Queries Example 1" width="470" height="568"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Esto es lo que hace a GraphQL poderoso cuando lo comparamos con REST, ¿recuerdas el ejemplo que veíamos anteriormente en el cuál hacíamos un montón de peticiones usando REST? ese ejemplo se podría ver de la siguiente manera en una simple query de GraphQL:&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%2Fmqw33tb6d5ncqbijrcrz.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%2Fmqw33tb6d5ncqbijrcrz.png" alt="Nested GraphQL Queries Example 2" width="588" height="904"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.    Un solo Endpoint&lt;/strong&gt;: A diferencia de REST que utiliza muchísimos endpoints GraphQL usualmente solo utiliza uno, usando múltiples Querys con la información necesaria.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5.    Suscripciones&lt;/strong&gt;: GraphQL no solo nos permite pedir información si no también recibir actualizaciones en tiempo real, usando algo llamado Suscripciones.&lt;/p&gt;

&lt;p&gt;En nuestra red social de ejemplo, si los usuarios estuviesen leyendo alguna publicación, podríamos suscribirnos a esa publicación, para saber cuándo, un usuario hace un nuevo comentario, de esta manera podemos actualizar la pantalla en tiempo real.&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%2F015dnajb7wxoy48fwijf.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%2F015dnajb7wxoy48fwijf.png" alt="GraphQL Subscription Example" width="790" height="530"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusión
&lt;/h2&gt;

&lt;p&gt;En conclusión ahora sabes qué es GraphQL y qué intenta solucionar; no solo es hacer menos peticiones, sino que también el esquema (Schema) actúa como una manera de documentar el alcance de tu API. A pesar de que en este ejemplo mostramos las ventajas de GraphQL sobre REST, lo cierto es que no existe una herramienta que resuelva absolutamente todo, cada una tiene su uso y la persona responsable de seleccionar estas herramientas debe tomar en cuenta los pros y contras de cada una según la situación.&lt;/p&gt;

&lt;p&gt;Muchas veces los desarrolladores nos emocionamos con nuevas tecnologías y queremos implementarlas en donde sea con tal de poner en practica lo que aprendemos, pero, lo que he aprendido con el tiempo es que, primero debemos entender el problema que queremos resolver y al final elegir la herramienta que mejor lo resuelva.&lt;/p&gt;

&lt;p&gt;Link al &lt;a href="https://dev.to/ramtoearth/graphql-disenando-tu-primer-esquema-505p"&gt;siguiente artículo&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>graphql</category>
      <category>beginners</category>
      <category>spanish</category>
    </item>
    <item>
      <title>AWS IoT Core Starter with Esp32, MQTT, Rust &amp; Terraform</title>
      <dc:creator>Ramses Mata</dc:creator>
      <pubDate>Tue, 23 Sep 2025 19:00:53 +0000</pubDate>
      <link>https://dev.to/ramtoearth/aws-iot-core-starter-with-esp32-mqtt-rust-terraform-2bb</link>
      <guid>https://dev.to/ramtoearth/aws-iot-core-starter-with-esp32-mqtt-rust-terraform-2bb</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Most ESP32 + AWS IoT Core examples/tutorials use the Arduino framework and rely on manually clicking through the AWS console. That approach works for quick demos, but it quickly becomes confusing and error-prone—especially if you need to reproduce the setup or scale to multiple devices.&lt;/p&gt;

&lt;p&gt;I recently created a repository that makes it much easier to get ESP32 devices securely talking to AWS IoT Core with Infrastructure-as-Code, and modern firmware written in Rust using the Espressif ESP-IDF framework.&lt;/p&gt;

&lt;h1&gt;
  
  
  Prerequisites
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;The tools needed to get this example working are described in the &lt;a href="https://github.com/RamMaths/aws-iot-esp32-example?tab=readme-ov-file#-quick-start-guide" rel="noopener noreferrer"&gt;repository&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Basic understanding of the MQTT protocol&lt;/li&gt;
&lt;li&gt;Basic understanding of terraform&lt;/li&gt;
&lt;li&gt;Clone the repository
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/RamMaths/aws-iot-esp32-example.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  What we are going to build
&lt;/h1&gt;

&lt;p&gt;Instead of another “hello world” demo, the goal here is to create a minimal but extensible template for ESP32 devices communicating with AWS IoT Core. &lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Subscribe to Topics
&lt;/h2&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%2F9wibwqazhxyxbgk3ofc9.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%2F9wibwqazhxyxbgk3ofc9.png" alt=" " width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In MQTT, a topic works like a communication channel between devices and services.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ESP32 devices subscribe to the topic: &lt;code&gt;esp32/1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;AWS IoT Core Test Client subscribes to the topic: &lt;code&gt;esp32/2&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This setup ensures that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Commands sent from the test client on &lt;code&gt;esp32/1&lt;/code&gt; will be received by the ESP32 devices.&lt;/li&gt;
&lt;li&gt;Device responses published to &lt;code&gt;esp32/2&lt;/code&gt; will be received by the test client.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 2: Publish a Command from the Test Client
&lt;/h2&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%2F67wa1r4eans07qher34u.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%2F67wa1r4eans07qher34u.png" alt=" " width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we use the AWS IoT Test Client to publish a message in JSON format to the topic &lt;code&gt;esp32/1&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ping"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The message field contains the command we want the ESP32 device to execute.&lt;/li&gt;
&lt;li&gt;In this example, we send "ping".&lt;/li&gt;
&lt;li&gt;The firmware listens for this value and matches it against predefined actions (e.g., respond with "pong").&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using JSON keeps the communication simple and extensible—you can later add more fields or new commands without changing the overall structure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Device Response
&lt;/h2&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%2Fa1h3aumdyqvrbngisyav.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%2Fa1h3aumdyqvrbngisyav.png" alt=" " width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When an ESP32 device receives a command, the firmware matches it against predefined actions.&lt;/p&gt;

&lt;p&gt;For the ping command, the device responds by publishing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pong"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to the topic &lt;code&gt;esp32/2&lt;/code&gt;.&lt;br&gt;
Since the test client is subscribed to this topic, it immediately receives the response.&lt;/p&gt;

&lt;p&gt;🔧 This example only implements a simple ping → pong workflow, but this project is designed to be extensible. For example, if a device subscribed to &lt;code&gt;esp32/light&lt;/code&gt;, you could add commands like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"on"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"off"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

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

&lt;/div&gt;



&lt;h1&gt;
  
  
  Acquiring cloud resources
&lt;/h1&gt;

&lt;p&gt;With the repository cloned and the terraform CLI installed, we can now provision the required AWS IoT Core resources.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Initialize Terraform
&lt;/h2&gt;

&lt;p&gt;Navigate into the Terraform directory and initialize the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;terraform
terraform init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;👉 Make sure your AWS credentials are configured (aws configure) before proceeding.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Configure Variables
&lt;/h2&gt;

&lt;p&gt;Copy the example variables file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp &lt;/span&gt;terraform.tfvars.example terraform.tfvars
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;terraform.tfvars&lt;/code&gt; and edit the things array. Each thing represents an IoT device in AWS IoT Core. For each device, you define:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;name&lt;/code&gt;: the device identifier in AWS IoT&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;topic_prefix&lt;/code&gt;: the MQTT topic namespace the device can publish/subscribe to&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example configuration for two ESP32 devices (S3 and C3):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Terraform variables for AWS IoT ESP32 Example&lt;/span&gt;
&lt;span class="c1"&gt;# Copy this file to terraform.tfvars and customize the values&lt;/span&gt;

&lt;span class="c1"&gt;# List of IoT Things to create with their configurations&lt;/span&gt;
&lt;span class="nx"&gt;things&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"esp32s3"&lt;/span&gt;
    &lt;span class="nx"&gt;topic_prefix&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"esp32"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"esp32c3"&lt;/span&gt;
    &lt;span class="nx"&gt;topic_prefix&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"esp32"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# AWS region where IoT resources will be created&lt;/span&gt;
&lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;

&lt;span class="c1"&gt;# Tags to apply to all AWS resources&lt;/span&gt;
&lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Project&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ESP32-IoT-Example"&lt;/span&gt;
  &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"development"&lt;/span&gt;
  &lt;span class="nx"&gt;ManagedBy&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform"&lt;/span&gt;
  &lt;span class="nx"&gt;Owner&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"your-name"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Apply Terraform
&lt;/h2&gt;

&lt;p&gt;Finally, deploy the resources:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;terraform&lt;/span&gt; &lt;span class="nx"&gt;plan&lt;/span&gt;
&lt;span class="k"&gt;terraform&lt;/span&gt; &lt;span class="nx"&gt;apply&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IoT Things for each device (esp32s3, esp32c3)&lt;/li&gt;
&lt;li&gt;Certificates and policies (automatically downloaded into certs/)&lt;/li&gt;
&lt;li&gt;Topic permissions based on the prefixes you specified&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✅ At this point, your AWS environment is fully provisioned and ready for your ESP32 devices to connect securely.&lt;/p&gt;

&lt;h1&gt;
  
  
  Compiling the Firmware
&lt;/h1&gt;

&lt;p&gt;With the cloud resources provisioned by Terraform, the next step is to configure, build, and flash the ESP32 firmware.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Configure the Project
&lt;/h2&gt;

&lt;p&gt;Navigate into the firmware directory for your ESP32 variant: &lt;/p&gt;

&lt;p&gt;After terraform creates the resources it will output something like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;firmware/example
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(See the &lt;a href="https://github.com/RamMaths/aws-iot-esp32-example?tab=readme-ov-file#%EF%B8%8F-detailed-setup-instructions" rel="noopener noreferrer"&gt;README setup guide&lt;/a&gt; for details on configuring Xtensa vs RISC-V targets.)&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Create &lt;code&gt;cfg.toml&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Terraform outputs the connection details you need. Your config should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[example]&lt;/span&gt;
&lt;span class="c"&gt;# Wi-Fi Configuration&lt;/span&gt;
&lt;span class="py"&gt;wifi_ssid&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"YOUR_WIFI_NETWORK"&lt;/span&gt;
&lt;span class="py"&gt;wifi_pass&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"YOUR_WIFI_PASSWORD"&lt;/span&gt;
&lt;span class="py"&gt;mqtt_url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"mqtts://your-endpoint.iot.region.amazonaws.com"&lt;/span&gt;
&lt;span class="py"&gt;mqtt_client_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"thing_name"&lt;/span&gt;
&lt;span class="py"&gt;mqtt_topic_pub&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"topic_prefix"&lt;/span&gt;
&lt;span class="py"&gt;mqtt_topic_sub&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"topic_prefix"&lt;/span&gt;
&lt;span class="py"&gt;cert_ca&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"certs/AmazonRootCA1.pem"&lt;/span&gt;
&lt;span class="py"&gt;cert_crt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"certs/[certificate-id]-certificate.pem.crt"&lt;/span&gt;
&lt;span class="py"&gt;cert_key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"certs/[certificate-id]-private.pem.key"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Replace &lt;code&gt;wifi_ssid&lt;/code&gt; and &lt;code&gt;wifi_pass&lt;/code&gt; with your network credentials.&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;mqtt_topic_pub&lt;/code&gt; = "esp32/2" and &lt;code&gt;mqtt_topic_sub&lt;/code&gt; = "esp32/1".&lt;/li&gt;
&lt;li&gt;Leave the rest as generated by Terraform.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Save this as &lt;code&gt;cfg.toml&lt;/code&gt; inside your firmware project.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Copy Certificates
&lt;/h2&gt;

&lt;p&gt;Copy the certificates for your device into the firmware directory. The folder must be named &lt;code&gt;certs&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="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; ../../terraform/certs/thing_name ./certs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Build and Flash
&lt;/h2&gt;

&lt;p&gt;Now compile and flash the firmware to your ESP32:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo build &lt;span class="nt"&gt;--release&lt;/span&gt;
cargo espflash ./path/to/the/binary &lt;span class="nt"&gt;-p&lt;/span&gt; /path/to/your/esp32/port
cargo espmonitor /path/to/your/esp32/port
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What to Expect in the Logs
&lt;/h2&gt;

&lt;p&gt;Once the ESP32 connects to Wi-Fi and establishes an MQTT session with AWS IoT Core, you should see logs similar to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;I &lt;span class="o"&gt;(&lt;/span&gt;1234&lt;span class="o"&gt;)&lt;/span&gt; example: WiFi connected successfully
I &lt;span class="o"&gt;(&lt;/span&gt;1235&lt;span class="o"&gt;)&lt;/span&gt; example: MQTT client created successfully
I &lt;span class="o"&gt;(&lt;/span&gt;1236&lt;span class="o"&gt;)&lt;/span&gt; example: Subscribed to topic &lt;span class="s2"&gt;"esp32/1"&lt;/span&gt;
I &lt;span class="o"&gt;(&lt;/span&gt;1237&lt;span class="o"&gt;)&lt;/span&gt; example: Starting main application loop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These messages confirm that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your device joined the Wi-Fi network&lt;/li&gt;
&lt;li&gt;The MQTT client was initialized correctly&lt;/li&gt;
&lt;li&gt;The subscription to the expected topic succeeded&lt;/li&gt;
&lt;li&gt;The application is now running and ready to handle messages&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common Errors
&lt;/h2&gt;

&lt;p&gt;It’s normal to briefly see messages like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Failed to subscribe to topic &lt;span class="s2"&gt;"esp32/1"&lt;/span&gt;: &lt;span class="o"&gt;{}&lt;/span&gt;, retrying...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This happens while AWS IoT validates the certificates and sets up the secure connection. As long as the retries eventually succeed and you see the subscription message, your setup is working correctly.&lt;/p&gt;

&lt;h1&gt;
  
  
  🎉 Congratulations!
&lt;/h1&gt;

&lt;p&gt;If you’ve followed along to this point, you’ve come a long way — from setting up cloud resources with Terraform, to configuring certificates, flashing firmware, and seeing your ESP32 securely connect to AWS IoT Core. That’s a huge achievement! 🚀&lt;/p&gt;

&lt;p&gt;Thank you for reading and coding along. I hope you not only got things working but also learned something new about how ESP32, MQTT, and AWS IoT fit together.&lt;/p&gt;

&lt;h1&gt;
  
  
  🧑‍💻 What’s Next? Experiment!
&lt;/h1&gt;

&lt;p&gt;This repository is meant as a starting point. Feel free to dive in and make it your own:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add more commands beyond the simple ping → pong demo&lt;/li&gt;
&lt;li&gt;Explore how the Wi-Fi connection is established in &lt;code&gt;startup.rs&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Take a closer look at our generalized MQTT client in &lt;code&gt;client.rs&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;It runs a separate thread to keep listening for messages&lt;/li&gt;
&lt;li&gt;Uses &lt;code&gt;crossbeam_channel&lt;/code&gt; to forward messages to the main thread safely&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Experiment with publishing structured JSON commands or even sensor data&lt;/li&gt;

&lt;/ul&gt;

&lt;h1&gt;
  
  
  🔧 Extending the Main Loop
&lt;/h1&gt;

&lt;p&gt;Inside &lt;code&gt;main.rs&lt;/code&gt;, you’ll find the main loop where incoming MQTT messages are matched against commands. Right now it only handles "ping". Here’s where you can extend it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="py"&gt;.message&lt;/span&gt;&lt;span class="nf"&gt;.as_str&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"ping"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Ping received, sending pong"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;JsonMessage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pong from: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="py"&gt;.config.mqtt_client_id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="s"&gt;"light_on"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Turning light ON"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;JsonMessage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Light is now ON"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&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="s"&gt;"light_off"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;info!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Turning light OFF"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;JsonMessage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Light is now OFF"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&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="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;warn!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unknown action: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="py"&gt;.message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;JsonMessage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unknown action: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="py"&gt;.message&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use this pattern to hook up any device action — toggling GPIO pins, controlling peripherals, or sending structured responses back to AWS IoT Core.&lt;/p&gt;

&lt;h1&gt;
  
  
  🙌 Thank You
&lt;/h1&gt;

&lt;p&gt;Thanks again for reading. Now go ahead — experiment, break things, fix them again, and build something awesome. That’s where the real learning happens.&lt;/p&gt;

&lt;p&gt;If you found this helpful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⭐ Star the &lt;a href="https://github.com/RamMaths/aws-iot-esp32-example" rel="noopener noreferrer"&gt;repository&lt;/a&gt; on GitHub to support the project&lt;/li&gt;
&lt;li&gt;💬 Share your feedback, questions, or improvements in the repo’s Discussions/Issues&lt;/li&gt;
&lt;li&gt;🚀 Show off what you’ve built with this template — I’d love to see your projects!&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rust</category>
      <category>aws</category>
      <category>iot</category>
      <category>terraform</category>
    </item>
  </channel>
</rss>
