# Luca Del Puppo — Complete Website Corpus (llms-full.txt) > Luca Del Puppo — AI Native Engineer at Nearform, Google Developer Expert in Web Technologies, GitKraken Ambassador, and Codemotion Ambassador. Writing and speaking on TypeScript, Node.js, Fastify, React, and AI. Based in Povegliano (TV), Italy (45.7617° N, 12.2036° E). ## 1. Profile & Community Roles - **Name**: Luca Del Puppo (@puppo92) - **Motto**: Run, Code & Learn - **Role**: AI Native Engineer @ Nearform (https://nearform.com/) - **Location**: Povegliano (TV), Italy (45.7617° N, 12.2036° E) - **Email**: luca@delpuppo.net - **GitHub**: https://github.com/Puppo - **LinkedIn**: https://www.linkedin.com/in/lucadelpuppo/ - **Hashnode**: https://blog.delpuppo.net/ - **Dev.to**: https://dev.to/puppo/ - **YouTube**: https://www.youtube.com/@Puppo_92 - **X / Twitter**: https://x.com/puppo92 ### Community Leadership - **Google Developer Expert** (Google · Web Technologies · Active): Recognized by Google for deep expertise in modern Web Technologies, JavaScript/TypeScript engines, and developer advocacy across international conferences. - **GitKraken Ambassador** (GitKraken · Git & Developer Experience · Active): Advocating for high-velocity Git workflows, GitLens automation, and team developer experience through technical deep-dives and demos. - **Codemotion Ambassador** (Codemotion · European Tech Community · Active): Supporting one of Europe's largest developer communities by mentoring speakers, curating technical tracks, and speaking at flagship events. - **Former Microsoft MVP** (Microsoft · Developer Technologies · Alumni): Awarded Microsoft Most Valuable Professional for community leadership, open-source contributions, and technical content creation. ### Engineering Stack - **Languages & Runtime**: TypeScript, JavaScript (ESNext), Node.js, Web Standards - **Backend & Architecture**: Fastify, PostgreSQL, Distributed Systems, REST & GraphQL - **Frontend & UI**: React, SolidJS, Astro, Performance & CWV - **AI & Tooling**: Google Antigravity, On-Device AI (Gemini Nano), Nx Monorepos, GitLens / GitKraken --- ## 2. Public Speaking Archive (74 Sessions) - [Sep 30, 2026] **Don't Trust Your Types: Type Testing with TSTyche** @ NodeConf EU (conference, Bologna, Italy) — [Event](https://nodeconf.eu/) | [Slides](https://talks.delpuppo.net/dont-trust-your-types/) | [Recording](https://www.youtube.com/watch?v=-7_DhDxqKog&list=PLHJJhUxJCR0c) - [Jun 6, 2026] **Costruiamo agenti con ADK-js** @ DevFest Vicenza (workshop, Vicenza, Italy) — [Event](https://devfestvicenza.it/) | [Slides](https://talks.delpuppo.net/costruiamo-agenti-con-adk-js) - [May 12, 2026] **Costruiamo agenti con ADK-js** @ GDG Venezia (workshop, Padua, Italy) — [Event](https://gdg.community.dev/events/details/google-gdg-venezia-presents-costruiamo-agenti-con-adk-js/) | [Slides](https://talks.delpuppo.net/costruiamo-agenti-con-adk-js/1) - [May 8, 2026] **AI in the Browser: Am I Still Waiting for It?** @ GDG on Campus Polytechnic University of Milan (meetup, Milan, Italy) — [Event](https://gdg.community.dev/events/details/google-gdg-on-campus-polytechnic-university-of-milan-presents-road-to-hack-ai-in-the-browser-talk/) | [Slides](https://talks.delpuppo.net/ai-in-the-browser-am-i-still-waiting-for-it) - [Apr 24, 2026] **AI in the Browser: Am I Still Waiting for It?** @ OSDay (conference, Florence, Italy) — [Event](https://osday.dev/schedule/f322ac17-8d0e-4a29-8126-35a76edd9c88) | [Slides](https://talks.delpuppo.net/ai-in-the-browser-am-i-still-waiting-for-it) - [Apr 18, 2026] **Build AI agents with ADK-js** @ DevFest Pisa (workshop, Pisa, Italy) — [Event](https://devfest.gdgpisa.it/talk/costruiamo-agenti-con-adk-js/) | [Slides](https://talks.delpuppo.net/costruiamo-agenti-con-adk-js/1) - [Apr 10, 2026] **AI Agents Made Simple** @ jsDay (conference, Bologna, Italy) — [Event](https://www.jsday.it/) | [Slides](https://talks.delpuppo.net/ai-agents-made-simple) | [Recording](https://www.youtube.com/watch?v=zyLgrOToQQY) - [Mar 31, 2026] **AI won't replace you (?). But your workflow will never be the same!** @ Codemotion Rome (conference, Rome, Italy) — [Event](https://conferences.codemotion.com/roma/agenda/#ticketrm26) - [Dec 1, 2025] **Chrome DevTools, for Super Fast SSR Applications!** @ React Advanced (conference) — [Event](https://reactadvanced.com/) | [Slides](https://talks.delpuppo.net/chromedevtools-for-super-fast-ssr-applications) | [Recording](https://gitnation.com/contents/chrome-devtools-for-super-fast-ssr-applications) - [Nov 29, 2025] **Vibe Coding - Let’s follow the flow!** @ GDG Bari (conference, Bari, Italy) — [Event](https://bari.devfest.it/sessions/vibe-coding-let%E2%80%99s-follow-the-flow/) | [Slides](https://talks.delpuppo.net/vibe-coding-let-s-follow-the-flow) - [Oct 28, 2025] **Vibe Coding - Let’s follow the flow!** @ GDG Treviso (meetup, Treviso, Italy) — [Event](https://gdg.community.dev/events/details/google-gdg-treviso-presents-vibe-coding-lets-follow-the-flow-luca-del-puppo/) | [Slides](https://talks.delpuppo.net/vibe-coding-let-s-follow-the-flow) - [Oct 25, 2025] **AI in the Browser: Am I Still Waiting for It?** @ GDG Roma Città (conference, Rome, Italy) — [Event](https://gdg.community.dev/events/details/google-gdg-roma-citta-presents-devfest-roma-2025/) | [Slides](https://talks.delpuppo.net/ai-in-the-browser-am-i-still-waiting-for-it) - [Oct 16, 2025] **React Compiler, how can you help me?** @ React Day Italy (conference, Verona, Italy) — [Event](https://www.reactjsday.it/talk/react-compiler-how-can-you-help-me/) | [Slides](https://talks.delpuppo.net/react-compiler-how-can-you-help-me) - [Oct 14, 2025] **ChromeDevTools—are you confident in your expertise?** @ Codemotion Milan (conference, Milan, Italy) — [Event](https://conferences.codemotion.com/milan2025/agenda/) | [Slides](https://talks.delpuppo.net/chromedevtools-are-you-confident-in-your-expertise) - [Oct 4, 2025] **Vibe Coding - Let’s follow the flow!** @ GDG Modena (conference, Modena, Italy) — [Event](https://devfest.modena.it/2025/sessions/vibe-coding--lets-follow-the-flow/) | [Slides](https://talks.delpuppo.net/vibe-coding-let-s-follow-the-flow) - [Oct 4, 2025] **Vibe Coding - Let’s follow the flow!** @ GDG Venezia (conference, Venezia, Italy) — [Event](https://devfest25.gdgvenezia.it/agenda/) | [Slides](https://talks.delpuppo.net/vibe-coding-let-s-follow-the-flow) | [Recording](https://www.youtube.com/watch?v=C5R_7742V7I) - [Sep 20, 2025] **ChromeDevTools—are you confident in your expertise?** @ GDG Campobasso (conference, Campobasso, Italy) — [Event](https://gdg.community.dev/events/details/google-gdg-campobasso-presents-devfest-campobasso-2025/) | [Slides](https://talks.delpuppo.net/chromedevtools-are-you-confident-in-your-expertise) - [Jun 13, 2025] **Luca Del Puppo - Copilot e Vibe Coding** @ DevDojo (live) — [Event](https://www.devdojo.it/) | [Recording](https://www.youtube.com/watch?v=laF0KNO_2Cg) - [Apr 17, 2025] **Bull-ish your queues with BullMQ** @ NodeCongress (conference) — [Event](https://nodecongress.com/) | [Slides](https://slides.com/puppo92/bull-ish-your-queues-with-bullmq/fullscreen) | [Recording](https://gitnation.com/contents/bull-ish-your-queues-with-bullmq) - [Apr 9, 2025] **Bull-ish your queues with BullMQ** @ Node.js Global Summit 25 (conference) — [Event](https://www.geekle.us/schedule/node25) | [Slides](https://slides.com/puppo92/bull-ish-your-queues-with-bullmq/fullscreen) - [Apr 7, 2025] **TypeScript Gymnastics - why are they so powerful for you?** @ Firenze.dev (conference, Bologna, Italy) — [Event](https://www.jsday.it/archive/2025-edition/) | [Slides](https://slides.com/puppo92/typescript-gymnastics-why-are-they-so-powerful-for-you/scroll?chrome=hidden) | [Recording](https://www.youtube.com/watch?v=gFFeeNjoYDQ) - [Mar 31, 2025] **Zod e TypeScript: la guida definitiva alla validazione dei dati** @ devmy (live) — [Event](https://devmy.it/) | [Recording](https://www.youtube.com/watch?v=-XqHfJlokIk) - [Feb 20, 2025] **TypeScript Gymnastics - why are they so powerful for you?** @ Firenze.dev (meetup, Firenze, Italy) — [Event](https://firenze.dev/event/typescript-gymnastics-why-are-they-so-powerful-for-you) | [Slides](https://slides.com/puppo92/typescript-gymnastics-why-are-they-so-powerful-for-you/scroll?chrome=hidden) - [Jan 28, 2025] **Scale your Frontend application with Module Federation** @ GDG Torino 2025 (meetup, Torino, Italy) — [Event](https://gdg.community.dev/events/details/google-gdg-torino-presents-web-talks-il-futuro-del-web-con-webxr-e-module-federation/) | [Slides](https://slides.com/puppo92/scale-your-frontend-application-with-module-federation/scroll?chrome=hidden) - [Nov 30, 2024] **Scale your project with NX** @ DevFest Basilicata 2024 (conference, Matera, Italy) — [Event](https://devfest.gdgbasilicata.it/) | [Slides](https://slides.com/puppo92/scale-your-project-with-nx/scroll?chrome=hidden) - [Nov 16, 2024] **Dive into advanced TypeScript** @ DevFest Venezia 2024 (conference, Venice, Italy) — [Event](https://devfest24.gdgvenezia.it/) | [Slides](https://slides.com/puppo92/dive-into-advanced-typescript/scroll?chrome=hidden) | [Recording](https://www.youtube.com/watch?v=PZPZ9n2q1ho) - [Oct 26, 2024] **Scale your Frontend application with Module Federation** @ DevFest Trento 2024 (conference, Trento, Italy) — [Event](https://gdg.community.dev/events/details/google-gdg-trento-presents-devfest-trento-2024/) | [Slides](https://slides.com/puppo92/scale-your-frontend-application-with-module-federation/scroll?chrome=hidden) - [Oct 23, 2024] **Why you should know JavaScript before rewriting your application in Rust?** @ Codemotion 2024 (conference, Milan, Italy) — [Event](https://conferences.codemotion.com/milan2024/agenda/) | [Slides](https://slides.com/puppo92/why-you-should-know-javascript-before-rewriting-your-application-in-rust/fullscreen) | [Recording](https://www.youtube.com/watch?v=jfL0O0uJEZ8) - [Oct 12, 2024] **Scale your Frontend application with Module Federation** @ DevFest Modena 2024 (conference, Modena, Italy) — [Event](https://devfest.modena.it/) | [Slides](https://slides.com/puppo92/scale-your-frontend-application-with-module-federation/scroll?chrome=hidden) - [Sep 28, 2024] **Scale your Frontend application with Module Federation** @ Come to Code (conference, Pignola, Italy) — [Event](https://www.cometocode.it/) | [Slides](https://slides.com/puppo92/scale-your-frontend-application-with-module-federation/scroll?chrome=hidden) - [Jun 28, 2024] **TypeScript - strategie avanzate per trasformare JS da tipaccio a tipizzato!** @ Coderful (conference, Catania, Italy) — [Event](https://jsnation.com/) | [Slides](https://docs.google.com/presentation/d/1XYozT4qZbFi7fmvef8JsyP61y-O7N1gxAqtE2ShzxJo/edit?usp=sharing) - [Jun 13, 2024] **Dive Into Advanced TypeScript** @ JSNation (conference, Amsterdam, Netherlands) — [Event](https://jsnation.com/) | [Slides](https://slides.com/puppo92/dive-into-advanced-typescript/edit) | [Recording](https://portal.gitnation.org/contents/dive-into-advanced-typescript) - [May 28, 2024] **Not My Type - con Luca Del Puppo** @ Continuous Delivery (podcast, Milan, Italy) — [Event](https://landing.sparkfabrik.com/continuous-delivery-newsletter) | [Recording](https://www.youtube.com/watch?v=-HeDKpzfiL8&t=1469s) - [May 24, 2024] **Angular 18 🔥 | Interview with Google Developer Expert Luca Del Puppo** @ Angular Love (interview) — [Event](https://angular.love/en/) | [Recording](https://www.youtube.com/watch?v=zwotukZCd9g) - [May 21, 2024] **Durable and modular Frontend application with Nx** @ Angular Spring Camp (conference, Warsaw, Poland) — [Event](https://angular.love/en/) | [Slides](https://slides.com/puppo92/durable-and-modular-frontend-application-with-nx/scroll?chrome=hidden) | [Recording](https://www.youtube.com/watch?v=zi2dIIbuaso&t=4060s) - [May 18, 2024] **Scale your Frontend application with Module Federation** @ Xe One Day 2024 (conference, Venice, Italy) — [Event](https://www.xedotnet.org/eventi/one-day-rethink-application/) | [Slides](https://slides.com/puppo92/scale-your-frontend-application-with-module-federation/scroll?chrome=hidden) - [Apr 11, 2024] **Exploring the Enigmatic - Navigating JavaScript's Uncharted Realms** @ JsDay 2024 (conference, Verona, Italy) — [Event](https://2024.jsday.it/) | [Slides](https://slides.com/puppo92/exploring-the-enigmatic-navigating-javascripts-uncharted-realms/scroll?chrome=hidden) - [Apr 5, 2024] **CSS Layout ma come siete cambiati?** @ CssDay 2024 (conference, Faenza, Italy) — [Event](https://2024.cssday.it/) | [Slides](https://slides.com/puppo92/css-layout-ma-come-siete-cambiati/scroll?chrome=hidden) - [Apr 4, 2024] **Breaking REST Chains - A Fastify & Mercurius Pathway to GraphQL Glory** @ Node Congress 2024 (conference) — [Event](https://nodecongress.com/) | [Slides](https://slides.com/puppo92/breaking-rest-chains-a-fastify-mercurius-pathway-to-graphql-glory/scroll?chrome=hidden) | [Recording](https://portal.gitnation.org/contents/breaking-rest-chains-a-fastify-and-mercurius-pathway-to-graphql-glory) - [Feb 23, 2024] **GitHub Copilot for lazy developers** @ Jakala (meetup, Roncade, Italy) - [Feb 22, 2024] **Breaking REST Chains - A Fastify & Mercurius Pathway to GraphQL Glory** @ Commit University (meetup, Florence, Italy) — [Event](https://www.commitsoftware.it/en/commituniversity/) | [Slides](https://slides.com/puppo92/breaking-rest-chains-a-fastify-mercurius-pathway-to-graphql-glory/scroll?chrome=hidden) | [Recording](https://www.youtube.com/watch?v=nlYudhteZlE) - [Feb 6, 2024] **Breaking REST Chains - A Fastify & Mercurius Pathway to GraphQL Glory** @ Global Summit for Node.js '24 (conference) — [Event](https://events.geekle.us/nodejs24/) | [Slides](https://slides.com/puppo92/breaking-rest-chains-a-fastify-mercurius-pathway-to-graphql-glory/scroll?chrome=hidden) - [Jan 15, 2024] **Advent of Code - The Last Episode** @ Codemotion Advent of Code (live, Italy) — [Event](https://www.linkedin.com/company/codemotion/posts/?feedView=videos) | [Recording](https://www.linkedin.com/company/codemotion/posts/?feedView=videos) - [Dec 18, 2023] **Advent of Code - The Third Episode** @ Codemotion Advent of Code (live, Italy) — [Event](https://www.linkedin.com/events/7142422854842720258/comments/) | [Recording](https://www.linkedin.com/events/7142422854842720258/comments/) - [Dec 2, 2023] **Write once, test everywhere with Playwright** @ DevFest Bari (conference, Bari, Italy) — [Event](https://bari.devfest.it/sessions/write-once-test-everywhere-with-playwright/) | [Slides](https://slides.com/puppo92/write-one-time-test-everywhere-with-playwright/fullscreen) - [Nov 19, 2023] **Exploring the Enigmatic - Navigating JavaScript's Uncharted Realms** @ DevFest Pescara (conference, Pescara, Italy) — [Event](https://devfest.gdgpescara.it/agenda/2) | [Slides](https://slides.com/puppo92/exploring-the-enigmatic-navigating-javascripts-uncharted-realms/fullscreen) - [Nov 11, 2023] **JavaScript's hidden gems - unveiling the magic of Iterators and Generators** @ DevFest Trento (conference, Trento, Italy) — [Event](https://gdg.community.dev/events/details/google-gdg-trento-presents-devfest-trento-2023/) | [Slides](https://slides.com/puppo92/javascripts-hidden-gems-unveiling-the-magic-of-iterators-and-generators/fullscreen) - [Oct 27, 2023] **Typescript con Luca Del Puppo (Nearform)** @ Gitbar podcast (podcast) — [Event](https://www.youtube.com/watch?v=KDlhLFF780E) | [Recording](https://www.youtube.com/watch?v=KDlhLFF780E) - [Oct 26, 2023] **Dive into TypeScript** @ Schrödinger Hat (meetup, Verona, Italy) — [Event](https://www.eventbrite.it/e/biglietti-schrodinger-session-verona-2-733392726387?aff=ebdsoporgprofile) | [Slides](https://slides.com/puppo92/dive-into-typescript/fullscreen) | [Recording](https://www.youtube.com/watch?v=KCRAM3AGWsk) - [Oct 10, 2023] **Dev VS Dev - "We will not ship shit"** @ Codemotion (webinar, Italy) — [Event](https://www.linkedin.com/events/devvsdev-wewillnotshipshit7117060681132036097/comments/) | [Recording](https://www.linkedin.com/events/devvsdev-wewillnotshipshit7117060681132036097/comments/) - [Oct 9, 2023] **EP 3.4.0 CRESCERE COME DEV - CAFFESVILUPPO con Luca Del Puppo** @ CAFFESVILUPPO (podcast, Italy) — [Event](https://youtu.be/mAXNHmcLtJ8?si=7v4M9i9fh324QU0l) | [Recording](https://youtu.be/mAXNHmcLtJ8?si=7v4M9i9fh324QU0l) - [Jul 27, 2023] **Write one time, test everywhere with Playwright** @ We Are Developers (conference, Berlin, Germany) — [Event](https://www.wearedevelopers.com/) | [Slides](https://slides.com/puppo92/write-one-time-test-everywhere-with-playwright/fullscreen) - [Jun 17, 2023] **Dive into Typescript** @ We Make Future (meetup, Milan, Italy) — [Event](https://www.wemakefuture.it/next/relatori/#) | [Slides](https://docs.google.com/presentation/d/1YNnBN3pYEoJ8HLr5itDMG7e13t-F368h/edit?usp=sharing&ouid=107460965147414222407&rtpof=true&sd=true) - [Jun 15, 2023] **Zod === Typescript, but at runtime in your react applications** @ ReactJS Milano (meetup, Milan, Italy) — [Event](https://www.meetup.com/react-js-milano/events/293786119) | [Slides](https://slides.com/puppo92/zod-typescript-but-at-runtime-in-your-react-applications/fullscreen) | [Recording](https://www.youtube.com/watch?v=5CjqU9w_4iA) - [Jun 6, 2023] **Zod === Typescript, but at Runtime in Your React Applications** @ React Summit 2023 (conference, Amsterdam, Netherlands) — [Event](https://portal.gitnation.org/contents/zod-typescript-but-at-runtime-in-your-react-applications) | [Slides](https://slides.com/puppo92/zod-typescript-but-at-runtime-in-your-react-applications/fullscreen) | [Recording](https://portal.gitnation.org/contents/zod-typescript-but-at-runtime-in-your-react-applications) - [Apr 20, 2023] **Prisma the ORM that Node was waiting for** @ Commit University (conference, Florence, Italy) — [Event](https://www.youtube.com/watch?v=vqYpNTG-C84) | [Slides](https://slides.com/puppo92/prisma-the-orm-that-node-was-waiting-for/fullscreen) | [Recording](https://www.youtube.com/watch?v=vqYpNTG-C84) - [Apr 15, 2023] **GraphQL with Mercurius and Prisma, love at first sight** @ NodeCongress 2023 (conference, Berlin, Germany) — [Event](https://portal.gitnation.org/contents/graphql-with-mercurius-and-prisma-love-at-first-sight) | [Slides](https://slides.com/puppo92/graphgl-with-mercurius-and-prisma-love-at-first-sight/fullscreen) | [Recording](https://portal.gitnation.org/contents/graphql-with-mercurius-and-prisma-love-at-first-sight) - [Mar 31, 2023] **Design System for Software Development Team** @ CssDay 2023 (conference, Faenza, Italy) — [Event](https://2023.cssday.it/talks_speakers/) | [Slides](https://slides.com/puppo92/design-system-for-software-development-team/fullscreen) - [Mar 24, 2023] **Validate HTTP request with HttpClient & Zod** @ Ng-Be 2023 (conference, Brussels, Belgium) — [Event](https://ng-be.org/talks/2023/3/24/luca-del-puppo) | [Slides](https://slides.com/puppo92/validate-http-request-with-httpclient-zod/fullscreen) | [Recording](https://www.youtube.com/watch?v=B2Hdehjq4vU) - [Feb 23, 2023] **Write one time, test everywhere with Playwright** @ Marca User Group (meetup, Treviso, Italy) — [Event](https://m-u-g.github.io/eventi/2023/02/23/write-one-time-test-everywhere-with-playwright.html) | [Slides](https://slides.com/puppo92/write-one-time-test-everywhere-with-playwright/fullscreen) - [Feb 7, 2023] **Keep alive your typescript definitions using Zod** @ JSWorld Conference 2023 (conference, Amsterdam, Netherlands) — [Event](https://jsworldconference.com/program) | [Slides](https://speakerdeck.com/puppo/keep-alive-your-typescript-definitions-using-zod) | [Recording](https://www.youtube.com/watch?v=19I1VEOor2A) - [Dec 2, 2022] **Aware dependencies using Nx Graph** @ NgRome (conference, Rome, Italy) — [Event](https://ngrome.io/agenda) | [Slides](https://speakerdeck.com/puppo/aware-dependencies-using-nx-graph) | [Recording](https://www.youtube.com/watch?v=h_Faplx37yk) - [Nov 19, 2022] **Prisma the ORM that node was waiting for** @ GDG Pescara (conference, Pescara, Italy) — [Event](https://devfest.gdgpescara.it/sessions/304) | [Slides](https://speakerdeck.com/puppo/prisma-the-orm-that-node-was-waiting-for) - [Nov 17, 2022] **How to scale up your projects using NX like a pro** @ ReactJS Milano (meetup, Milan, Italy) — [Event](https://www.meetup.com/react-js-milano/events/289471598) | [Slides](https://speakerdeck.com/puppo/how-to-scale-up-your-projects-like-a-pro) | [Recording](https://www.youtube.com/watch?v=NosVg3ASgMo) - [Oct 25, 2022] **Alive, tipi sopravvissuti** @ Milano Frontend (meetup, Milano, Italy) — [Event](https://www.meetup.com/it-IT/milano-front-end/events/289043876/) | [Slides](https://speakerdeck.com/puppo/alive-types) | [Recording](https://www.twitch.tv/videos/1634157989) - [Oct 20, 2022] **Integrare Unit e E2E test in applicazioni React 18** @ ReactDay (workshop, Verona, Italy) — [Event](https://2022.reactjsday.it/workshop/workshop_Unit-Test.html) | [Slides](https://speakerdeck.com/puppo/reactday-2022-unit-testing) - [Oct 7, 2022] **How to scale up your angular projects like a pro** @ AngularDay (conference, Verona, Italy) — [Event](https://2022.angularday.it/talks_speakers/) | [Slides](https://speakerdeck.com/puppo/how-to-scale-up-your-angular-projects-like-a-pro) - [Jun 27, 2022] **Prisma, l’ORM del futuro** @ Codemotion (workshop) — [Event](https://talks.codemotion.com/paths/prisma-lorm-del-futuro) - [Apr 29, 2022] **Prisma is in the Air** @ XeDotNet (meetup, Italy) — [Event](https://www.xedotnet.org/eventi/prisma-is-in-the-air/) | [Slides](https://speakerdeck.com/puppo/prisma-in-the-air) - [Mar 10, 2022] **Can typescript definitions survive even at runtime?** @ MUG (meetup, Treviso, Italy) — [Event](https://m-u-g.github.io/eventi/2022/03/10/can-typescript-definitions-survive-even-at-runtime.html) | [Slides](https://speakerdeck.com/puppo/can-typescript-definitions-survive-even-at-runtime) | [Recording](https://www.youtube.com/watch?v=6Aajmo9E-SM) - [Feb 16, 2022] **Prisma: un po' di colore nel nero mondo degli ORM** @ Devmy (webinar) — [Event](https://devmy.it/) | [Recording](https://www.youtube.com/watch?v=MTMcWrDbPA8) - [Oct 19, 2021] **Is it possible to build your UI components using only web components?** @ FEVR (meetup, Verona, Italy) — [Event](https://www.fevr.it/eventi/2021/10/is-it-possible-to-build-your-ui-components-using-only-web-components/) | [Slides](https://speakerdeck.com/puppo/is-it-possible-to-build-your-ui-components-using-only-web-components) | [Recording](https://www.youtube.com/watch?v=RBWJZWC86Bg) - [Oct 7, 2021] **Stay Reactive Stay Quiet with RxJS** @ MUG (meetup, Treviso, Italy) — [Event](https://m-u-g.github.io/eventi/2021/10/07/stay-reactive-stay-quiet-with-rxjs.html) | [Slides](https://speakerdeck.com/puppo/stay-reactive-stay-quiet-with-rxjs) | [Recording](https://m-u-g.github.io/eventi/2021/10/07/stay-reactive-stay-quiet-with-rxjs.html) - [Apr 21, 2021] **NX: il ritorno!** @ Devmy (webinar) — [Event](https://devmy.it/) | [Recording](https://www.youtube.com/watch?v=hS_2Okd-CkM) --- ## 3. Visited Countries (13 Countries) - 🇮🇹 **Italy** (IT): Home base in Povegliano (TV) · Dolomites trail running & 40+ talks - 🇳🇱 **Netherlands** (NL): Conference speaking & Amsterdam tech community - 🇧🇪 **Belgium** (BE): International conferences & meetups - 🇩🇪 **Germany** (DE): European engineering summits & workshops - 🇵🇱 **Poland** (PL): Conference speaker & developer community - 🇪🇸 **Spain** (ES): Iberian travels & mountain trails - 🇵🇹 **Portugal** (PT): Atlantic coast & tech events - 🇫🇷 **France** (FR): Alpine crossings & European exploration - 🇬🇧 **England** (GB): London tech ecosystem & travel - 🇦🇹 **Austria** (AT): Eastern Alps & mountain trails - 🇸🇮 **Slovenia** (SI): Julian Alps hiking & trail running - 🇬🇷 **Greece** (GR): Mediterranean islands & history - 🇨🇻 **Cape Verde** (CV): Volcanic island trails in the Atlantic --- ## 4. Complete Blog Articles (110 Articles) ### Automating my Codemotion Badge with Antigravity - **Canonical URL**: https://delpuppo.net/blog/automating-my-codemotion-badge-with-antigravity/ - **Article LLMs.txt**: https://delpuppo.net/blog/automating-my-codemotion-badge-with-antigravity/llms.txt - **Published**: Jul 8, 2026 - **Tags**: agenticarchitect, googleantigravity - **Summary**: Today, I wanna show you how to set up your devices with Antigravity using only the agentic mode,... Today, I wanna show you how to set up your devices with Antigravity using only the agentic mode, without touching any code. At the last CodeMotion Rome, I got my special badge. A [CM Node](https://www.cmnode.it/en/), a special hardware board, that you can customise for your conferences. ![Illustration for Automating my Codemotion Badge with Antigravity](https://cdn.hashnode.com/uploads/covers/6262bb170f819f588af747fb/ef4e944a-9845-4010-8250-11cc7a6babab.png) I'm not a super nerd for this kind of stuff, so I decided to use Antigravity and all its capabilities to build and flash the best board ever for my future conferences. ## Small intro about the board This board has been delivered with a [GitHub repository](https://github.com/Codemotion-Official/CMNode) to understand how to flash it and how it works. In Rome, I saw different attendees with awesome badges. For instance, Soumaya had built a badge with a small plastic hand that used a sensor when someone was near her; it started waving to say hi. Since that day, I have been thinking about what I can build to have a cool badge. Nothing has come to my mind, so today, with this blog, we will build my badge together, and thanks to Antigravity, we will create the best version possible. ## Antigravity If you don't know [Antigravity](https://antigravity.google/). Antigravity is a set of tools built by Google. We have: - [Antigravity 2.0](https://antigravity.google/product/antigravity-2): is a dedicated platform to work with agents. Orchestrate multiple autonomous agents working in parallel across independent projects. - [Antigravity CLI](https://antigravity.google/product/antigravity-cli): terminal-first surface to interact with Antigravity agents. Stay in your flow without context switching - [Antigravity IDE](https://antigravity.google/product/antigravity-ide): editor view offers tab autocompletion, natural language code commands, and a configurable, and context-aware configurable agent - [Antigravity SDK](https://antigravity.google/product/antigravity-sdk): build AI agents that autonomously read files, run commands, edit code, and more. The Agent SDK gives you the same tools, agent loop, and context management that power Google Antigravity, programmable in Python. Today, in this blog post, we will use Antigravity 2.0 to set up the board. You can use this tool to orchestrate many projects from a unique UI. This tool is fully agentic; using natural language, you can spawn agents, plan, and work on your projects. If you want to check the codebase in detail, you must use the Antigravity IDE. I’m not a fan of this decision, I’d prefer one tool for both, but this is what we have, and we must use it as it is. ### Let’s start cooking Okay, after this introduction, let’s see what we can do with Antigravity and the CodeMotion Board. So, I’m dumb about this board right now, and I want to use Antigravity to figure out what I can do with it and get an idea of what my badge might look like. I open the Antigravity app, and from the UI, I start a new project, and in the prompt, I type > I want to set up my Codemotion Badge. > It's a CMNode v0 [https://www.cmnode.it/en/](https://www.cmnode.it/en/), and it can be programmed. > I want to create two different screens: the first a classic one with name, surname, heading, and company, and a QR code linking to my website. > The second one must be fantastic, and I want different options to choose from you. > > I have this repository as an example to set up the badge [https://github.com/Codemotion-Official/CMNode](https://github.com/Codemotion-Official/CMNode); you can use it as an example to create my badge. > Lastly, I don't want to flash the ROM every time, so I'd like a small web page I can access via Wi-Fi to update the information. > > Can you plan the work to do it? > Include also two or three possible ideas for the second screen. It must be cool and fancy! > > The badge is already connected to the laptop in case you want to deploy the result. Now, I press enter and let’s see the result and what it will cook. The result > # **CMNode v0 WiFi Configuration & Dual Screen Setup** > > The goal of this task is to upgrade your CMNode v0 badge by replacing the serial-based configuration with a Wi-Fi-based web interface. We will also save the configured data to flash memory so it persists across reboots, and implement two different display layouts: a "Classic" screen and a "Fantastic" screen. > > ## **User Review Required** > > **IMPORTANT** > > Please review the proposed ideas for the "Fantastic" screen below. Let me know which one you prefer, or if you'd like to combine elements from different ideas! > > ### **Ideas for the "Fantastic" Second Screen:** > > 1. **The "Hacker Terminal"** : A retro command-line interface aesthetic. The screen features a black background with white text, simulating a terminal session (e.g., `> user@cmnode:~$ whoami`, followed by your details). We can use monospace fonts and add some faux hex-dumps or ASCII elements. > > 2. **The "RPG Stats / Gamer Card"** : Your badge becomes a character stat sheet. It will display you as a "Level 99 Developer" with stats like "INT: 99", "COFFEE: 100%", an energy bar, and your details integrated into a game-like UI. > > 3. **The "Minimalist Cyberpunk"** : A bold, modern, and high-contrast design. It uses large vertical typography for your name, heavy geometric shapes, thick red accent lines, and a large barcode aesthetic alongside your QR code. > > **Let me know which of these three ideas (or a mix!) you want me to build for the second screen.** > > ## **Proposed Changes** > > We will modify the firmware to support a captive-like Wi-Fi Access Point and Web Server. > > ### **Core Logic Updates** > > #### **[MODIFY]** `src/main.cpp` **(or** `NodeV0.ino`**)** > > - **Wi-Fi Access Point** : The ESP32-C3 will broadcast an SSID (e.g., `CMNode-Config`). You can connect to it with your phone or laptop. > > - **Web Server** : A built-in web server will serve a simple, mobile-friendly HTML configuration page at [`http://192.168.4.1`](http://192.168.4.1). > > - **Form Submission** : The webpage will contain a form to update your Name, Surname, Role, Company, QR Link, and a dropdown to select the active screen layout (Classic vs Fantastic). > > - **Persistent Storage** : Integrate the `Preferences` library to save your data to the ESP32's non-volatile storage (NVS). This ensures your badge remembers your details even after turning off. > > - **Display Pipeline** : Refactor the `drawContent()` function to support rendering two completely different layouts based on your selection. > > ## **Verification Plan** > > ### **Manual Verification** > > 1. I will write the updated code and you will flash it to your badge via PlatformIO or Arduino IDE. > > 2. We will verify that the badge broadcasts the `CMNode-Config` Wi-Fi network. > > 3. We will connect to it, open the web page, submit new data, and verify that the E-paper display updates immediately. > > 4. We will reboot the badge to confirm the data is preserved. > > 5. We will toggle between the Classic and Fantastic screens to verify the layouts. I commented on the plan, deciding to go with option one and asked for an example design. Antigravity has started reviewing the plan and updated it in this way > # **CMNode v0 WiFi Configuration & Dual Screen Setup** > > The goal of this task is to upgrade your CMNode v0 badge by replacing the serial-based configuration with a Wi-Fi-based web interface. We will also save the configured data to flash memory so it persists across reboots, and implement two different display layouts: a "Classic" screen and a "Fantastic" screen. > > ## **Design Choice: "Hacker Terminal"** > > You've selected the **Hacker Terminal** idea for the second screen. This layout will simulate a retro command-line interface on the E-ink display: > > - **Background** : Black (inverted). > > - **Text** : Monospace white font, simulating terminal output (`root@cmnode:~$ whoami`). > > - **Data** : Your name, surname, role, and company will be displayed as the "output" of the terminal command. > > - **Accents** : Faux hex-dumps or system logs on the side, and a right-aligned QR code. > > Here is a mockup of the design: > > ![Hacker Terminal Mockup](https://cdn.hashnode.com/uploads/covers/6262bb170f819f588af747fb/ef34f58b-fea4-4891-be97-aed61105a043.png) > > Does this mockup and the updated plan look good to you? If so, I will proceed with the implementation! > > ## **Proposed Changes** > > We will modify the firmware to support a captive-like Wi-Fi Access Point and Web Server. > > ### **Core Logic Updates** > > #### **[MODIFY]** `src/main.cpp` **(or** `NodeV0.ino`**)** > > - **Wi-Fi Access Point** : The ESP32-C3 will broadcast an SSID (e.g., `CMNode-Config`). You can connect to it with your phone or laptop. > > - **Web Server** : A built-in web server will serve a simple, mobile-friendly HTML configuration page at [`http://192.168.4.1`](http://192.168.4.1). > > - **Form Submission** : The webpage will contain a form to update your Name, Surname, Role, Company, QR Link, and a dropdown to select the active screen layout (Classic vs Fantastic). > > - **Persistent Storage** : Integrate the `Preferences` library to save your data to the ESP32's non-volatile storage (NVS). This ensures your badge remembers your details even after turning off. > > - **Display Pipeline** : Refactor the `drawContent()` function to support rendering two completely different layouts based on your selection. > > ## **Verification Plan** > > ### **Manual Verification** > > 1. I will write the updated code and you will flash it to your badge via PlatformIO or Arduino IDE. > > 2. We will verify that the badge broadcasts the `CMNode-Config` Wi-Fi network. > > 3. We will connect to it, open the web page, submit new data, and verify that the E-paper display updates immediately. > > 4. We will reboot the badge to confirm the data is preserved. > > 5. We will toggle between the Classic and Fantastic screens to verify the layouts. As you can see, we now have a clear idea of how the second screen should look and a plan to implement everything, but before spawning the agent work, I'd also like to have a design of the first screen of the badge, so using the chat, I ask to include it also in the plan. After two iterations of the prompt, this is the result ![Illustration for Automating my Codemotion Badge with Antigravity](https://cdn.hashnode.com/uploads/covers/6262bb170f819f588af747fb/54c7c4db-d0b6-4eb4-a280-1142bb66e63e.png) ### The implementation To implement the plan, we just need to press the Proceed button and wait ![Illustration for Automating my Codemotion Badge with Antigravity](https://cdn.hashnode.com/uploads/covers/6262bb170f819f588af747fb/7019cabe-c09b-4157-a55a-7a7ed5556c14.png) During the implementation, we can check what is happening through the UI ![Illustration for Automating my Codemotion Badge with Antigravity](https://cdn.hashnode.com/uploads/covers/6262bb170f819f588af747fb/498b5b50-cac5-4265-987f-c034e8e63631.png) And if we notice something weird, we can stop the agent and type a new prompt to drive it again on the right path. If you, like me, prefer to get confirmation before running commands or editing files, you must also approve these actions through the UI. After 2 minutes, this is the result. ![Illustration for Automating my Codemotion Badge with Antigravity](https://cdn.hashnode.com/uploads/covers/6262bb170f819f588af747fb/a45ebeaf-4e97-4e88-bd2c-cf70b5fa4d8b.png) And if I connect my laptop to the badge Wi-Fi, I can configure my badge. ![Illustration for Automating my Codemotion Badge with Antigravity](https://cdn.hashnode.com/uploads/covers/6262bb170f819f588af747fb/63efae28-30d8-4913-88aa-96724d39712a.png) ### The refine Now we have a badge working, but we want to improve it a bit: first, I want to change the screen by pressing the button on the back without connecting to the badge's Wi-Fi, and I want to set the Wi-Fi password via Environment Variables. So I can prompt this > Can we also change the screen view using the button on the rear of the badge without connecting to the badge's Wi-Fi? And after a minute, the result is ready, and my badge has been flashed. And lastly, I can ask not to hard-code the password but use an environment variable, > use an environment variable for the wifi password same here, after one minute and a couple of approvals, the result is again ready and in my badge. To complete, I want to change the layout by using one of the buttons on the back of the badge. So again I ask Antigravity to do it for me. > Can we change the screen view also by using the button in the rear of the badge without connecting to the badge wifi? and same here, after some minutes, sometimes the result is happier in the badge. And the final result is this one ![Illustration for Automating my Codemotion Badge with Antigravity](https://cdn.hashnode.com/uploads/covers/6262bb170f819f588af747fb/5691371e-4de6-432a-a7b5-35ff5b35ffd4.jpg) and this one ![Illustration for Automating my Codemotion Badge with Antigravity](https://cdn.hashnode.com/uploads/covers/6262bb170f819f588af747fb/d2f79f9b-69eb-4ee9-8237-a31c3dbdb42a.jpg) ### Conclusion It's time to wrap up! So my experience with Antigravity has been great! I love to use it to build new things, or as a teammate to learn new technology, or just to prototype a pet project like this one. If you are in the agentic development, this tool can be a good one for you! The experience is great, and if you want to have control over different projects or streams, it can be really helpful. The only pain point for me is the fact that if you want to check the code, you must install another tool, the Antigravity IDE, to check it. In this era of agent development, what I find great is the ability to plan and review the changes before the agent implements them! This is the game-changer for me in this moment, and then a lot and a lot of code review! Okay, that's it! I hope you enjoy this introduction to Antigravity and that you now want to test it for your project. I hope you also like my new super badge 🚀 _p.s. to check the code result, you can navigate to this_ [_repository_](https://github.com/Puppo/codemotion-badge) _Google Cloud credits are provided for this project._ --- ### Take control of your job with GitLens Launchpad - **Canonical URL**: https://delpuppo.net/blog/take-control-of-your-job-with-gitlens-launchpad/ - **Article LLMs.txt**: https://delpuppo.net/blog/take-control-of-your-job-with-gitlens-launchpad/llms.txt - **Published**: Dec 23, 2025 - **Tags**: gitkraken, tooling, Developer Tools, DeveloperExperience - **Summary**: As a developer, you have to take control of your projects every day. Whether it is a company... As a developer, you have to take control of your projects every day. Whether it is a company repository, an open source project you maintain or collaborate on, or a simple pet project. Gaining control over your projects often depends on the platform you're working with. While there are many options available, such as [Atlassian](http://atlassian.com/) and [GitLab](https://gitlab.com), today I will focus on using [GitHub](https://github.com/) as the platform of choice. Let me guide you through how you can effectively manage your projects on [GitHub](https://github.com/). I’ll talk about the [GitHub](https://github.com/) one because that is where I’m working on. For me, checking the status of my PRs is essential in my daily work. On average, I spend about one or two hours a day tracking down failing CI checks and keeping my PRs in sync with the main branch, or reviewing other PRs. To do that, I used to use the [GitHub](https://github.com/) website on [my PRs page](https://github.com/pulls). This page is good; you can find all your PRs, including those where your teammates are requesting a review. You can check whether your PR has reviews and whether the CI passes. ![github.com pull requests](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4eb207mh7zc4qc4fxonk.png) Here you can see my PRs at this specific moment. As you can see, you can immediately receive information about the PRs, the number of comments, and more. I used this page for a while because it is a perfect way to check all the work across many repositories, but it has a small problem: to check the page, I have to switch between [VSCode](https://code.visualstudio.com/) and the browser during my job. This constant switching might seem trivial, but research suggests it takes an average of 23 seconds to return to a productive state after such a context switch. In the fast-paced developer environment, these seconds add up, potentially impacting overall efficiency and output. So, I decided to search for another solution that would let me have the same information across the projects without leaving my [VSCode](https://code.visualstudio.com/) instance. ## The first useful extensions to the migration [GitHub Pull Requests](https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-pull-request-github) was the first extension I started to test. This extension enables checking all PRs in the current repository I’m working on. I can see my open PRs and my teammates' open PRs. From a [VSCode](https://code.visualstudio.com/) panel, I can check the status of a PR or review another one. This extension enables us to open a PR directly from [VSCode](https://code.visualstudio.com/) without using the [GitHub](https://github.com/) website, reducing the need to switch context between the editor and the [GitHub](https://github.com/) website. Here is a picture of the extension ![GitHub Pull Requests Extension](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5ckvxthwheha79ip123j.png) As you can see, in the left panel, we can control PRs and Issues. By clicking the PR, we can view its home page. By clicking the arrow icon that appears when you hover over the PR, you can go to the Review mode and add comments or suggestions to that PR. That extension has improved my DX experience, especially in PR creation and review, but, as you can see, it works only on the open repository, which is a limitation, since I cannot see if in other projects something or someone is blocked by my work, or it’s waiting for a review or something else. ## The Final Solution While browsing the web, I came across a feature of [GitKraken](https://www.gitkraken.com/) called [Launchpad](https://www.gitkraken.com/features/launchpad). This feature enables us to get a big-picture view of all issues and PRs where we are the creator or a follower. If you don’t know [GitKraken](https://www.gitkraken.com/), it is a Git client with an awesome UI for managing your repositories. You can use it as a [desktop app](https://www.gitkraken.com/git-client?_gl=1*1yb8ynx*_up*MQ..*_ga*MTM4MjU2MTEwMS4xNzY2MjM2Mjc3*_ga_TTGBQHQ5E4*czE3NjYyMzYyNzYkbzEkZzAkdDE3NjYyMzYyNzYkajYwJGwxJGgxNjcyOTAyNTQ3), [website](https://gitkraken.dev/), editor extension ([GitLens](https://www.gitkraken.com/gitlens?_gl=1*w5a9a6*_up*MQ..*_ga*MTEyMDYwODM3LjE3NjYyMzYzMTQ.*_ga_TTGBQHQ5E4*czE3NjYyMzYzMTMkbzEkZzAkdDE3NjYyMzYzMTMkajYwJGwxJGgyMDQ0NjUzMjY0)), or the [CLI](https://www.gitkraken.com/cli?_gl=1*1ltt9q5*_up*MQ..*_ga*MTEyMDYwODM3LjE3NjYyMzYzMTQ.*_ga_TTGBQHQ5E4*czE3NjYyMzYzMTMkbzEkZzEkdDE3NjYyMzY2ODgkajYwJGwxJGgyMDQ0NjUzMjY0). They have also released an [MCP server](https://www.gitkraken.com/mcp?_gl=1*1p7bqfc*_up*MQ..*_ga*MTEyMDYwODM3LjE3NjYyMzYzMTQ.*_ga_TTGBQHQ5E4*czE3NjYyMzYzMTMkbzEkZzEkdDE3NjYyMzY3MjAkajI4JGwxJGgyMDQ0NjUzMjY0) in the last period to manage your repository through an LLM. As you can imagine, I use the [GitLens](https://www.gitkraken.com/gitlens) extension, which has a panel that lets me see everything across my repositories. *Spoiler: I use it with* [*GitHub*](https://github.com/)*, but you can also use it with* [*Bitbucket*](https://bitbucket.org/) *and* [*GitLab*](https://gitlab.com/)*. It can also combine data across different platforms.* ![GitLens Lauchpad](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q3us68bhv49mb7fs3wp6.png) In this panel, there are different groups where you can find your PRs that meet that status. If you click the PR, you will see a pop-up that lets you get more info or jump into the repository if you have already set it up on your machine. ![GitLens Lauchpad Pop-Up](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5p2cm57xnjhkpno8suvk.png) As you can see, just using these two extensions, I reduced the number of switches between my lovely [VSCode](https://code.visualstudio.com/) and the [GitHub](https://github.com/) website. In my editor, I already have control over all my repositories, and with the correct panel, I can do most of my work directly in the editor. Before closing, I’d like to give you another extension that reduces the need to switch between the editor and the [GitHub](https://github.com/) website. This extension is the [GitHub Actions](https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-github-actions) one. This extension lets you check the status of actions directly in the editor, without jumping to [GitHub](https://github.com/) to search for them. ## Conclusion It’s time to wrap up this article. At first, I showed how I used to check my current work across projects on [GitHub](https://github.com/), but eventually it became a pain point because switching between the editor and the browser was annoying. Then I moved to the [GitHub Pull Requests](https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-pull-request-github) extension, which enables me to open, review, and check PRs and Issues in the current open repository. Lastly, I talked about the [GitLens](https://www.gitkraken.com/gitlens) [Launchpad,](https://www.gitkraken.com/features/launchpad) which, through a panel in the editor, provides a big-picture view of all the PRs across the repositories I’m working on and helps me reduce switching between the editor and [GitHub](https://github.com/) during my daily work. *If you want to use the* [*GitLens*](https://www.gitkraken.com/gitlens) *Launchpad, it is a PRO feature that requires a valid* [*GitKraken*](https://www.gitkraken.com/) *license. To buy one, check this* [in this reference](https://gitkraken.cello.so/I4dkhuQcpJN)*.* That’s it for this article. I hope this feature can also improve your daily DX, and see you in the following articles. Bye bye 👋 --- ### The Mood Mirror App - My Antigravity first experience - **Canonical URL**: https://delpuppo.net/blog/the-mood-mirror-app/ - **Article LLMs.txt**: https://delpuppo.net/blog/the-mood-mirror-app/llms.txt - **Published**: Dec 4, 2025 - **Tags**: ai, gemini, vibecoding, antigravity, Google Antigravity, vibe coding - **Summary**: Just a week ago, Google launched Antigravity, and why not give it a try and share my experience? And... Just a week ago, Google launched [Antigravity](https://antigravity.google/), and why not give it a try and share my experience? And here I am for this. In this article, I'll talk about my first experience with Antigravity and how I built a small web app called **The Mood Mirror**. This app emulates an artist who can convert your mood into abstract images or videos. Spoiler: this app uses [Gemini 3](https://deepmind.google/models/gemini/), [Nano Banana Pro](https://gemini.google/overview/image-generation/) and [Veo](https://gemini.google/overview/video-generation/), but I'll talk about them in a future article. ## Antigravity Intro Let's start with a small presentation about Antigravity. Google Antigravity is an Agentic IDE. It is a fork of VSCode with a dedicated agent section that features models such as Gemini, Claude, and GPT. This last part is what makes Antigravity different from VSCode or Cursor; this section is built and maintained by Google. Yes, the experiences are common, but how this Agent section is integrated into the IDE is what differentiates it from the others, and this is where all the IDE teams are working to improve their users' DX. For more info about Google Antigravity, you can check its website [in this reference](https://antigravity.google/). ## "The Mood Mirror" Setup To test the tool's full power, I decided to build the application almost entirely in "Vibe Coding". To that end, I began by setting up the project and wrote a prompt outlining my intention and what I hoped to achieve. ![Antigravity Screen](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yg8obm27dltd6ty6bshh.png) In this picture, you can see what Antigravity looks like, and as you can imagine, in the chat, you can prompt our intention. For my application, I entered this prompt. **Role:** You are a Senior Software Engineer Agent. **Project:** Build a responsive web application called **"Mood Mirror"**. **Stack:** React, Tailwind CSS, and the Gemini 3 SDK. **Core Functionality:** 1. **Input:** Create a clean, minimalist text area where the user enters how they feel (e.g., "I am anxious about my deadline"). 2. **The "Director" Logic (Gemini 3):** When the user submits, send the text to Gemini 3. - Analyse the **Sentiment Score** (0 to +1.0). - Extract an **Emotional Colour Palette** (e.g., "Stormy Grey and Electric Blue"). - Generate a **Creative Prompt** for an abstract art piece based on that emotion. 3. **The "Artist" Execution (Nano Banana):** - Pass the *Creative Prompt* to the Nano Banana Pro API. - **Conditional Logic:** If the Sentiment Score is extreme (above 0.8 or below -0.8), request a **5-second looping Video (MP4)**. Otherwise, request a **High-Res Image (PNG)**. 4. **Dynamic UI:** Update the app's background gradient instantly to match the *Emotional Colour Palette* returned by Gemini while the image generates. **Execution Plan:** * Scaffold the project. * Write the SentimentService using the Gemini 3 logic. * Implement the Nano Banana API hook. * Deploy to preview. The result of this prompt was a plan of action for Antigravity to implement my request. I got this result because I selected "planning" in the bottom-left drop-down. If you decide to go with the fast option, Antigravity doesn't show the plan and immediately develops it. Btw, I chose the planning options so that I can check and comment on this plan directly in the IDE. ![Execution Plan](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sj7xr24a7xq3wu9e5cqh.png) In the image above, you can see an example of the plan I received. As you can see, you can either accept it or review it, and then ask Antigravity to check your comments and review the plan. The prompt and its plan are the result of this [commit](https://github.com/Puppo/the-mood-mirror/commit/e5a2784b77c93014a9cb36ecd70c94a2ef8e4a13). **What went wrong? 😑** The setup has gone smoothly and pretty well, but I ran into an issue: Antigravity generated a JavaScript application rather than a TypeScript one. Not a big deal. Later, I asked it to review the project in JavaScript and TypeScript, and it converted it without any issues. [in the GitHub repository](https://github.com/Puppo/the-mood-mirror/commit/66ba677e104cca6c1c09de4d339e1e6e9cae4c77) you can see the result. And lastly, it generated two files, one for the Sentiment discovery and the other for the image generation, but it created a sort of mock service without. I didn’t iterate again to fix them, but I’ve worked on them later. ## Commit linting into the project The second test I did was about setting up a development tool to verify the commit messages. I like to use commitlint and conventional commits for my project, so my second test was adding the setup for these tools. I opened a new chat, and I entered this prompt. *I want to add commitlint and conventional-commit to this project. Can you set it up?* I was concerned that the prompt wasn’t particular, but I did it to see what Antigravity produces and how good the results are. I ran that prompt in “fast” mode, and the first result was pretty good. It installs and sets up `@commitlint/cli` and `@commitlint/config-conventional` and after that, it added `husky` to validate the commit message in the pre-commit hook. I’m not a `husky` fan, I prefer `lefthook`. So after this first iteration, I asked Antigravity to replace `husky` with `lefthook`. The result I got worked, but it wasn’t the best solution ever. I decided not to reiterate this time, to leave it in the repo and also show what an Agentic IDE can generate when we don’t check its results and trust it too much. In this second iteration, Antigravity added lefthook without any problem, but it didn’t remove all the `husky` references. In fact, if you check the [commit](https://github.com/Puppo/the-mood-mirror/commit/1ca9fed538fa1ee15f3e8dd07ac149e01ee68bf3), you can see the `_husky` folder with two files, with no sense script for this repo, and in the `package.json`, it forgot to remove the `prepare` script. As you can see, our code review when using these tools is crucial to avoid trash or unnecessary code. ## The Gemini 3.0 integration The next test was the Gemini integration to build the Sentiment analysis service. As I did in the previous tests, I started from a new chat. The prompt in this case was a bit more articulated. I asked to generate a service that accepts a text and, through Gemini, returns a structured result with these fields: * *moodScore*: a value between 0 and 1 to determine the mood score * *promptType*: This is the value that determines if the mirror will show an image or a video * *promptText*: This is the text to pass later to generate the video or the image This result must later be used to generate an image or a video using NanoBanana Pro or Veo 3. This iteration has been a bit convoluted. The first version used the old npm package `@google/generative-ai` and not the new one `@google/genai`. So I kept reiterating to get the code updated to the new version of the SDK. Another issue I had was that Antigravity created a single file to handle both images and videos. So, for that, I reiterated the need to split the logic for image and video generation into two separate files, and I also fixed the video generation to use the video model instead of the image model. After that, I got a working application that met my first requirements, and you can see a demo in the video below. *\[and* [in the GitHub repository](https://github.com/Puppo/the-mood-mirror/commit/9dc0d5f74ee505ecf3f04f7baa53248eeea0e331#diff-053150b640a7ce75eff69d1a22cae7f0f94ad64ce9a855db544dda0929316519) *you can check the commit\]* > 🎬 **Watch / View Embedded Resource:** [https://www.youtube.com/watch?v=098qvXvpVZQ](https://www.youtube.com/watch?v=098qvXvpVZQ) ## Conclusion It’s time to wrap up this first experience with Antigravity. Overall, the experience with the tool was good. This is its first version, and only a few weeks after its release, Antigravity showed great DX and a good understanding of what I want to achieve. Yes, it is not perfect, and in some cases, the plan described at the beginning hasn’t been satisfied. But this also happens with IDEs that have been in place for months. As with the other Agentic IDEs, including Antigravity, developer checks are key to boosting implementation velocity while maintaining codebase quality. The planning mode is fantastic in my opinion, because taking a look at the plan and reviewing or confirming it before starting the implementation can reduce the time wasted on reiterating the Agent result; as we saw, it’s still not perfect yet, but I’m pretty sure it will be tuned and improved in the future. If I had to rate this first experience, I'd give it **7/10** because the flow was excellent, but I expected more precise results. Okay, that’s all. I hope you enjoyed this article. Let me know in the comments your experience with Antigravity or other Agentic IDEs. Bye Bye 👋 --- ### Improve Your Git Commit Messages with GitLens Tips - **Canonical URL**: https://delpuppo.net/blog/improve-your-git-commit-messages-with-gitlens-tips/ - **Article LLMs.txt**: https://delpuppo.net/blog/improve-your-git-commit-messages-with-gitlens-tips/llms.txt - **Published**: Oct 3, 2025 - **Tags**: git, gitkraken, ai, gitlens, commit messages, commit - **Summary**: In the AI era, we can do many different things with LLM: generate code, images, videos or ask... In the AI era, we can do many different things with LLM: generate code, images, videos or ask whatever we want and wait for an answer. The AI is here, and we cannot snub it. I love using it to simplify my day, help me find solutions, or learn new things. For instance, one of the most challenging tasks I perform every day is crafting a commit message for my next commit. I love to commit often, and I avoid commit messages like “WIP”, “work in progress”, “first step”, “second step”, and so on. In the past, I spent a considerable amount of time crafting the perfect commit message for the next commit. Currently, thanks to [GitLens](https://www.gitkraken.com/gitlens) (by [GitKraken](https://www.gitkraken.com/)), I can ask for help from the AI to generate a prefilled message based on my actions, and then review the generated message. ## How to generate messages To generate the commit messages, first of all, you have to change your codebase 😎 After that, when you are ready to commit, it’s time to generate the message. To do that, you need to go to the Source Control panel in [VSCode,](https://code.visualstudio.com/) and if you have the [GitLens](https://www.gitkraken.com/gitlens) extension installed, you can use the “Magic” icon to generate the message. ![GitLens Context Menu](https://cdn.hashnode.com/res/hashnode/image/upload/v1757526339717/5be16f0c-c4a7-4c3c-b54a-69221c17f656.png) If you click the button, you should have three options 1. Generate Commit Message with GitLens 2. Compose Commit (Preview) 3. Switch GitLens AI Provider/Model ![GitLens Context Menu Options](https://cdn.hashnode.com/res/hashnode/image/upload/v1757526614655/cb3b35b6-2be9-4734-afe2-d239ec2e48cb.png) It goes without saying that the first option is the correct one to use when generating your commit message. After clicking the button, you must wait for a couple of seconds, and the result will appear in the text box. ![Generated commit message](https://cdn.hashnode.com/res/hashnode/image/upload/v1757526749686/3de71875-6d01-406f-8214-fe8b92c0e9aa.png) As you can see, the result is excellent. A commit message that follows the conventional commit rules and a small description of what happened in the code. Now, you can decide whether the message is okay and commit without changing it, or review it and then commit with your edited message. ## Custom Instructions As with all AI features, instructions are crucial to achieving the best possible result. Also, for this feature, we can set up instructions to guide the AI in creating the messages. To do that, you have to go to Settings and search for “GitLens commit message AI custom instruction”. You should find an option “**Gitlens › Ai › Generate Commit Message: Custom Instructions (Preview)”**, and this option has a text box that you can fill. In my case, I inserted this instruction: “Please generate the commit message in the format of conventional commit,” but you can insert all the instructions that you want. These instructions will help the AI generate the best messages based on your needs, and they will likely reduce the changes required to achieve the best message in the end. ## Conclusions As you can see, this feature is pretty simple but very helpful. It can reduce the time you spend generating commit messages and help you describe the intention of your commits in the best way. I use it daily, and it helps me to be more precise in my commit messages, which in turn helps me summarise the commit in the best way. *N.B. I’m not a native English speaker, and this feature helps me express myself in the best possible way in my commits.* Okay, that’s all, folks. I hope you’ve enjoyed the content and found it helpful. See you soon Bye Bye 👋 --- ### Do you really need the Cloud to work with AI? - **Canonical URL**: https://delpuppo.net/blog/do-you-really-need-the-cloud-to-work-with-ai/ - **Article LLMs.txt**: https://delpuppo.net/blog/do-you-really-need-the-cloud-to-work-with-ai/llms.txt - **Published**: Jun 25, 2025 - **Tags**: aisprint, gemini, ai, chrome, gemini-nano, geminiAPI - **Summary**: We are in the AI era. New models emerge daily, and many applications have already integrated AI into... We are in the AI era. New models emerge daily, and many applications have already integrated AI into their workflows. [Gemini](https://gemini.google.com/), [OpenAI](https://openai.com/), [Copilot](https://copilot.microsoft.com/), [Deepseek](https://www.deepseek.com/), [Llama](https://www.llama.com/), and many others enable AI in your applications or help you write code, but they all need an internet connection to be accessible. You need the Cloud or to connect to a managed service to get an answer from the model. In some scenarios, this approach exposes you to security issues or the risk of sharing data with another service, and you may want to avoid it. To help the team and companies benefit from AI and avoid these issues, the Chrome team is working to make Gemini (nano) part of the Chrome browser. The idea is to download the models in the browser when you have connectivity and use them directly from the browser, offline, if needed. So let's see how it works 🚀 *These features are on the building; most are available only under the Canary version and feature flag. If you are interested, you can find all the info at this* [in this reference](https://developer.chrome.com/docs/ai/built-in)*.* ## Features available As you've already understood, only limited features are available; some are stable, and some are not. Let's start by listing the features, and later, we will look at some of them. The available API are: * [Translator API](https://developer.chrome.com/docs/ai/translator-api) * [Language Detector](https://developer.chrome.com/docs/ai/language-detection) * [Summarizer API](https://developer.chrome.com/docs/ai/summarizer-api) * [Writer API](https://developer.chrome.com/docs/ai/writer-api) * [Rewriter API](https://developer.chrome.com/docs/ai/rewriter-api) * [Prompt API](https://developer.chrome.com/docs/ai/prompt-api) * [Proofreader API](https://developer.chrome.com/docs/ai/built-in-apis#proofreader_api) Some are stable from Chrome 138, and some are available for local experiments; you can check out the status of the APIs [in this reference](https://developer.chrome.com/docs/ai/built-in-apis#proofreader_api) so you can be sure about the current situation when you read this post. As you can see, the APIs' names are fantastic, and they explain their behaviour. If you want to try these features, start from [this documentation](https://developer.chrome.com/docs/ai/get-started) section. Unfortunately, you must enable the features through the configurations, so follow these steps to get a Chrome instance ready to run AI models directly from your laptop. Before closing this chapter, here is a quick reflection on these APIs. As you can notice, all of them work with text; some produce, some detect, some translate, and so on. So, for now, you can work with text and not produce images or videos from the browser, but it's a great starting point! ## Let's talk about features ### Language Detector The language detector API is one of the features available from Chrome 138. It allows you to detect the language of a specific word or phrase. It returns a list of possible languages with the confidence score for that language. This means that we need to make a strategy to choose the best language for our application, but in most cases, the language with the best score is the best one for our application! **How it works!** One thing to do is check if the API is available or not to prevent errors. To do that, you must check the `self` object. ```typescript if ('LanguageDetector' in self) { // Language Detector Available } ``` Then, you must check if the model is already available in your browser or if you need to download it. ```typescript const availability = await LanguageDetector.availability(); ``` This result can have four possible values: * `"unavailable"`: The requested options are unsupported, or the model cannot be prompted. * `"downloadable"`: The request is supported, but you need to download additional files before creating a session. These downloads may include the language model or fine-tuning. * `"downloading"`: The request is supported and a download is ongoing, which must be completed before creating a session.. * `"available"`: The request is supported, and you can create a session. *This method is pretty much the same for all APIs. Following this flow, you can check if a specific API is available.* When you reach the `available` status, you are finally ready to work with the **LanguageDetector** API, and you can create an instance of it by following the next code. ```typescript const instance = await LanguageDetector.create(); const result = await instance.detect('Hello world!'); // result = [{ // detectedLanguage: 'en', // confidence: 0.9973934888839722 // }]; ``` As you can see, this API is straightforward and pretty easy to learn. With that, you have completed your first learning about AI in Chrome with Gemini Nano, and it’s time to move on to another fantastic API! *N.B. If you are working with* [*TypeScript*](https://www.typescriptlang.org/) *and want to get the best DX possible, a types package that simplifies your work and enables IntelliSense for these APIs exists.* `@types/dom-chromium-ai` *This is the name of the package, and we have to thank you,* [*Christian Liebel*](https://github.com/christianliebel) *and* [*Thomas Steiner*](https://github.com/tomayac)*, for it.* ### Translator API Translator API is one of the most common features for an AI. It's pretty simple: you send a text, and you will receive the text translated. This API is available from Chrome 138. But let’s see how it works. **How it works!** First, you must check if the API is available in the browser. ```typescript if ('Translator' in self) { // Translator API available } ``` If the API is enabled, you must check if the languages you need for source and target are available. To do that, you must use the `availability` method exposed by the `Translator` object. ```typescript const translatorCapabilities = await Translator.availability({ sourceLanguage: 'en', targetLanguage: 'it', }); ``` The result of this method can have four different values: * `"unavailable"`: The implementation does not support translation of the given languages. * `"downloadable"`: The implementation supports translation of the given languages, but a download is required to proceed. The download may be the browser model. * `"downloading"`: The implementation supports translation of the given languages. The browser is finishing an ongoing download, as part of creating the associated object. * `"available"`: The implementation supports translation of the given languages, and any required downloads are already complete. Now, if you don’t receive `"unavailable"`, you can create the translator instance. This method will download the model for you if it hasn’t already been downloaded. ```typescript const translator = await Translator.create({ sourceLanguage: 'en', targetLanguage: 'it', monitor(m) { m.addEventListener('downloadprogress', (e) => { console.log(`Downloaded ${e.loaded * 100}%`); }); }, }); ``` As you can see, using the `create` method, you can create your instance and pass your source and target languages. Then, using the `monitor` method, you can listen to the download progress and maybe create a loader for the user to provide feedback and create a better UX experience. Obviously, the promise will fail if something goes wrong during the download, and you must handle it. But if everything is okay, you can use the new `translator` instance to translate the text by using the `translate` method. ```typescript await translator.translate('Where is the next bus stop, please?'); // Dov'è la prossima fermata dell'autobus, per favore? ``` One important thing to know about this API is that it processes messages sequentially. So, if you have many messages, you must keep this limitation in mind. Then, if you need to translate large amounts of text, it’s better to chunk it and create a [loading interface](https://web.dev/articles/building/a-loading-bar-component) as feedback for the user. As is evident, this API is also straightforward. There are some methods to learn, and you can integrate this API into your applications by using them. *Also, for this API, you can use the* `@types/dom-chromium-ai` *package to get a better experience with* [*TypeScript*](https://www.typescriptlang.org/)*.* ### Summarizer API The last API you will meet in this article is the Summarizer API. This API enable you to summarise small or large text. Also, this API is available from Chrome 138 stable. **How it works!** First, you must check if the API is available in the browser. ```typescript if ('Summarizer' in self) { // Summarizer API available } ``` If the API is enabled, then you must check if the model is available. ```typescript const summarizerCapabilities = await Summarizer.availability(); ``` Also, for this method, the possible results can be: * `"unavailable"` means that the implementation does not support the requested options. * `"downloadable"` means that the implementation supports the requested options, but first, the browser has to download something, such as a model (in Chrome's case, Gemini Nano) or fine-tuning for the model. * `"downloading"` means that the implementation supports the requested options, but it has to finish an ongoing download before it can proceed. * `"available"` means that the implementation supports the requested options and the summarizer can proceed. Now, if everything is okay, it’s time to create an instance of the Summarizer API. ```typescript const summarizer = await Summarizer.create({ monitor(m) { m.addEventListener('downloadprogress', (e) => { console.log(`Downloaded ${e.loaded * 100}%`); }); } }); ``` This `create` method exposes the `monitor` function to listen to the download progress and create a better experience for the user as well. This `create` method has different parameters you can pass to get different types of summaries or drive the summary's length. These parameters are: * `sharedContext`: Additional shared context that can help the summarizer. * `type`: The type of summarisation, with the allowed values `key-points` (***default***), `tldr`, `teaser`, and `headline`. To learn more about them, follow this [in this reference](https://developer.chrome.com/docs/ai/summarizer-api). * `format`: The format of the summarisation, with the allowed values `markdown` (default) and `plain-text`. * `length`: The length of the summarisation, with the allowed values `short`, `medium` (default), and `long`. The meanings of these lengths vary depending on the requested `type`. It's important to know that parameters can’t be changed. So, once you create a new instance, if you need to modify the parameters, you must create a new instance. Now that you have the summarizer instance, you can use it to summarise the text. ```typescript const longText = 'Blah blah blah......'; const summary = await summarizer.summarize(longText, { context: 'This is a recipe.', }); ``` This API also exposes another method, just in case you want to stream the summary result and make it available “faster.” To do that, you need to use the `summarizeStreaming` method instead of the `summarize` one. ```typescript const longText = 'Blah blah blah......'; const summary = await summarizer.summarizeStreaming(longText, { context: 'This is a recipe.', }); ``` And with that, you learnt the last AI API for Browsers available in this article 😃 Like the others, this API is straightforward too. There are just some methods to know, and you can integrate this API into your applications. ## Recipe Radar with AI App I wrote a lot in this article, I know, but to let you know how these APIs work. I created a sample app with some recipes. On the detail page, you can see the Language Detector and Translator API in action. Therefore, on the search page, you can see the Summarizer API in action to summarise the recipe and return a summary of the recipe to the user. If you are interested in it, here is the link to the repo and to the website: * *Github Repo*: [https://github.com/Puppo/recipe-radar-with-ai](https://github.com/Puppo/recipe-radar-with-ai) * *GitHub Page Website:* [https://puppo.github.io/recipe-radar-with-ai/](https://puppo.github.io/recipe-radar-with-ai/) Please don’t take too much care of the code structure. I vibe-coded the application's structure to reduce the time it took to create, and I spent more time on the AI feature and its integration. By the way, this application example implements and runs all these APIS on a website. It’s important to test in a Chrome version with these APIs available. ## Conclusion Okay, it’s time to wrap up! In this article, you learn how to integrate AI inside the Browser without relying on any services or cloud providers. You learnt how to use Language Detector, Translator, and Summarizer API on the Web and what they look like. Remember that these new features are not available in all browsers, so please be careful when using them in production. That’s it, folks. I hope you enjoyed the content. If so, please leave a comment or a reaction! See you soon with other awesome content 🚀 Bye Bye 👋 *Google Cloud credits are provided for this project.* #AISprint --- ### A Promise is forever - **Canonical URL**: https://delpuppo.net/blog/a-promise-is-forever/ - **Article LLMs.txt**: https://delpuppo.net/blog/a-promise-is-forever/llms.txt - **Published**: Feb 6, 2024 - **Tags**: javascript, es6, promise, abotwrotethis, promises, async - **Summary**: In the ever-evolving world of web development, mastering asynchronous operations is a crucial skill... In the ever-evolving world of web development, mastering asynchronous operations is a crucial skill for any JavaScript developer. Asynchronous programming allows web applications to handle tasks like API requests, file operations, or any activities that require waiting for results without blocking the main thread. Traditionally, this was managed through callback functions, leading to the infamous "callback hell," where code becomes tangled and difficult to maintain. Enter JavaScript Promises - a powerful and elegant solution to simplify asynchronous code management. Promises in JavaScript represent the eventual completion or failure of an asynchronous operation and its resulting value. They not only provide a cleaner and more manageable way to handle asynchronous tasks but also offer improved error handling and readability. This makes them an indispensable tool in the modern JavaScript developer's toolkit. In this comprehensive guide, we'll embark on a journey to unravel the mysteries of JavaScript Promises. From their fundamental concepts to advanced techniques, we will explore how Promises can streamline your code and make handling asynchronous operations a breeze. Whether you're just starting your journey with JavaScript or are a seasoned developer, this post aims to enhance your understanding and skills in working with Promises. So, let's dive in and unlock the full potential of JavaScript Promises, transforming the way you write and manage asynchronous code! ## **Section 1: Understanding Promises** ### **What is a Promise?** At its core, a JavaScript Promise is an object that represents the eventual completion or failure of an asynchronous operation and its resulting value. Unlike the immediate return of values in synchronous code, a Promise gives us a placeholder for a future value, which we can work with in a more structured and predictable manner. This approach solves many issues associated with older, callback-based patterns, commonly known as "callback hell." Promises allow for more readable and maintainable code, especially when dealing with complex sequences of asynchronous operations. ### **Creating a Promise** A Promise in JavaScript is created using the `Promise` constructor. It takes a function as its argument, known as the executor function. This executor function is called immediately by the Promise implementation and it receives two functions as parameters: `resolve` and `reject`. - `resolve` is called when the asynchronous task completes successfully, passing the result. - `reject` is used when the task fails, passing the error or reason for failure. Here's a basic example of creating a Promise: ```js let myPromise = new Promise((resolve, reject) => { // Asynchronous operation code here if(/* operation successful */) { resolve('Success'); } else { reject('Error'); } }); ``` In this example, the Promise will either resolve with `Success` or reject with `Error`, depending on the outcome of the asynchronous operation encapsulated within it. ### **Promise States** A Promise can be in one of three states: 1. **Pending** : The initial state of a Promise. The operation has not been completed yet. 2. **Fulfilled** : The operation was completed successfully, and the Promise now holds the resulting value. 3. **Rejected** : The operation failed, and the Promise holds the reason for the failure. This state model ensures that a Promise can only be resolved or rejected once and its state and value become immutable after that change. Understanding these foundational concepts is crucial for working effectively with Promises in JavaScript. They form the building blocks upon which more complex asynchronous operations can be built, allowing for more readable and maintainable code. ## **Section 2: Working with Promises** ### **then, catch, and finally** After creating a Promise, the next step is handling its resolution or rejection. This is where the methods `then`, `catch`, and `finally` come into play. - **then** : This method is used to access the result of a Promise if it is successfully resolved. It takes up to two functions as arguments: the first for the resolved case and the second for the rejected case. - **catch** : This method is used for error handling in Promises. It is essentially a shorthand for `then(null, rejection)`. - **finally** : Introduced in ES2018, `finally` is a method that executes a specified piece of code when the Promise is settled, regardless of its outcome (resolved or rejected). It's useful for cleanup actions. ### **Chaining Promises** One of the key strengths of Promises is their ability to be chained. This is done by returning another Promise in the `then` method, which allows for sequential asynchronous operations. ```js new Promise((resolve, reject) => { setTimeout(() => resolve(1), 1000);}) .then(result => { console.log(result); // 1 return result * 2; }) .then(result => { console.log(result); // 2 return result * 3; }) .then(result => { console.log(result); // 6 return result * 4; }); ``` In this chain, each `then` receives the result of the previous operation, allowing for a sequence of asynchronous tasks to be handled in a clean and readable manner. ### **Error Handling in Promises** Error handling is crucial in asynchronous code. In a Promise chain, if an error is thrown in any of the `then` methods, it propagates down the chain until it is caught by a `catch` method. This makes it easier to manage errors in complex asynchronous code. ```js doSomething() .then(result => doSomethingElse(result)) .then(newResult => doThirdThing(newResult)) .catch(error => console.error(error)) .finally(() => console.log('Operation done')); ``` ### **Real-world Examples** Promises are particularly useful when dealing with API requests. For example, fetching data from a remote server: ```js fetch('https://api.example.com/data') .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error('Error:', error)); ``` This section on working with Promises is critical for understanding how to effectively implement and manage asynchronous operations in JavaScript. It covers the fundamental methods for handling Promises and demonstrates the power of chaining for complex tasks, along with practical examples that the readers can relate to and implement in their own projects. ## **Section 3: Advanced Promise Concepts** ### **Async/Await Syntax** Introduced in ES2017, async/await is a syntactic feature in JavaScript that allows you to work with Promises in a more synchronous-like manner, making your asynchronous code look and behave a bit more like synchronous code. - **Async Functions** : Declaring a function as `async` automatically wraps its return value in a Promise. This means you can use `await` within these functions to pause the execution until the awaited Promise is resolved. - **Await** : The `await` keyword is used to pause the execution of an async function until a Promise is resolved, allowing you to write cleaner and more readable asynchronous code. ### **Error Handling in Async/Await** Error handling in async/await is achieved using try/catch blocks, making it syntactically similar to synchronous error handling. ```js async function safeFetch() { try { const response = await fetch('https://api.example.com/data'); return await response.json(); } catch (error) { console.error('Fetch failed:', error); throw error; // Re-throwing the error is important for caller awareness } } ``` ### **Handling Multiple Promises** #### Promise.all `Promise.all` is used when you need to wait for multiple Promises to complete. It takes an array of Promises and returns a new Promise that resolves when all of the input Promises resolve or rejects as soon as one of them rejects. ```js Promise.all([asyncTask1(), asyncTask2(), asyncTask3()]) .then(results => { console.log('All tasks completed:', results); }) .catch(error => { console.error('One of the tasks failed:', error); }); ``` #### Promise.race `Promise.race` resolves or rejects as soon as one of the Promises in an iterable resolves or rejects, with the value or reason from that Promise. ```js Promise.race([asyncTask1(), asyncTask2(), asyncTask3()]) .then(result => { console.log('First task completed:', result); }) .catch(error => { console.error('One of the tasks failed:', error); }); ``` #### Promise.allSettled `Promise.allSettled` is a method that returns a Promise that resolves after all of the given Promises have either been resolved or rejected, with an array of objects describing the outcome of each Promise. ```js Promise.allSettled([asyncTask1(), asyncTask2(), asyncTask3()]) .then(results => { results.forEach((result) => { if (result.status === 'fulfilled') { console.log('Success:', result.value); } else { console.log('Failure:', result.reason); } }); }); ``` #### Promise.any `Promise.any` takes multiple Promises and as soon as one of the Promises resolves, it returns a single Promise that resolves with the value from that Promise. If all of the passed-in Promises reject, then the returned Promise is rejected with an `AggregateError`. ```js Promise.any([asyncTask1(), asyncTask2(), asyncTask3()]) .then(result => { console.log('First successful task:', result); }) .catch(error => { console.error('All promises failed:', error); }); ``` These methods for handling multiple Promises are powerful tools in a JavaScript developer's arsenal. They offer different ways to manage multiple asynchronous operations, making it easier to write clean and efficient code. Understanding when and how to use each of these methods can significantly improve the way you handle concurrency in JavaScript. ### **Understanding Promise.withResolvers** In the realm of JavaScript Promises, `Promise.withResolvers` could be conceptualized as a utility function designed to enhance the flexibility and control over Promise resolution and rejection. Unlike the standard Promise constructor, which encapsulates the executor function, `Promise.withResolvers` could provide direct access to the `resolve` and `reject` functions outside the Promise context. This feature would be particularly useful in scenarios where the resolution or rejection of a Promise needs to be triggered externally or in more complex control flows. Imagine `Promise.withResolvers` functioning as follows: ```js Promise.withResolvers = Promise.withResolvers ?? function() { let resolve, reject; const promise = new Promise((res, rej) => { resolve = res; reject = rej; }); return { promise, resolve, reject }; } const { promise, resolve, reject } = Promise.withResolvers(); promise .then(value => console.log(`Resolved with: ${value}`)) .catch(error => console.error(`Rejected with: ${error}`)); // The Promise can be resolved or rejected externally resolve("Success!"); // or reject("Failure"); ``` In this hypothetical implementation, `Promise.withResolvers` returns an object containing the Promise itself and its respective `resolve` and `reject` functions. This setup offers a more declarative way of handling Promises, especially in cases where the resolution condition is not immediately known or is dependent on external factors. This approach, while offering more control, also requires careful management to avoid issues like unresolved Promises or memory leaks. It exemplifies the flexibility of Promises in JavaScript and how they can be tailored to fit specific programming paradigms or application needs. This method has been implemented in ES2023 but creates a polyfill for it; it is really a piece of cake as you can see. ## **Section 4: Under the Hood** ### **Event Loop and Promises** JavaScript's concurrency model revolves around the Event Loop, which plays a critical role in executing asynchronous code like Promises. Understanding this mechanism is key to mastering JavaScript's asynchronous behavior. - **Event Loop** : JavaScript has a single-threaded runtime, meaning it can only execute one command at a time. The event loop enables non-blocking operations by offloading tasks like I/O, timers, and HTTP requests, which are handled outside the JavaScript engine. - **Role of Promises** : When a Promise is resolved or rejected, its callback is moved to the Microtask Queue, which is processed after the current execution stack is complete but before the event loop continues to the next iteration. This ensures that Promise callbacks are executed as soon as the JavaScript engine has the opportunity to do so, maintaining the non-blocking nature of asynchronous operations. ### **Creating Custom Promises** While many asynchronous operations are handled by built-in functions returning Promises, there are scenarios where you might need to create your own Promises. - **Custom Promise Example** : Let's say you're interfacing with a legacy API that uses callbacks, and you want to adapt it to use Promises for better code consistency and readability. ```js function legacyApiFunction(callback) { // Simulating a callback-based API setTimeout(() => { const data = 'some data'; callback(null, data); }, 1000); } function promisifyLegacyApi() { return new Promise((resolve, reject) => { legacyApiFunction((err, data) => { if (err) { reject(err); } else { resolve(data); } }); }); } promisifyLegacyApi() .then(data => console.log(data)) .catch(error => console.error(error)); ``` In this example, `promisifyLegacyApi` wraps the old callback-style function in a new Promise, providing a more modern, Promise-based interface for the same functionality. ### **Best Practices and Pitfalls** - **Avoiding the "Promise Hell"** : Similar to "callback hell," it's possible to end up in a situation with deeply nested Promises. To avoid this, leverage chaining and return Promises from within `then` callbacks, keeping the code flat. - **Error Handling** : Always handle errors in Promises, either using `catch` for individual Promises or global error handlers for unhandled Promise rejections. - **Debugging** : Debugging asynchronous code can be tricky. Utilize modern tools and techniques such as browser developer tools, which provide features to track and inspect Promises. If you want to dive deep into Promise, don't leave my YouTube video about it. > 🎬 **Watch / View Embedded Resource:** [https://www.youtube.com/watch?v=ROwJX3i_m2Q](https://www.youtube.com/watch?v=ROwJX3i_m2Q) ## **Conclusion** As we conclude our journey through the world of JavaScript Promises, it's clear that they are much more than just a tool for handling asynchronous operations. They represent a fundamental shift in how we write and think about asynchronous code in JavaScript, offering a robust and elegant way to manage complex operations. From understanding the basics of what a Promise is and how to create one, to exploring advanced concepts like `Promise.all`, `Promise.race`, `Promise.allSettled`, and `Promise.any`, we've covered a broad spectrum of functionalities that Promises provide. The async/await syntax further simplifies working with Promises, allowing for code that is not only more readable but also easier to maintain. The real power of Promises lies in their ability to streamline complex asynchronous tasks, making our code cleaner, more efficient, and more reliable. By understanding the event loop and how Promises fit into JavaScript's concurrency model, developers can write more performant and bug-free code. As you incorporate these concepts into your daily coding practices, remember that mastering Promises is a journey. There's always more to learn and new patterns to explore. Keep experimenting, keep learning, and most importantly, keep coding. The world of JavaScript is constantly evolving, and with tools like Promises at your disposal, you're well-equipped to tackle the challenges of modern web development. Happy coding! _You can find the code of this article_ [in the GitHub repository](https://github.com/Puppo/JavaScript-Examples/tree/01-promises)_._ > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### The Reflect API: The Swiss Army Knife Every JavaScript Developer Needs - **Canonical URL**: https://delpuppo.net/blog/javascript-you-dont-know/the-reflect-api-the-swiss-army-knife-every-javascript-developer-needs/ - **Article LLMs.txt**: https://delpuppo.net/blog/javascript-you-dont-know/the-reflect-api-the-swiss-army-knife-every-javascript-developer-needs/llms.txt - **Published**: Nov 9, 2023 - **Series**: The JavaScript You Don't Know (https://delpuppo.net/blog/series/javascript-you-dont-know/) - **Tags**: javascript, reflect, js - **Summary**: Ever wished for a magical toolkit that gives you superhero-like control over JavaScript objects? Say... Ever wished for a magical toolkit that gives you superhero-like control over JavaScript objects? Say hello to the `Reflect` API, the Swiss Army knife every modern JavaScript developer needs! From peeping into objects to conjuring custom behaviors `Reflect`is your one-stop spellbook! 📜 Strap in as we teleport through this arcane realm of JavaScript, where we'll not only decode `Reflect`, but also empower you with hands-on examples and practical tips! 🚀 ## **🎯 Core Objectives of Reflect** 1. **Introspection** : Much like looking into a magic mirror, `Reflect` allows you to delve into an object's properties and characteristics, enabling you to perform introspective tasks like checking property existence or retrieving property descriptors. 2. **Manipulation** : Think of `Reflect` as your Swiss Army knife for object operations. You can easily add, delete, or modify object properties and even trap or monitor these operations. 3. **Extensibility** : The `Reflect` API can be seen as a foundational layer that sets the stage for creating Proxy objects, empowering you to build custom behaviors for basic operations (like property lookup, assignment, enumeration, and more). ## 🛠 **Reflect Methods** ### 1. `Reflect.apply()` Forget the complexities of `Function.prototype.apply()`! With `Reflect.apply()`, invoking functions is a breeze. ```js const numbers = [1, 2, 3]; const sum = (a, b, c) => a + b + c; // Using Reflect.apply() const result = Reflect.apply(sum, null, numbers); console.log(result); // Output: 6 ``` ### 2. `Reflect.get()` Tired of the limitations of vanilla property retrieval? `Reflect.get()` offers more control and flexibility. ```js const obj = { x: 42, y: 'hello' }; // Using Reflect.get() const value = Reflect.get(obj, 'x'); console.log(value); // Output: 42 ``` ### 3. `Reflect.set()` Modify properties with pinpoint accuracy, even within complex object hierarchies. ```js const obj = { x: 42 }; // Using Reflect.set() Reflect.set(obj, 'x', 13); console.log(obj.x); // Output: 13 ``` ### 4. `Reflect.defineProperty()` Defining properties with their descriptors has never been easier! ```js const obj = {}; // Using Reflect.defineProperty() Reflect.defineProperty(obj, 'x', { value: 42, writable: false }); console.log(obj.x); // Output: 42 ``` ### 5. `Reflect.deleteProperty()` Wipe properties off the face of your object as if they never existed. ```js const obj = { x: 42 }; // Using Reflect.deleteProperty() Reflect.deleteProperty(obj, 'x'); console.log('x' in obj); // Output: false ``` ### 6. `Reflect.ownKeys()` Discover all the keys an object owns, including Symbols! ```js const obj = { x: 42, [Symbol('key')]: 'symbolValue' }; // Using Reflect.ownKeys() const keys = Reflect.ownKeys(obj); console.log(keys); // Output: ['x', Symbol(key)] ``` ## 🚀 **Why Use Reflect?** By now, you might be asking yourself, "Why should I dive into `Reflect` when there are other ways to manipulate and inspect JavaScript objects?" Great question! Let's unravel the reasons that make `Reflect` a powerful tool in your JavaScript arsenal. ### 1. Functional Paradigm The `Reflect` API offers static methods that embrace the functional programming paradigm, making them first-class citizens that you can pass around in your code. 👉 **Example:** Suppose you want to make property retrieval generic. You can do so with ease. ```js const genericGet = Reflect.get; const value = genericGet(someObject, 'someProperty'); ``` ### 2. Enhanced Error Handling Unlike methods in the `Object` API that often throw errors, `Reflect` methods return a Boolean to indicate success or failure, allowing for more elegant error handling. 👉 **Example:** With `Reflect.set()`, you can check if a property was set successfully and proceed accordingly. ```js if (Reflect.set(obj, 'key', 'value')) { // Property was set successfully } else { // Failed to set the property } ``` ### 3. Proxy Integration `Reflect` and `Proxy` are like two peas in a pod. The methods available in `Reflect` align perfectly with `Proxy` traps, allowing for seamless and straightforward custom behaviors. 👉 **Example:** Creating a logging proxy becomes ridiculously simple. ```js const handler = { get(target, property) { console.log(`Reading property: ${property}`); return Reflect.get(target, property); } }; const proxy = new Proxy(someObject, handler); ``` ### 4. Consistency and Predictability Methods in `Reflect` offer a more consistent API. They always return values (often Booleans) instead of throwing errors, and the parameter orders are predictable, leading to cleaner and more maintainable code. 👉 **Example:** Both `Reflect.get()` and `Reflect.set()` have consistent argument ordering: `target, propertyKey[, receiver]`. ```js Reflect.get(target, property); Reflect.set(target, property, value); ``` ### 5. Intuitive and Self-Documenting `Reflect` methods are often more intuitive and self-documenting than their `Object` counterparts. This readability makes it easier for developers, new and experienced alike, to understand the code. 👉 **Example:** Compare `Reflect.ownKeys(obj)` with `Object.getOwnPropertyNames(obj).concat(Object.getOwnPropertySymbols(obj))`. Which one do you find more intuitive? ### 6. Future-Proofing As JavaScript evolves, new methods are more likely to be added to the `Reflect` API, making it a wise choice for long-term projects. For more info don't waste my YouTube video on my [**YouTube Channel**](https://www.youtube.com/@Puppo_92). > 🎬 **Watch / View Embedded Resource:** [https://www.youtube.com/watch?v=gaCvSu3ncbw](https://www.youtube.com/watch?v=gaCvSu3ncbw) ## **Conclusion** And just like that, we've come to the end of our magical journey through the labyrinthine world of JavaScript's `Reflect` API! You're now armed with the tools, tricks, and spells—err methods—to perform object manipulation wizardry that would make Dumbledore proud. Ready to cast your own spells? Grab your keyboard wand, and let's make the JavaScript realm a little more enchanted, one `Reflect` method at a time! Remember, the magic isn't just in the code; it's in you, the coding sorcerer who brings it to life. Until our next spellbinding adventure, code on, wizards! 🚀 _You can find the code of this article_ [in the GitHub repository](https://github.com/Puppo/javascript-you-dont-know/tree/08-reflect-api) > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### Cracking the JavaScript Code: Unveil the Magic of Object Property Descriptors - **Canonical URL**: https://delpuppo.net/blog/javascript-you-dont-know/cracking-the-javascript-code-unveil-the-magic-of-object-property-descriptors/ - **Article LLMs.txt**: https://delpuppo.net/blog/javascript-you-dont-know/cracking-the-javascript-code-unveil-the-magic-of-object-property-descriptors/llms.txt - **Published**: Nov 2, 2023 - **Series**: The JavaScript You Don't Know (https://delpuppo.net/blog/series/javascript-you-dont-know/) - **Tags**: javascript, object, descriptor, js, Objects - **Summary**: 👋 Hey, JavaScript developers! Ever wondered what makes your objects tick? Or how to go all... 👋 Hey, JavaScript developers! Ever wondered what makes your objects tick? Or how to go all "Inception" on your JavaScript code by manipulating object properties at a deeper, more granular level? Well, you're in luck! Today, we're diving into the magical world of Object Property Descriptors in JavaScript—a realm where you become the puppet master of your objects. 🎭 Get ready for a roller coaster ride that takes you through the hidden alleys of JavaScript's objects. By the end of this post, you'll be wielding Object Property Descriptors like a wizard casting spells, giving you full control over properties, and even the behaviour of your objects. 🧙 ## What is a JavaScript Object Property Descriptor? In JavaScript, objects are more than just key-value pairs. They're intricate entities with characteristics that often go unnoticed, hidden beneath the surface like an iceberg in an ocean of code. Welcome to the fascinating realm of Object Property Descriptors, a tool that lets you truly tap into the full potential of JavaScript objects. An Object Property Descriptor is essentially a metadata object that details how a property on an object behaves. It's like the DNA of the property, encoding its attributes and defining its characteristics. You can think of an Object Property Descriptor as a control panel, giving you knobs and levers to fine-tune how a property can be accessed, modified, enumerated, or deleted. Here are the attributes you can configure with an Object Property Descriptor: - `value`: The actual value of the property. Simple enough, right? 📦 - `writable`: Determines if the property value can be changed. It's like a lock that you can engage or disengage at will. 🔒 - `enumerable`: Controls whether the property shows up during enumeration, like when you use a `for...in` loop or `Object.keys()`. It's like a spotlight—or lack thereof—on your property. 🔦 - `configurable`: Decides if the property can be deleted or has its attributes changed. Think of it as the ultimate veto power. 🛑 You can access and modify these attributes using built-in JavaScript methods like `Object.getOwnPropertyDescriptor()`, `Object.defineProperty()`, and `Object.defineProperties()`. These are your magic wands to perform spellbinding feats of object manipulation! For example, to make a property read-only, you can set its `writable` attribute to `false`. Or if you want to hide a property from enumeration, just set the `enumerable` attribute to `false`. It's like tailoring your object properties to fit your exact needs, almost as if you're crafting a bespoke suit! ## **Object Property Descriptors in Practice** It's time to get our hands dirty and jump into the practical aspects of using Object Property Descriptors in JavaScript. ### 1. Accessing Descriptors with `Object.getOwnPropertyDescriptor()` Let's start with the basics. Ever wondered what the descriptor of an existing object property looks like? `Object.getOwnPropertyDescriptor()` is your answer. It takes two arguments: the object itself and the property name as a string. ```js const myObj = { age: 30 }; const descriptor = Object.getOwnPropertyDescriptor(myObj, 'age'); console.log(descriptor); // Output: { value: 30, writable: true, enumerable: true, configurable: true } ``` Here, the `descriptor` object shows the default attributes: `value`, `writable`, `enumerable`, and `configurable`. No surprises, everything is set to `true` by default! 🎉 ### 2. Defining Descriptors with `Object.defineProperty()` Okay, ready to step it up? What if you want to create a property with custom attributes? Say hello to `Object.defineProperty()`. It takes three arguments: the object, the property name, and the descriptor object. ```js const person = {}; Object.defineProperty( person, 'name', { value: 'Alice', writable: false, enumerable: true, configurable: false } ); console.log(person.name); // Output: Alice ``` Now, [`person.name`](http://person.name) is read-only and not configurable but still enumerable. Try changing its value, and JavaScript will be like, "Nice try, buddy!" 🤨 ### 3. Batch Operation with `Object.defineProperties()` But wait, there's more! What if you want to define multiple properties at once? `Object.defineProperties()` has got your back. ```js const animal = {}; Object.defineProperties(animal, { name: { value: 'Dog', writable: true }, sound: { value: 'Woof', enumerable: false } }); ``` Here, `animal` gets two properties, each with its own set of descriptors. Efficient, isn't it? 🤓 ### 4. Computed property with `Object.defineProperty()` But wait, there's more again. What if you want to define a property that depends on other ones? `Object.defineProperty()` has got your back again. ```js const person = { firstName: 'John', lastName: 'Doe', }; // Define a read-only property Object.defineProperty(person, 'fullName', { get() { return `${this.firstName} ${this.lastName}`; }, enumerable: true, }); console.log(person.fullName); // John Doe ``` Here `person` has three properties, `firstName`, `lastName` and `fullName`. `fullName` depends on the others and it's enumerable so that it's included in the `for...in` loop and `Object.keys()` method. Ok, I think the idea now it's clear and you can use your imagination to combine these configurations to provide the best descriptors for your objects. To find out more don't waste my YouTube video on my [YouTube Channel](https://www.youtube.com/@Puppo_92) > 🎬 **Watch / View Embedded Resource:** [https://www.youtube.com/watch?v=AHLdUs8LtIA](https://www.youtube.com/watch?v=AHLdUs8LtIA) ## Conclusion In conclusion, Object Property Descriptors are a powerful feature of JavaScript that allows you to manipulate and define object properties with fine-grained control. This advanced feature offers developers the ability to create more dynamic and flexible code. Whether you're a junior developer or an expert, understanding Object Property Descriptors can help you level up your JavaScript skills. So go ahead, tinker with your code, challenge the norms, and keep pushing the boundaries. The JavaScript universe is your playground! 🚀 You can find the code of this article [in the GitHub repository](https://github.com/Puppo/javascript-you-dont-know/tree/07-object-descriptor). > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### JavaScript FinalizationRegistry Unraveled: Memory Management Magic Explained! - **Canonical URL**: https://delpuppo.net/blog/javascript-you-dont-know/javascript-finalizationregistry-unraveled-memory-management-magic-explained/ - **Article LLMs.txt**: https://delpuppo.net/blog/javascript-you-dont-know/javascript-finalizationregistry-unraveled-memory-management-magic-explained/llms.txt - **Published**: Oct 25, 2023 - **Series**: The JavaScript You Don't Know (https://delpuppo.net/blog/series/javascript-you-dont-know/) - **Tags**: javascript, memory, leak, js, FinalizationRegistry - **Summary**: Introduction JavaScript's memory management can be tricky, especially when it comes to... ## Introduction JavaScript's memory management can be tricky, especially when it comes to handling objects that are no longer needed. As developers, we often need to deal with memory leaks and other issues that arise from improper garbage collection. Fortunately, JavaScript has introduced a new feature called the FinalizationRegistry to help us manage memory more efficiently. In this blog post, we'll dive deep into the world of FinalizationRegistry, exploring its purpose, how to use it, and real-world scenarios where it can come in handy. ## A Simple Example To begin, let's take a look at a simple example to understand how FinalizationRegistry works. The FinalizationRegistry is an object that enables you to register a callback function that will be invoked when an object is garbage collected. Here's a basic example: ```js const registry = new FinalizationRegistry((heldValue) => { console.log(`Clean up: ${heldValue}`); }); function createObject() { const obj = {id: 1}; registry.register(obj, "Object 1"); } createObject(); // After some time, when the object is garbage collected, the console will log: "Clean up: Object 1" ``` In this example, we create a new FinalizationRegistry and provide a callback function that logs a message when the registered object is garbage collected. We then create an object and register it with the registry. ## Real-World Scenario Now, let's explore a more realistic scenario where FinalizationRegistry can be beneficial. Imagine we have a web application that allows users to upload and manipulate images. We use a third-party library to handle the image manipulation, and this library creates large objects that consume a significant amount of memory. To prevent memory leaks, we can use FinalizationRegistry to ensure that these objects are properly cleaned up when they are no longer needed. ```js const imageRegistry = new FinalizationRegistry((imageData) => { console.log(`Cleaning up image data: ${imageData}`); // Perform additional cleanup tasks, such as releasing resources associated with imageData }); function loadImage(imageUrl) { return fetch(imageUrl) .then((response) => response.blob()) .then((imageBlob) => { const imageData = new ImageData(imageBlob); imageRegistry.register(imageData, `Image: ${imageUrl}`); return imageData; }); } function manipulateImage(imageData) { // Use the third-party library to manipulate the image } loadImage('https://example.com/image.jpg') .then((imageData) => { manipulateImage(imageData); // After manipulation, the imageData object will eventually be garbage collected }); ``` In this example, we create an imageRegistry using FinalizationRegistry and register the ImageData objects when they are loaded. When an ImageData object is garbage collected, the provided callback function will log a message and perform any additional cleanup tasks required. ## Considerations While the FinalizationRegistry is a powerful tool for managing memory, there are some important considerations to keep in mind: 1. The timing of garbage collection is not guaranteed, and the callback function may not be invoked immediately after an object becomes unreachable. 2. FinalizationRegistry should not be used to manage critical resources, as it only provides a best-effort cleanup mechanism. 3. The held values in the registry should not be used to access the registered objects, as it may prevent them from being garbage collected. To find out more don't waste my YouTube video on my [YouTube channel](https://www.youtube.com/@Puppo_92) > 🎬 **Watch / View Embedded Resource:** [https://www.youtube.com/watch?v=3sgIFrjA61U](https://www.youtube.com/watch?v=3sgIFrjA61U) ## Conclusion The JavaScript FinalizationRegistry is a valuable addition to the language that can help developers manage memory more efficiently and prevent memory leaks. By understanding how it works and knowing when to use it, you can create more performant and memory-efficient applications. Keep in mind the considerations mentioned above, and happy coding! _You can find the code of this article_ [in the GitHub repository](https://github.com/Puppo/javascript-you-dont-know/tree/06-finalizationregistry)_._ > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### Unlocking the Power of Proxies: JavaScript's Secret Superheroes - **Canonical URL**: https://delpuppo.net/blog/javascript-you-dont-know/unlocking-the-power-of-proxies-javascripts-secret-superheroes/ - **Article LLMs.txt**: https://delpuppo.net/blog/javascript-you-dont-know/unlocking-the-power-of-proxies-javascripts-secret-superheroes/llms.txt - **Published**: Oct 18, 2023 - **Series**: The JavaScript You Don't Know (https://delpuppo.net/blog/series/javascript-you-dont-know/) - **Tags**: javascript, es6, proxy, proxies, js - **Summary**: JavaScript's Proxy object is a powerful feature that enables you to intercept and customize... JavaScript's Proxy object is a powerful feature that enables you to intercept and customize operations performed on objects. This versatile tool allows developers to create more efficient and flexible code while also improving code maintainability. In this article, we will dive into the world of JavaScript proxies, explore their use cases, and provide examples for expert developers. ## A Brief Introduction to Proxies in JavaScript A Proxy object in JavaScript is used to define custom behaviour for fundamental operations such as property lookup, assignment, enumeration, and function invocation. It acts as a wrapper around another object, called the target, and allows you to intercept and manipulate operations performed on the target object. ### Creating a JavaScript Proxy To create a JavaScript Proxy, you need two things: a target object and a handler object. The target object is the object you want to wrap with the Proxy, while the handler object defines the custom behaviour for the proxy. Here's a simple example of creating a Proxy: ```js const target = { name: "John", surname: "Doe", fullName: "John Doe" }; const handler = { get(_target, prop) { return _target[prop] ? _target[prop].toUpperCase() : "Property not found"; }, set(_target, prop, value) { if (prop === 'name') { _target.fullName = value + ' ' + _target.surname; } if (prop === 'surname') { _target.fullName = _target.name + ' ' + value; } if (prop === 'fullName') { throw new Error('fullName is not writable') } _target[prop] = value; return true; } }; const proxy = new Proxy(target, handler); console.log(proxy.fullName); // Output: "JOHN DOE" console.log(proxy.nonexistent); // Output: "Property not found" proxy.name = 'Jane'; console.log(proxy.fullName); // Output: "Jane Doe" proxy.surname = 'Smith'; console.log(proxy.fullName); // Output: "Jane Smith" proxy.fullName = 'Tom Smith'; // Error: fullName is not writable ``` In this example, we defined a `get` trap in the handler object, which intercepts the property lookup operation on the target object. When a property is accessed on the proxy object, the `get` trap is invoked, and it returns the property value in uppercase if the property exists or a custom message if it doesn't. Then, we defined a `set` trap, which intercepts a property's set. If the property name is `name` or `surname` calculates the `fullName` and sets the target object with the right value, but if you try to set the `fullName` property the code raises an error. ### Use Cases for Proxies in JavaScript #### Validation and Constraints Proxies can be used to enforce data validation and constraints when setting properties on an object. For example, you can ensure that a property value is a certain type or within a specific range. ```js const target = { age: 25 }; const handler = { set(_target, prop, value) { if (prop === "age" && (typeof value !== "number" || value < 0 || value > 150)) { throw new TypeError("Invalid age value"); } _target[prop] = value; return true; }, }; const proxy = new Proxy(target, handler); proxy.age = 30; // Works fine proxy.age = "thirty"; // Throws TypeError: "Invalid age value" ``` #### Logging and Profiling Proxies can be used to log and profile operations performed on an object, which can be helpful during debugging and performance analysis. ```js const target = { message: "Hello, World!" }; const handler = { get(_target, prop) { console.log(`Accessing property "${prop}"`); return _target[prop]; }, set(_target, prop, value) { console.log(`Setting property "${prop}" to "${value}"`); _target[prop] = value; return true; }, }; const proxy = new Proxy(target, handler); console.log(proxy.message); // Logs: "Accessing property "message"" and outputs: "Hello, World!" proxy.message = "Goodbye, World!"; // Logs: "Setting property "message" to "Goodbye, World!"" ``` To learn more don't miss my YouTube video about JavaScript Proxy on my [YouTube channel](https://www.youtube.com/@Puppo_92) > 🎬 **Watch / View Embedded Resource:** [https://www.youtube.com/watch?v=e7_lxSrP_Ok](https://www.youtube.com/watch?v=e7_lxSrP_Ok) ## Conclusion JavaScript's Proxy object is a powerful and versatile feature that allows developers to create more efficient and flexible code. By understanding and utilizing proxies, you can unlock their full potential and become a true JavaScript superhero. _You can find the code of this article_ [in the GitHub repository](https://github.com/Puppo/javascript-you-dont-know/tree/05-proxies)_._ > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### JavaScript WeakRef Explained: Harnessing Memory Management Magic - **Canonical URL**: https://delpuppo.net/blog/javascript-you-dont-know/javascript-weakref-explained-harnessing-memory-management-magic/ - **Article LLMs.txt**: https://delpuppo.net/blog/javascript-you-dont-know/javascript-weakref-explained-harnessing-memory-management-magic/llms.txt - **Published**: Oct 11, 2023 - **Series**: The JavaScript You Don't Know (https://delpuppo.net/blog/series/javascript-you-dont-know/) - **Tags**: javascript, es6, weakref, weakrefs, js - **Summary**: Introduction to WeakRef WeakRef is a powerful feature in JavaScript that allows developers... ## Introduction to WeakRef WeakRef is a powerful feature in JavaScript that allows developers to create weak object references. A weak reference is a reference that does not prevent the object it refers to from being garbage collected. This can be useful when you want to maintain a reference to an object without preventing it from being cleaned up by the garbage collector. ## How WeakRef Works WeakRef works by creating a weak reference to an object. This reference does not count towards the object's reference count and does not prevent it from being garbage collected. To access the object, you can call the `deref()` method on the WeakRef instance. If the object is still alive, the method will return the object; otherwise, it will return undefined. ## Implementing WeakRef in Your Code To use WeakRef in your code, follow these steps: 1. Create a new WeakRef instance by passing the object you want to reference as an argument: `const weakRef = new WeakRef(targetObject);` 2. To access the object, call the `deref()` method on the WeakRef instance: `const object = weakRef.deref();` 3. Check if the object is still alive by testing if it is not undefined: `if (object !== undefined) { /* object is still alive */ }` ## Use Cases for WeakRef WeakRef can be helpful in several scenarios, such as: 1. **Caching**: Use WeakRef to create a cache for expensive-to-create objects. When an object is no longer needed, it can be garbage collected automatically, freeing up memory. 2. **DOM Element References**: In web applications, you can use WeakRef to hold references to DOM elements. This can help prevent memory leaks when you need to keep track of elements for event handling or manipulation. 3. **Resource Cleanup**: When working with external resources like files or network connections, you can use WeakRef to keep track of those resources. If the resource becomes unused (e.g., a file is closed or a network connection is no longer needed), it can be automatically released. 4. **Memoization**: Implement memoization using WeakRef to store the results of function calls. This allows you to cache function results without preventing the input arguments from being garbage-collected when they're no longer needed. 5. **Managing Timers**: When using timers (e.g., setTimeout or setInterval), you can use WeakRef to hold references to objects associated with the timer. This can help ensure that the timer doesn't keep objects alive longer than necessary. 6. **Event Handling**: In event-driven applications, you can use WeakRef to manage event listeners. When an object with event listeners is no longer in use, the associated event listeners can be automatically removed. 7. **Reacting to DOM Node Removal**: In cases where you want to perform actions when DOM nodes are removed, you can use WeakRef to track DOM nodes. When a node is removed from the DOM, you can receive a notification and perform cleanup tasks. 8. **Custom Data Structures**: Create custom data structures that use WeakRef to hold references to elements. This can be particularly useful in scenarios like implementing caches or data structures with automatic cleanup. 9. **Managing Web Workers**: When working with Web Workers, you can use WeakRef to manage references to worker instances. When a worker is no longer needed, its reference can be automatically released. 10. **Optimizing Memory-Intensive Applications**: In memory-intensive applications, you can use WeakRef to ensure that large data structures or objects are only kept in memory as long as they're actively used. ## Best Practices and Limitations When using WeakRef, keep in mind the following best practices and limitations: 1. Use WeakRef only when necessary, as it can introduce complexity and potential performance issues. 2. Do not rely on the timing of garbage collection, as it may vary across JavaScript engines and environments. 3. Be aware that WeakRef is not supported in all environments, so you may need to provide a fallback implementation or polyfill. If you want to learn more about WeakRef, don't miss out on my YouTube video on [my YouTube channel](https://www.youtube.com/@Puppo_92) 🚀 > 🎬 **Watch / View Embedded Resource:** [https://youtu.be/-rN03KAHGC4](https://youtu.be/-rN03KAHGC4) ## Conclusion WeakRef is a powerful feature in JavaScript that allows you to create weak references to objects, enabling you to maintain references without preventing garbage collection. By understanding how WeakRef works and its use cases, you can use memory more efficiently in your applications and avoid memory leaks. [_Here is_](https://github.com/Puppo/javascript-you-dont-know/tree/04-weakrefs) _the source code of this post._ > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### Unlock the Power of JavaScript WeakSet: Mastering a Hidden Gem! - **Canonical URL**: https://delpuppo.net/blog/javascript-you-dont-know/unlock-the-power-of-javascript-weakset-mastering-a-hidden-gem/ - **Article LLMs.txt**: https://delpuppo.net/blog/javascript-you-dont-know/unlock-the-power-of-javascript-weakset-mastering-a-hidden-gem/llms.txt - **Published**: Oct 4, 2023 - **Series**: The JavaScript You Don't Know (https://delpuppo.net/blog/series/javascript-you-dont-know/) - **Tags**: javascript, es6, weakset, weaksets, es2015, js - **Summary**: In the vast landscape of JavaScript, many powerful features often go unnoticed. One such hidden gem... In the vast landscape of JavaScript, many powerful features often go unnoticed. One such hidden gem is the WeakSet, an unsung hero of the language that can help you optimize your code and manage memory more effectively. In this article, we'll dive deep into WeakSet, uncovering their potential and learning how to use them effectively in our JavaScript projects. ## A Brief Introduction to WeakSet WeakSet is a collection of objects similar to the more commonly used Set. However, there's a significant difference between the two: WeakSet holds only weak references to the objects stored within them. This means that if an object is only referenced by a WeakSet, it can still be garbage collected, freeing up valuable memory resources. This unique feature of WeakSet makes it an excellent choice for managing certain types of data relationships, mainly when memory management is a priority. ## Creating and Using WeakSet To create a WeakSet, instantiate a new instance of the WeakSet class: ```js const weakSet = new WeakSet(); ``` Adding objects to a WeakSet is easy. Just use the `add` method: ```js const obj1 = {}; const obj2 = {}; weakSet.add(obj1); weakSet.add(obj2); ``` WeakSets also provide methods for checking if an object is present (`has`) and removing objects (`delete`): ```js console.log(weakSet.has(obj1)); // true weakSet.delete(obj1); console.log(weakSet.has(obj1)); // false ``` However, unlike Sets, WeakSets do not have methods for iterating over their contents or determining their size. This is because of their weakly-referenced nature, which makes it impossible to know how many objects are still being held in memory. ## Use Cases for WeakSets ### 1. Managing DOM Elements `WeakSet` can be incredibly useful when working with the DOM. You can store references to DOM elements without worrying about memory leaks. When an element is removed from the DOM, the reference in the `WeakSet` will be automatically garbage collected. ```js const domElements = new WeakSet(); const element = document.querySelector('.my-element'); domElements.add(element); // Later, when the element is removed from the DOM domElements.has(element); // false (garbage collected) ``` ### 2. Private Data Storage `WeakSet` can be used to store private data associated with an object without exposing it. Since the references are weak, the data will be automatically removed when the object is no longer reachable. ```js const privateData = new WeakSet(); class MyClass { constructor() { privateData.add(this); } #data = 'I am private data'; getData() { if (privateData.has(this)) { return this.#data; } return undefined; } } ``` To see WeakSets in action, don't waste the opportunity to look at my YouTube video about them. > 🎬 **Watch / View Embedded Resource:** [https://youtu.be/m3BKqgeVAa4](https://youtu.be/m3BKqgeVAa4) ## In Conclusion WeakSet may not be the most well-known feature of JavaScript, but it offers unique capabilities that can be invaluable in certain situations. By understanding and embracing WeakSet, you can optimize your code, enhance memory management, and unlock the full potential of this hidden gem in the JavaScript universe. You can find the source code [in the GitHub repository](https://github.com/Puppo/javascript-you-dont-know/tree/03-weaksets) > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### Unlocking JavaScript's Secret Weapon: The Power of WeakMaps - **Canonical URL**: https://delpuppo.net/blog/javascript-you-dont-know/unlocking-javascripts-secret-weapon-the-power-of-weakmaps/ - **Article LLMs.txt**: https://delpuppo.net/blog/javascript-you-dont-know/unlocking-javascripts-secret-weapon-the-power-of-weakmaps/llms.txt - **Published**: Sep 27, 2023 - **Series**: The JavaScript You Don't Know (https://delpuppo.net/blog/series/javascript-you-dont-know/) - **Tags**: javascript, es6, weakmap - **Summary**: Introduction JavaScript is a powerful and versatile programming language that has evolved... ## Introduction JavaScript is a powerful and versatile programming language that has evolved over the years, introducing new features and improvements to enhance its performance and capabilities. One such feature is the WeakMap, a collection of key-value pairs that offers unique benefits regarding memory management and data privacy. This article will delve into the world of WeakMap in JavaScript, exploring their creation, usage, limitations, and real-world applications. ## Understanding WeakMap In JavaScript, a WeakMap is a type of Map that holds key-value pairs where the keys are objects, and the values can be any arbitrary data. The primary difference between a Map and a WeakMap is how they manage memory management. In a WeakMap, when a key object becomes inaccessible, the entry is automatically removed, allowing the garbage collector to reclaim memory efficiently. ## Creating and Using WeakMap To create a new WeakMap, use the `new WeakMap()` constructor. Once created, you can set values for the keys using the `set` method, as shown below: ```js const weakmap = new WeakMap(); const key = { name: "John Doe" }; weakmap.set(key, "Some value"); ``` To retrieve values associated with keys, use the `get` method: ```js const value = weakmap.get(key); // "Some value" ``` You can remove entries from the WeakMap using the `delete` method and check for the existence of a key using the `has` method: ```js weakmap.delete(key); // true weakmap.has(key); // false ``` ## Practical Applications of WeakMap WeakMap offers several practical applications in JavaScript: 1. Privacy and Encapsulation: By storing private data separately from the object, WeakMap can enhance privacy and encapsulation. This is particularly useful for classes and objects where internal data should not be exposed to the outside world. 2. Memory Management: WeakMap automatically releases unreferenced keys and associated values, allowing for efficient memory management in large applications. 3. Caching: WeakMap can implement caching mechanisms without causing memory leaks, as they automatically remove entries when keys are no longer accessible. ## Limitations of WeakMap Despite their benefits, WeakMap has some limitations: 1. Keys must be objects: Unlike Maps, WeakMap requires keys to be objects, not primitive values. 2. No built-in iteration methods: WeakMap does not have built-in methods for iterating over their entries, such as `forEach` or `entries`. 3. No `clear` method: There is no built-in method to clear the entire WeakMap. 4. Limited compatibility: WeakMap is not supported in some older browsers, limiting their compatibility. ## Real-world Examples and Use Cases WeakMap can be utilized in various real-world scenarios: 1. Event Listeners: When managing event listeners in DOM elements, WeakMap can help prevent memory leaks by automatically removing entries when elements are no longer accessible. 2. Private Data Storage: WeakMap can store private data in classes and objects, ensuring that internal data is not exposed to the outside world. 3. Large Applications: WeakMap can optimize memory usage in large applications by automatically releasing unreferenced data, improving performance and reducing memory leaks. _To find out more about WeakMap don't miss out on my Youtube video on my_ [_Youtube channel_](https://www.youtube.com/@puppo_92)_._ > 🎬 **Watch / View Embedded Resource:** [https://youtu.be/8Z4ZYDSxN3A](https://youtu.be/8Z4ZYDSxN3A) ## Conclusion In conclusion, WeakMap offers powerful features and applications for efficient memory management and data privacy in JavaScript. While they have some limitations, their benefits often outweigh these drawbacks, making them a valuable addition to a JavaScript developer's toolkit. As the language continues to evolve, future developments will likely address these limitations and further improve the usefulness of WeakMap in JavaScript applications. _The code of this article is available_ [in the GitHub repository](https://github.com/Puppo/javascript-you-dont-know/tree/02-weakmaps) > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### Unveiling the Magic: Exploring JavaScript Symbols - **Canonical URL**: https://delpuppo.net/blog/javascript-you-dont-know/unveiling-the-magic-exploring-javascript-symbols/ - **Article LLMs.txt**: https://delpuppo.net/blog/javascript-you-dont-know/unveiling-the-magic-exploring-javascript-symbols/llms.txt - **Published**: Sep 20, 2023 - **Series**: The JavaScript You Don't Know (https://delpuppo.net/blog/series/javascript-you-dont-know/) - **Tags**: javascript, es6, symbols, js - **Summary**: Introduction to JavaScript Symbols JavaScript Symbols are a new primitive data type... ## Introduction to JavaScript Symbols JavaScript Symbols are a new primitive data type introduced in ECMAScript 6 (ES6). They are used as unique, non-string identifiers for object properties, useful in avoiding naming collisions and enabling more advanced programming techniques. > 🎬 **Watch / View Embedded Resource:** [https://youtu.be/o8YDHAS-fo0](https://youtu.be/o8YDHAS-fo0) ## Creating and Understanding Symbols Symbols are created using the `Symbol()` function, which can take an optional description as an argument. Each symbol is unique; even if two symbols have the same description, they are considered distinct. ```js const propSymbol = Symbol("propName"); const propSymbol2 = Symbol("propName"); propSymbol === propSymbol2 // false ``` ## Unique Identifiers and Property Keys Symbols are often used as property keys in objects to ensure uniqueness and prevent naming collisions. They can be used alongside string keys without conflict, and their uniqueness makes them ideal for use as metadata or private properties in objects. ```js const propSymbol = Symbol("propName"); const propSymbol2 = Symbol("propName"); const obj = { [propSymbol]: "propSymbol", [propSymbol2]: "propSymbol2" } ``` ## The Global Symbol Registry The global Symbol registry allows you to create and share symbols across different scopes and realms. To create a global symbol, use `Symbol.for(key)`, which takes a string as its argument. This method checks if a symbol with the same key exists in the registry; if not, it creates a new one. ```js // my-module-1.js export const propSymbol = Symbol("propName"); export const propSymbolShare = Symbol.for("propName"); // my-module-2.js export const propSymbol = Symbol("propName"); export const propSymbolShare = Symbol.for("propName"); // index.js import { propSymbol as propSymbolM1, propSymbolShare as propSymbolShareM1 } from './my-module.js' import { propSymbol as propSymbolM2, propSymbolShare as propSymbolShareM2 } from './my-module-2.js' console.log(propSymbolM1 === propSymbolM2); // false console.log(propSymbolM1 === propSymbolShareM1); // false console.log(propSymbolM2 === propSymbolShareM2); // false console.log(propSymbolShareM1 === propSymbolShareM2); // true ``` It's also possible to retrieve the key of a Symbol using `Symbol.keyFor(key)` but only for shared keys. ```js export const propSymbol = Symbol("propName"); export const propSymbolShare = Symbol.for("propName"); console.log(Symbol.keyFor(propSymbol)); // undefined console.log(Symbol.keyFor(propSymbolShare)); // propName ``` ## Symbols and Iteration Symbols are not enumerable, which means they are not included in standard iteration methods like `for...in` loops or `Object.keys()`. To access an object's symbol properties, use `Object.getOwnPropertySymbols()`. ```js const propSymbol = Symbol("propName"); const propSymbol2 = Symbol("propName"); const obj = { [propSymbol]: "propSymbol", [propSymbol2]: "propSymbol2" } console.log(JSON.stringify(obj)); // {} console.log(Object.keys(obj)); // [] console.log(Object.getOwnPropertyNames(obj)); // [] for (const key in obj) { console.log(key); // nothing } console.log(Object.getOwnPropertySymbols(obj)); // [Symbol(propName), Symbol(propName)] ``` ## Well-Known Symbols in JavaScript Well-known symbols are predefined global symbols that represent common behaviours in JavaScript, such as `Symbol.iterator`, `Symbol.asyncIterator`, `Symbol.toStringTag`, and `Symbol.species`. These symbols can be used to customize the behaviour of built-in JavaScript objects and classes. ## Use Cases and Practical Applications Symbols can be used to implement private properties, metadata, or methods, avoiding naming collisions in libraries and frameworks. They are also used to customize the behaviour of well-known JavaScript objects and classes, such as iterators and string conversion. ## Limitations and Considerations Symbols are not supported in older JavaScript environments, and their unique nature may require additional care when working with serialization or proxy objects. Additionally, they are not directly accessible during iteration, which may require extra steps to access their properties. ## Conclusion JavaScript Symbols provide a powerful, unique identifier for object properties, helping to avoid naming collisions and enabling advanced programming techniques. Understanding their use cases and limitations is essential for modern JavaScript developers. _You can find the source code_ [in the GitHub repository](https://github.com/Puppo/javascript-you-dont-know/tree/01-symbols) > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### JavaScript Async Generators Unleashed: Harnessing Asynchronous Power - **Canonical URL**: https://delpuppo.net/blog/js-iterators-generators/javascript-async-generators-unleashed-harnessing-asynchronous-power/ - **Article LLMs.txt**: https://delpuppo.net/blog/js-iterators-generators/javascript-async-generators-unleashed-harnessing-asynchronous-power/llms.txt - **Published**: Sep 15, 2023 - **Series**: JavaScript Iterators & Generators (https://delpuppo.net/blog/series/js-iterators-generators/) - **Tags**: javascript, generator, generators, async, js, asynchronous - **Summary**: In the dynamic realm of JavaScript, the ability to efficiently handle asynchronous operations is... In the dynamic realm of JavaScript, the ability to efficiently handle asynchronous operations is paramount for building responsive and robust applications. While Async Iterators have already proven their worth in managing asynchronous data streams, JavaScript takes another leap forward with Async Generators. Imagine a world where you can effortlessly produce a continuous stream of asynchronous data while dynamically controlling its flow. This is precisely where Async Generators excel, revolutionizing the way we handle and manipulate asynchronous sequences. In this blog post, we're diving deep into the captivating world of JavaScript Async Generators. We'll unveil how Async Generators empower developers to create elegant solutions for complex asynchronous scenarios. Whether you're grappling with real-time data synchronization, asynchronous data processing, or any scenario demanding fine-tuned control over data flow, join us on this journey as we unravel the capabilities, nuances, and transformative power of JavaScript Async Generators. Before we delve into Async Generators, let's recap the basics of asynchronous programming. In JavaScript, asynchronous tasks are crucial for handling time-consuming operations without blocking the main thread. Functions like setTimeout, AJAX requests, and Promises have become commonplace in managing asynchronous operations. Generators, denoted by `function*` syntax, are a powerful feature introduced in ES6. They allow us to pause and resume the execution of a function, providing a valuable mechanism for lazy evaluation. The `yield` keyword within a generator function lets us yield values individually, pausing the function's execution until the next value is requested. If you want to learn more, there is relative content about them in this series. Now, imagine combining the magic of asynchronous operations with the versatility of generators. Enter Async Generators! These supercharged functions enable us to work with asynchronous data streams more intuitively and elegantly. An async generator function is declared using `async function*` syntax. Inside, the `yield` keyword can be combined with `await` to handle asynchronous operations gracefully. This means we can yield promises, fetch data and seamlessly integrate it into our data stream. The real beauty of async generators lies in their ability to simplify complex asynchronous tasks. They're particularly handy when dealing with data streams from sources like databases, APIs, or even real-time event streams. Imagine processing a continuous stream of sensor data or handling multiple concurrent requests without drowning in callback hell! Let's take a look at an example ```ts async function* getUsers(ids: number[]) { for (const id of ids) { console.log("getUsers next"); const data = fetch(`https://reqres.in/api/users/${id}`).then(res => res.json() ); yield data; } } ``` Using a `for-of` loop, the function iterates over the IDs and fetches the data for every user on every iteration. However, using the yield, stop the execution and resume it only when the caller asks for the next value. Like Async Iterators, also Async Generators can be iterated using a `for-await-of` loop. ```ts for await (const user of getUsers([1, 2, 3, 4, 5])) { console.log(user); } ``` Error handling and completion are crucial aspects of asynchronous programming. With async generators, we can catch errors using try-catch blocks within the generator function. Plus, we can signal the end of data using the 'return' statement, closing the loop on our data stream. ```ts try { for await (const user of getUsers([1, 2, 3, 4, 5])) { console.log(user); } } catch (err) { console.error(err); } async function* getUsers(ids: number[]) { for (const id of ids) { console.log("getUsers next"); const data = fetch(`https://reqres.in/api/users/${id}`).then(res => res.json() ); if (id > 4) return data; // <-- Return yield data; } } ``` Like Generators, also Async Generators can be combined. For instance, we can create an Async Generator `map` function that accepts an Async Generator and a transformation function and returns a new async generator with the transformations. ```ts async function* map( asyncIterable: AsyncIterable, callback: (value: T) => U ) { for await (const item of asyncIterable) { yield callback(item); } } ``` Asynchronous programming in JavaScript continues to evolve, and Async Generators are a testament to its dynamic nature. With their power to elegantly manage complex asynchronous tasks, async generators offer a glimpse into the future of streamlined coding. So, fellow developers, embrace this innovative tool and unlock new possibilities in your projects. I also created a video about Async Generators on my [Youtube channel](https://www.youtube.com/@Puppo_92/) and you can find it below. > 🎬 **Watch / View Embedded Resource:** [https://youtu.be/0uQvNZ8th08](https://youtu.be/0uQvNZ8th08) That's a wrap for today's discussion on JavaScript Async Generators. I hope you found this exploration insightful and inspiring. Remember, staying curious and open to new concepts is the key to mastering your craft in the ever-evolving programming landscape. If you enjoyed this post, like and share it. And if you have questions, feedback or doubts let me know in the comments. Until next time, happy coding and thanks for reading 👩💻👨💻 N.B. you can find the code of this post [in the GitHub repository](https://github.com/Puppo/javascript-iterators-and-generators/tree/04-async-generators). > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### Mastering JavaScript Async Iterators: Unlocking Asynchronous Magic - **Canonical URL**: https://delpuppo.net/blog/js-iterators-generators/mastering-javascript-async-iterators-unlocking-asynchronous-magic/ - **Article LLMs.txt**: https://delpuppo.net/blog/js-iterators-generators/mastering-javascript-async-iterators-unlocking-asynchronous-magic/llms.txt - **Published**: Sep 8, 2023 - **Series**: JavaScript Iterators & Generators (https://delpuppo.net/blog/series/js-iterators-generators/) - **Tags**: javascript, iterators, async, asynchronous, iterator, js - **Summary**: In the ever-evolving landscape of JavaScript, staying up-to-date with its latest features is crucial... In the ever-evolving landscape of JavaScript, staying up-to-date with its latest features is crucial for writing efficient and modern code. One such feature that has garnered significant attention is the Async Iterator. While iterators have long been an integral part of JavaScript for sequential data processing, the introduction of asynchronous programming patterns brought about the need for asynchronous iteration. Imagine effortlessly traversing through data streams that might involve fetching data from APIs, reading from files, or any other asynchronous data source. This is precisely where Async Iterators shine, providing a seamless and elegant solution to handle such scenarios. In this blog post, we'll delve into the world of JavaScript Async Iterators, exploring their fundamentals, understanding their benefits, and uncovering how they can be a game-changer in writing robust asynchronous code. Whether you're a seasoned developer looking to expand your skill set or a newcomer curious about advanced JavaScript techniques, this blog post is for you. We will unravel the power of Async Iterators and take your asynchronous programming skills to new heights. Before we jump into the code, let's understand what async iterators are. In JavaScript, iterators are objects that allow us to loop over collections. Async iterators take this concept a step further by allowing us to handle asynchronous operations, like fetching data from APIs or reading from streams. Creating an async iterable is simple. We use the `Symbol.asyncIterator` to define the async iterator method inside an object. This method will return an object with the `next` method that resolves a promise containing the `next` value in the asynchronous sequence. Let's take a look at an example. ```ts const getUsers = (ids: number[]): AsyncIterable => { return { [Symbol.asyncIterator]() { let i = 0; return { async next() { console.log("getUsers next"); if (i === ids.length) { return { done: true, value: null }; } const data = await fetch( `https://reqres.in/api/users/${ids[i++]}` ).then(res => res.json()); return { done: false, value: data }; }, }; }, }; }; ``` Imagine you have a list of IDs and want to read the user data only if needed. Using AsyncIterators, you can create a function that handles the API and returns the result of every request on every iteration, making the code more transparent. To consume the values of an async iterable, we use the `for-await-of` loop. This loop works just like the regular `for-of` loop, but it's designed specifically for asynchronous iterables. ```ts for await (const user of getUsers([1, 2, 3, 4, 5])) { console.log(user); } ``` Error handling is crucial when dealing with asynchronous operations. Async iterators allow us to handle errors using `try-catch` blocks around the `for-await-of` loop. ```ts try { for await (const user of getUsers([1, 2, 3, 4, 5])) { console.log(user); } } catch (err) { console.error(err); } ``` AsyncIterator runs code only if needed, so until you don't call the next method, nothing happens, like for Iterators. The `return` method exists also for AsyncIterators. This method is used in case the code doesn't complete all the iterations. Imagine the loop calls a break or a return; in this case, JavaScript under the hood calls the `return` method for us. In this method, we can handle whatever we need. We may need to reset something or check the current value of the iterator. ```ts const getUsers = (ids: number[]): AsyncIterable => { return { [Symbol.asyncIterator]() { let i = 0; return { ... async return() { console.log("getUsers return"); return { done: true, value: null }; }, }; }, }; }; ``` Async Iterators are powerful like Iterators, and we can create functions that accept an AsyncIterator and manipulate it to return another Async Iterator. For instance, we can create a map function that accepts an Async Iterator and returns another with a callback specified by the user. ```ts function map(iter: AsyncIterable, fn: (v: T) => U): AsyncIterable { return { [Symbol.asyncIterator]() { const iterator = iter[Symbol.asyncIterator](); return { async next() { console.log("map next"); const { done, value } = await iterator.next(); if (done) return { done, value: null }; return { done, value: fn(value) }; }, async return() { console.log("map return"); if (iterator?.return) await iterator?.return(); return { done: true, value: null }; }, }; }, }; } ``` These functions have all the benefits said before. Javascript does nothing until the codebase doesn't ask for the next function; the same is true for the `return` method, and now you can compose the `getUsers` with the `map` to build a new Async Iterator. ```ts const iterator = map(getUsers([1, 2, 3, 4, 5]), user => user.data.id) for await (const num of iterator) { if (num === 3) break; console.log(num); } ``` And there you have it a deep dive into the world of asynchronous iterators in JavaScript. They provide an elegant solution to working with asynchronous data streams, making your code more organized and efficient. Experiment with async iterators in your projects, and you'll be amazed at how they simplify complex asynchronous workflows. I also created a video on my [Youtube channel](https://www.youtube.com/@Puppo_92/), that you can find below. > 🎬 **Watch / View Embedded Resource:** [https://youtube.com/watch?v=0_mzZ9QOebg](https://youtube.com/watch?v=0_mzZ9QOebg) If you found this content helpful, like and share it. And if you have any questions, feedback, or doubts, let me know in the comments 😀 Thanks for reading! And Happy coding! 👩💻 👨💻 N.B. you can find the code of this post [in the GitHub repository](https://github.com/Puppo/javascript-iterators-and-generators/tree/03-async-iterators). > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### Unleashing the Power: Demystifying JavaScript Generators - **Canonical URL**: https://delpuppo.net/blog/js-iterators-generators/unleashing-the-power-demystifying-javascript-generators/ - **Article LLMs.txt**: https://delpuppo.net/blog/js-iterators-generators/unleashing-the-power-demystifying-javascript-generators/llms.txt - **Published**: Sep 1, 2023 - **Series**: JavaScript Iterators & Generators (https://delpuppo.net/blog/series/js-iterators-generators/) - **Tags**: javascript, generators, generator, js - **Summary**: Welcome to this blog post, where we're diving into the fascinating world of JavaScript generators. If... Welcome to this blog post, where we're diving into the fascinating world of JavaScript generators. If you're looking to take your coding skills to the next level and explore a powerful tool for managing operations, you've come to the right place. In the fast-paced landscape of web development, managing tasks and handling complex flows of data can be quite challenging. This is where JavaScript generators come into play. These remarkable constructs introduce a new level of control and flexibility, allowing developers to write code that can be paused, resumed, and iterated step by step. In this blog post, we'll take you on a journey through the core concepts of JavaScript generators. We'll unravel their syntax, explore their capabilities, and showcase examples of how they can simplify your code and make it more manageable. By the end of this read, you'll not only understand the ins and outs of generators but also be equipped with the knowledge to start using them effectively in your own projects. Buckle up as we embark on this exploration of JavaScript generators, unravelling their potential and discovering how they can revolutionize the way you write code. Whether you're a seasoned developer or just starting with JavaScript, there's something valuable here for you. Let's begin! A generator in JavaScript is a function that, after the function keyword, has a `*`. It's not possible to create generators in JavaScript using arrow functions. Generators are special functions that implement the Iterable interface very simply; you can learn more about them in this blog post. The particularity of generators is the `yield` keyword. Using the `yield`, we can return a new value to the iteration but without ending the function execution, simply putting it on pause, until the next request for the next value and until it's not completed. ```ts function* range(start: number, end: number): Iterable { let n = start; while (n <= end) { console.log("range next"); yield n++; } } ``` As you can notice, the range generator is a simple generator function, and it's understandable from the `*` near-function keyword. Using a simple while, the function checks if the current value is less or equal to the end value and if true, using the `yield` returns the value; else exists from the while and closes the generator. Like iterators, generators only run once someone calls for the `next` value. It's possible to go through a generator using a simple for-of-loop. ```ts for (const num of range(1, 10)) { console.log(num); } ``` Using the Iterable interface ```ts const iterator = range(1, 10)[Symbol.iterator](); while (true) { const { done, value } = iterator.next(); if (done) break; console.log(value); } ``` Or using the spread operator to save all the values in an Array. ```ts for (const num of [...range(1, 10)]) { console.log(num); } ``` It's important to remember that if you use the spread operator, you will lose all the generators' benefits because it runs the generator until it's not completed to copy all the values in the array. Like iterators also, generators can be combined. For instance, we can create a map generator function that accepts an iterator and a transformation function and returns the computation inside the transformation with the value emitted by the generator. And that's the result. ```ts function* map( iterable: Iterable, callback: (value: T) => U ): Iterable { for (const value of iterable) { yield callback(value); } } const mapRange = map(range(1, 10), value => value * 10); for (const num of mapRange) { console.log(num); } ``` That's all folks. You can find a video about them on my [Youtube channel](https://www.youtube.com/@Puppo_92/). > 🎬 **Watch / View Embedded Resource:** [https://youtu.be/mi2JlrXBRIs](https://youtu.be/mi2JlrXBRIs) In conclusion, JavaScript generators stand as a remarkable tool in the arsenal of every developer striving for more efficient and flexible programming. Through their unique ability to pause and resume execution, generators provide an innovative solution to managing complex flows of data and operations. Throughout this blog post, we've journeyed through the syntax, use cases, and benefits of JavaScript generators. We've seen how they can simplify code, making it more readable and maintainable. By harnessing the power of generators, you can transform the way you approach programming challenges. These constructs not only offer a structured approach to handling tasks but also contribute to writing code that is less error-prone and easier to reason about. As you venture into the realm of generators, remember that practice is key. Experiment with different scenarios, explore their interplay with other techniques and gradually integrate them into your projects. With time, you'll become proficient in leveraging generators to create responsive and efficient applications. So, whether you're a front-end developer aiming to tame complex UI interactions or a back-end engineer dealing with intricate data processing, generators offer a fresh perspective on tackling programming. Embrace their power, refine your skills, and elevate your coding prowess in the dynamic world of JavaScript. Thank you for reading and happy generating! 👩💻 👨💻 😜 N.B. you can find the code of this post [in the GitHub repository](https://github.com/Puppo/javascript-iterators-and-generators/tree/02-generators). > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### Iterate Like a Pro: Mastering JavaScript Iterators for Effortless Code - **Canonical URL**: https://delpuppo.net/blog/js-iterators-generators/iterate-like-a-pro-mastering-javascript-iterators-for-effortless-code/ - **Article LLMs.txt**: https://delpuppo.net/blog/js-iterators-generators/iterate-like-a-pro-mastering-javascript-iterators-for-effortless-code/llms.txt - **Published**: Aug 25, 2023 - **Series**: JavaScript Iterators & Generators (https://delpuppo.net/blog/series/js-iterators-generators/) - **Tags**: javascript, iterators, iterator, js - **Summary**: Welcome to this blog post, where we're about to unravel the power and versatility of JavaScript... Welcome to this blog post, where we're about to unravel the power and versatility of JavaScript iterators. If you've ever found yourself working with data collections and wondered if there's a more efficient and elegant way to navigate them, you're in the right place. In the realm of modern web development, efficiently managing and manipulating data is a crucial skill. This is where JavaScript iterators come into play, offering a systematic approach to traversing various data structures such as arrays, maps, and sets. Whether you're a beginner eager to understand the basics or an experienced developer looking to fine-tune your code, grasping the concept of iterators can significantly enhance your programming journey. In this blog post, we'll embark on a comprehensive exploration of JavaScript iterators. We'll break down their fundamental principles, explore practical examples of their usage, and demonstrate how they can simplify complex data operations. By the time you finish reading, you'll not only understand iterators inside and out but also be equipped to leverage their potential to write more concise, readable, and efficient code. So, whether you're striving to make your codebase more elegant or seeking to optimize your data manipulation techniques, join us as we delve into the world of JavaScript iterators. From novice to expert, there's something here for everyone. Let's dive in! Built-in in Javascript exists many structures that implement the Iterators pattern, for instance: Array, Set and Map. In Javascript, an object to be iterable must implement the Iterable interface. But what is the Iterable interface? First, to be iterable, an object must have a `next` method. This method must return two properties: `done` and `value`. Done is used to detect if the iteration is completed; instead, the value contains the current value. Last but not least, if you want your object to become an iterator, you must expose the iterable interface in the `Symbol.iterator` of your object, like in this example. ```ts const array = [1, 2, 3, 4, 5]; const iterator = array[Symbol.iterator](); for (let result = iterator.next(); !result.done; result = iterator.next()) { console.log(result.value); } ``` For example, here is a range function implemented as an iterator. ```ts const range = (start: number, end: number): Iterable => { return { [Symbol.iterator]() { let n = start; return { next() { console.log("range next"); if (n > end) { return { done: true, value: null }; } return { done: false, value: n++ }; }, }; }, }; }; ``` As you can notice, this function accepts two numbers, start and end and returns a new object with a single property, in this case, the iterator property. Then, inside this function, there is the next function that, on every call, checks if the current value is greater than the end and, if true, returns a new object with done as true and value as null; else returns an object with done false and value with the current value. The beautiful thing about iterator is that javascript only does something once you ask for the next value. Every iterator can be iterated using the for-of loop ```ts for (const num of range(1, 10)) { console.log(num); } ``` or using its native method, so calling the `Symbol.iterator` function and then using the next method and checking the done property if it is true or not. ```ts const rangeIterator = range(1, 10)[Symbol.iterator](); for (let result = rangeIterator.next(); !result.done; result = rangeIterator.next()) { console.log(result.value); } ``` It's possible to copy all the iterator values in an Array using the spread operator too. ```ts for (const num of [...range(1, 10)]) { console.log(num); } ``` Iterator also has another method, the `return` method. This method is used in case the code doesn't complete the iteration. Imagine the loop call a break or a return; in this case, JavaScript under the hood calls the `return` method for us. In this method, we can handle whatever we need. We may need to reset something or check the current value of the iterator. ```ts const range = (start: number, end: number): Iterable => { return { [Symbol.iterator]() { let n = start; return { next() { console.log("range next"); if (n > end) { return { done: true, value: null }; } return { done: false, value: n++ }; }, return() { console.log("range return"); return { done: true, value: null }; }, }; }, }; }; for (const num of range(1, 10)) { if (num > 5) break; console.log(num); } ``` Iterators are powerful, and we can also create functions that accept an iterator and manipulate it to return another iterator. For instance, we can create a map function that accepts an iterator and returns another with a callback specified by the user. ```ts function mapIterable( iterable: Iterable, callback: (value: T) => U ): Iterable { return { [Symbol.iterator]() { const iterator = iterable[Symbol.iterator](); return { next() { console.log("mapIterable next"); const { done, value } = iterator.next(); if (done) { return { done: true, value: null }; } return { done, value: callback(value) }; }, return() { console.log("mapIterable return"); if (iterator.return) { iterator.return(); } return { done: true, value: null }; }, }; }, }; } ``` All the info said before is true also for this new iterator. JavaScript does nothing until the codebase doesn't ask for the next value; the same is true for the return method, and now you can compose the range iterators with the map iterator to build a new one. ```ts const mapRange = mapIterable(range(1, 10), value => value * 10); for (const num of mapRange) { if (num > 50) break; console.log(num); } ``` Ok folks, that's all! If you want to dive deep with Iterators, don't waste my video on my [Youtube Channel](https://www.youtube.com/@Puppo_92/). > 🎬 **Watch / View Embedded Resource:** [https://www.youtube.com/watch?v=BSctSkMGtiM](https://www.youtube.com/watch?v=BSctSkMGtiM) In conclusion, understanding and utilizing JavaScript iterators can greatly enhance your ability to work with collections of data in a more elegant and efficient manner. With iterators, you can streamline your code, improve its readability, and reduce memory consumption by processing data one element at a time. This powerful concept empowers developers to implement custom iteration behaviour, making their code more adaptable to different scenarios. By grasping the fundamentals of iterators, such as the `next()` method and the concept of iterable objects, you open the door to more sophisticated programming techniques and design patterns. Whether you're working with arrays, maps, sets, or other data structures, iterators provide a standardized way to traverse through data and perform operations without unnecessary complexity. As the JavaScript language continues to evolve, iterators remain a foundational concept that plays a crucial role in modern programming paradigms. Incorporating iterators into your coding arsenal equips you with a versatile tool for managing data, ultimately leading to cleaner, more maintainable, and more robust code. So, as you embark on your journey to master JavaScript iterators, remember that they are more than just technical features; they represent a shift in how you approach and solve problems in your code. With practice and exploration, you'll be able to wield iterators effectively, making your codebase more efficient and your development experience more enjoyable. Thanks for reading and Happy coding! 😃 👩💻 👨💻 N.B. you can find the code of this article [in the GitHub repository](https://github.com/Puppo/javascript-iterators-and-generators/tree/01-iterators)! > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### 🏆 My journey to the MVP Award 🏆 - **Canonical URL**: https://delpuppo.net/blog/my-journey-to-the-mvp-award/ - **Article LLMs.txt**: https://delpuppo.net/blog/my-journey-to-the-mvp-award/llms.txt - **Published**: Jun 9, 2023 - **Tags**: mvp, award, microsoft, journey, Awards - **Summary**: This history started many years ago! I was a student, and I was going to the ITS in Pordenone. I... This history started many years ago! I was a student, and I was going to the [ITS](https://www.itsaltoadriatico.it/) in Pordenone. I spent two years of my life in that school, where I met great friends and learned a lot from awesome teachers, one of them is [Andrea Dottor](https://www.dottor.net/). In the first lesson with him, he started with his presentation, "I'm a freelancer, I work with different companies and blah blah blah... I'm a Microsoft MVP too". That was the first time that I heard about this program. As you can imagine, I was 20, and I didn't get any importance to that tense, but in reality, it was the first moment that I came across the MVP program. After that, Andrea and I became colleagues and friends. I always see Andrea as a Mentor, and I have always followed him. So I started to go with him to the [xedotnet](https://www.xedotnet.org/) meetup. I met other incredible developers like [Mirco Vanini](https://mvp.microsoft.com/en-us/PublicProfile/4039714?fullName=Mirco%20Vanini) (another MVP), Daniele Morosinotto and many others there. Thanks to xe, I learned what a community is and how a community works. I started to understand the benefit of sharing knowledge in the development environment, and my mind started to build a strange idea "One day you will be like them, maybe not an MVP but a speaker yes, and you will share your knowledge with the others." ![Andrea and I during a running by night](https://cdn.hashnode.com/res/hashnode/image/upload/v1682441754145/597ab828-4c01-48b9-96fb-ec31b8ec82fb.jpeg) I was young at that moment and decided to continue to follow the community, but I put my effort into improving myself in my job. For almost 8 years, I dedicated my time to learn stuff in my bedroom and in my free time, developing different projects in my daily routine, and I changed 3 jobs too. When I started to work in Flowing, in my mind something happened. Flowing was an environment with awesome developers, and many of them were and are speakers too. But I was not ready to do my first talk, so I tried to think of something different that could be useful for the community. After some days, I decided to write my first series about Typescripts. Why typescript? It is my favorite language with javascript, and many times, I saw people fighting with it. So I said: "Ok, this is the right topic, let's go!". 21 articles compose my first series, " **Typescript - Tips & Tricks Series**", which you can find [in this reference](https://dev.to/puppo/series/11213). In reality, the audience at the beginning was not so much, but after some months, [Lars](https://twitter.com/LayZeeDK) contacted me to ask if I would join the "[This is learning](https://dev.to/this-is-learning)" community, and as you can imagine, I accepted :) Here I also met another good man: [Santosh](https://twitter.com/SantoshYadavDev), who runs with Lars "This is learning" community. During that period, another crazy and good friend had a strange idea. I organized and took as "Teacher" a workshop about Nx in Flowing, and he was invited as an attendee. He appreciated the workshop so much that he invited me to his youtube channel to speak about Nx ([Nx the return](https://www.youtube.com/watch?v=hS_2Okd-CkM)). The name of the friend is [Francesco Sciuti](https://mvp.microsoft.com/en-us/PublicProfile/5003974?fullName=Francesco%20Sciuti), another MVP and also the Batman of the developers. In that period, I was also writing a new series called "[RxJS - Getting Started](https://dev.to/puppo/series/13743)", 15 articles to help developers to be more confident with [RxJS](https://rxjs.dev/). Why this topic? Because I worked on many Angular projects and saw many developers stuck or completely unfamiliar with it. ![Francesco and I at GDP Pescara](https://cdn.hashnode.com/res/hashnode/image/upload/v1682441809835/0f5ef1a1-13b9-4368-8da1-288453d3b001.jpeg) After that, a colleague and friend ([Francesco Strazzullo](https://www.linkedin.com/in/francescostrazzullo/)) tried to push me out of my comfort zone, and he asked me to be a speaker at the [Mug](https://marcausergroup.it/) meetup. I was a bit scared; speaking in front of many people created a strange sensation in me, but after some sane pushing, I said yes! So the 7th of October 2021, I did my first talk called [Stay Reactive Stay Quiet with RxJS](https://marcausergroup.it/eventi/2021/10/07/stay-reactive-stay-quiet-with-rxjs.html). The sensations at the end were really good, so on the 19th of October, I did the second at the [FEVR](https://www.fevr.it/) community called "[Is it possible to build your UI components using only web components?](https://www.fevr.it/eventi/2021/10/is-it-possible-to-build-your-ui-components-using-only-web-components/)" (thanks to [Matteo Guidotto](https://www.linkedin.com/in/matteoguidotto/) for this talk). It was the COVID period, so taking talks and doing conferences in 2021 was really difficult. For this reason, I continued to write another series called: [It's Prisma Time](https://dev.to/puppo/series/15827). At the beginning of 2022, the decisive turning point. During a chat with Andrea, I told him: "I would become a Microsoft MVP; what have I to do to achieve it?" I had this idea for some months, but I wanted to concretize it at that moment. He looked at me and said, "It's time to do some great conferences and maybe speak more about some Microsoft technologies". So I went back home, and with the euphoria in my blood, I started to write some articles about [Visual Studio Code](https://dev.to/puppo/series/16773). These articles spoke about some common commands for me when I worked with VsCode, but these articles were awesome for the community! Many and many people didn't know these commands, and the articles received a lot of appreciation. Another thing I did was research conferences and check out conferences where I could send a CFP. I remember I sent so many CFPs that I forgot how many noes I received 😅 But at the beginning of September, the first confirmations arrived: [AngularDay 2022](https://2022.angularday.it/talks_speakers/), then the [workshop](https://2022.reactjsday.it/workshop/workshop_Unit-Test.html) at the react day with [Fabio Biondi](https://mvp.microsoft.com/en-us/PublicProfile/5003526?fullName=Fabio%20Biondi), another MVP and good friend that shared with me also a great weekend at the GDP Pescara, where I talked about [Prisma](https://devfest.gdgpescara.it/sessions/304) and last but not least the [ngrome](https://www.youtube.com/watch?v=h_Faplx37yk). During this period, I also continued to do meetups, thanks to [Milano Frontend](https://www.meetup.com/it-IT/milano-front-end/) and [React JS Milano](https://www.meetup.com/it-IT/react-js-milano/) having me as a speaker. If you are curious, you can find the list of all my talks and contributions [in this reference](https://mvp.microsoft.com/en-us/PublicProfile/5005133?fullName=Luca%20Del%20Puppo). ![GDP Pescare with friends](https://cdn.hashnode.com/res/hashnode/image/upload/v1682441933231/b8126fb5-db35-437d-abe6-83ba82da18e4.jpeg) In the middle of December 2022, I spoke again with Andrea to ask him if he could nominate me as Microsoft MVP. He did it, and I spent an evening filling out the form of all my contributions during the last year. Why last year? Because the MVP Award is based on the last year. I started my history 10 years ago, yes, but the MVP Award takes into consideration only the last year. I decided to tell you the entire story because I know the last year is only the cherry on top, but without the previous nine, the last year would probably be a failure. As you can imagine, after the filling, you have to wait for the evaluation, which takes at most 3 months. I was lucky; on the 3rd of January 2023, in my inbox appeared an email that said: "Congratulation! You are a Microsoft MVP 2023"! Now, I suppose you can imagine how much happy I was, I think I stayed euphory for the following two or three days 😅 Then after some weeks, I did a call with [Cristina](https://www.linkedin.com/in/crisgonz/). She takes care of all the MVPs on the Meditterian side and explained to me how the program works, its benefits and so on. She is really kind and she really put a lot of effort into helping the MVPs. Thanks Cristina, for all the things you do for us! 🫶 ![Juri and I at angularday IT](https://cdn.hashnode.com/res/hashnode/image/upload/v1682442061423/c0122998-9147-45fe-b802-ae312657339f.jpeg) Now my journey is only at the beginning. I have different ideas for continuing. I opened my own [Youtube channel,](https://www.youtube.com/@Puppo_92) I'll continue to write articles, and I'll continue to do talks at conferences and meetups. **What did I learn from this story?** The MVP award is "only" a simple and beautiful award that helps me remember all the effort I put in these years to improve myself and try to help others! And if you want to join the program: love communities, and put your effort into helping them! Do this, and you'll be on the right path to being part of the MVP Family. Remember: **improve yourself, help others and follow your passion!** ![Illustration for 🏆 My journey to the MVP Award 🏆](https://cdn.hashnode.com/res/hashnode/image/upload/v1682165702062/bd016870-0d47-4e22-a171-b2c78fddbb9a.jpeg) _Photo by [Vlad Bagacian](https://unsplash.com/@vladbagacian?utm_source=Devto&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=devto&utm_medium=referral)_ --- ### React Query - Infinite Queries - **Canonical URL**: https://delpuppo.net/blog/react-query/react-query-infinite-queries/ - **Article LLMs.txt**: https://delpuppo.net/blog/react-query/react-query-infinite-queries/llms.txt - **Published**: Jun 7, 2023 - **Series**: React Query (https://delpuppo.net/blog/series/react-query/) - **Tags**: react, reactquery, reacthooks - **Summary**: Hey Folks, Today it's time to learn how you can build an infinite query with React Query. To build... Hey Folks, Today it's time to learn how you can build an infinite query with React Query. To build an infinite query you have to use the `useInfiniteQuery` hook. This hook is similar to the `useQuery` but with some more properties to handle the infinite loading in the best way. The key concepts, if you are working with `useInfiniteQuery` are: - **Key** : in this case, the key is unique for each page, `useInfiniteQuery` handles all the pages inside its state - **page** : each chunk of your infinite query is represented as a page and the first page returned by the `useInfiniteQuery` is undefined. This helps you to understand if it's the first iteration or not. - **hasNextPage** : is a variable returned by the hook and its scope is to determine if another chunk exists or not - **fetchNextPage** : is a function used to fetch the next chunk - **isLoading** : is a flag that determines if there is a loading ongoing (The query has no data yet) - **isFetching** : is a flag that determines if there is a fetch request ongoing - **getNextPageParam** : this function is an option to pass at the hook and it is used to determine if there is another chunk or not after the response ok, let's see the code to be clearer ```ts import { useInfiniteQuery } from '@tanstack/react-query'; import { useState } from 'react'; const fetchTodos = async ( page: number, limit: number, signal: AbortSignal | undefined ): Promise<{ totals: number; todos: Todo[]; }> => { const response = await fetch(`api/tasks?_page=${page + 1}&_limit=${limit}`, { signal, }); if (!response.ok) { throw new ResponseError('Failed to fetch todos', response); } const todos: Todo[] = await response.json(); const totals = Number.parseInt( response.headers.get('x-total-count') || '0', 10 ); return { totals, todos, }; }; interface UseTodos { todos: Todo[]; isLoading: boolean; isFetching: boolean; error?: string; hasNext: boolean; next?: () => void; } export const useTodos = (): UseTodos => { const [limit] = useState(5); const { data, hasNextPage, fetchNextPage, isLoading, isFetching, error } = useInfiniteQuery( [QUERY_KEY.todos], ({ signal, pageParam: page = 0 }) => fetchTodos(page, limit, signal), { refetchOnWindowFocus: false, retry: 2, getNextPageParam: (lastPage, pages) => { if (Math.ceil(lastPage.totals / limit) > pages.length) return pages.length; return undefined; }, } ); return { todos: data?.pages.flatMap(({ todos }) => todos) || [], isLoading, isFetching, error: mapError(error), next: fetchNextPage, hasNext: hasNextPage || false, }; }; ``` As you can notice, the flow is very simple, the hook returns the current page or chunk, you have to handle the fetch request and then you can determine the result and if there is another chunk or not. Another important thing to keep in mind is that the result of the `useInfiniteQuery` is an array and each element of the array contains the data of each chunk. Ok, I suppose you have an idea of how Infinite Queries work in React Query, but if you want to deep into it don't miss my youtube about it. > 🎬 **Watch / View Embedded Resource:** [https://www.youtube.com/watch?v=F1KD4GY1iws](https://www.youtube.com/watch?v=F1KD4GY1iws) I think thats all from this article. This is the last post of this series, I hope you enjoyed the content and now you are aware of using ReactQuery in your React application. See you soon folks Bye Bye 👋 p.s. you can find the code of the video [in the GitHub repository](https://github.com/Puppo/learning-react-query/tree/12-infinitive-scroll) _Photo by [Rahul Mishra](https://unsplash.com/@rahuulmiishra?utm_source=Devto&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=devto&utm_medium=referral)_ > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### React Query - Paginated List - **Canonical URL**: https://delpuppo.net/blog/react-query/react-query-paginated-list/ - **Article LLMs.txt**: https://delpuppo.net/blog/react-query/react-query-paginated-list/llms.txt - **Published**: Jun 1, 2023 - **Series**: React Query (https://delpuppo.net/blog/series/react-query/) - **Tags**: react, reactquery, reacthooks - **Summary**: Hey Folks, Today it's time to learn how you can paginate a list with ReactQuery. It's common to... Hey Folks, Today it's time to learn how you can paginate a list with ReactQuery. It's common to create a pagination of a list to improve the user interface of your platform if you are building a list in your application, but also to limitate the work in your API. Using ReactQuery, building a paginate list is a piece of cake. You have to follow 3 steps: 1. Create a state to save the value of the current page 2. Create a state to save the limit of the pages (the number of elements on every page) 3. Fetch the data every time the user changes one of the previous states. But let's see an example that is easier to understand ```ts const fetchTodos = async ( page: number, limit: number, signal: AbortSignal | undefined ): Promise<{ totals: number; todos: Todo[]; }> => { const response = await fetch(`api/tasks?_page=${page}&_limit=${limit}`, { signal, }); if (!response.ok) { throw new ResponseError('Failed to fetch todos', response); } const todos: Todo[] = await response.json(); const totals = Number.parseInt( response.headers.get('x-total-count') || '0', 10 ); return { totals, todos, }; }; interface UseTodos { todos: Todo[]; isLoading: boolean; isFetching: boolean; error?: string; pages: number; page: number; setPage: Dispatch>; } export const useTodos = (): UseTodos => { const [page, setPage] = useState(1); const [limit] = useState(5); const { data: { todos, totals } = { todos: [], totals: 0, }, isLoading, isFetching, error, } = useQuery( [QUERY_KEY.todos, page, limit], ({ signal }) => fetchTodos(page, limit, signal), { refetchOnWindowFocus: false, retry: 2, } ); return { todos, isLoading, isFetching, error: mapError(error), pages: Math.ceil(totals / limit), page, setPage, }; }; ``` As you can notice, the process to build a pagination with ReactQuery is very simple. The fetch request has to contain the page and the limit (you can handle a pagination also with limit and offset if you want) and in your `useQuery` you have to handle some simple stuff. First, the key of your query must include the page and limit too, second you must pass page and limit to your fetch request. As you can see, building a paginated list with ReactQuery is really a piece of cake, but if want to dive into it don't miss my youtube video about it > 🎬 **Watch / View Embedded Resource:** [https://www.youtube.com/watch?v=Ma7-hdNMdWs](https://www.youtube.com/watch?v=Ma7-hdNMdWs) I think thats all from this article; I hope you enjoyed this content! See you soon folks Bye Bye 👋 p.s. you can find the code of the video [in the GitHub repository](https://github.com/Puppo/learning-react-query/tree/11-paginate-result) _Photo by [Rahul Mishra](https://unsplash.com/@rahuulmiishra?utm_source=Devto&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=devto&utm_medium=referral)_ > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### React Query - Abort Request - **Canonical URL**: https://delpuppo.net/blog/react-query/react-query-abort-request/ - **Article LLMs.txt**: https://delpuppo.net/blog/react-query/react-query-abort-request/llms.txt - **Published**: May 17, 2023 - **Series**: React Query (https://delpuppo.net/blog/series/react-query/) - **Tags**: react, reactquery, reacthooks - **Summary**: Hey Folks, Today it's time to learn how you can abort an ongoing request with ReactQuery. Before... Hey Folks, Today it's time to learn how you can abort an ongoing request with ReactQuery. Before moving to the example, I want to introduce the [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal). AbortSignal is a javascript interface used to abort ongoing methods. Typically, you can create an AbortSignal by an [AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController), which simplifies the creation and execution of the AbortSignal. When you create an AbortController (`controller = new AbortController()`) you get an instance of it that has two core concepts: the signal (`controller.signal`) and the abort method (`controller.abort()`). The first one is important because it contains the signal with its status, and the second one is important because it allows you to change the status of the abort from `false` to `true`. Using these two interfaces, in javascript, you can handle out-of-the-box the abort of an HTTP request created with the [fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) but also with [axios](https://axios-http.com) if you want. When you create a request, both accept a signal and if you abort that signal you can abort the request. AbortSignal has also a static method called `timeout` that accepts a number that describes the timeout time in milliseconds. Using this method you can create easily a signal that changes its status after some time. Very common if you want to handle a timeout of your request. Just a quick example to make you understand the power of these interfaces. ```ts try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 5000); const res = await fetch(url, { signal: controller.signal }); const body = await res.json(); } catch (e) { if (e.name === "AbortError") { // Notify the user of abort. // Note this will never be a timeout error! } else { // A network error, or some other problem. console.log(`Type: ${e.name}, Message: ${e.message}`); } } finally { clearTimeout(timeoutId); } ``` In this example, you can see an abort request created with an AbortController. This example uses a setTimeout method to handle the timeout and as you can notice, when the code creates the fetch API passes the signal status to the fetch. In this way, the fetch request and the signal are linked together. This example could be simplified in this way using the AbortSignal timeout method. ```ts try { const res = await fetch(url, { signal: AbortSignal.timeout(5000) }); const body = await res.json(); } catch (e) { if (e.name === "AbortError") { // Notify the user of abort. // Note this will never be a timeout error! } else { // A network error, or some other problem. console.log(`Type: ${e.name}, Message: ${e.message}`); } } ``` Ok, now you have an idea of how AbortSignal and AbortController work, so we can move on to the next step; how can you abort a request with ReactQuer? Good news for you, ReactQuery out-of-the-box handles a signal for you for every query and mutation, and your business it's only decided if you want to use it or not. Let's start with an example ```ts const fetchTodos = async (signal: AbortSignal | undefined): Promise => { const response = await fetch('api/tasks', { signal, }); if (!response.ok) { throw new ResponseError('Failed to fetch todos', response); } return await response.json(); }; interface UseTodos { todos: Todo[]; isLoading: boolean; isFetching: boolean; error?: string; setUserFilter: Dispatch>; } export const useTodos = (): UseTodos => { const client = useQueryClient(); const { data: todos = [], isLoading, isFetching, error, } = useQuery([QUERY_KEY.todos], ({ signal }) => fetchTodos(signal), { refetchOnWindowFocus: false, retry: 2 }); return { todos, isLoading, isFetching, error: mapError(error), setUserFilter, }; }; ``` As you can notice, `useQuery` has a second parameter that contains different options, one of them is the signal. This signal is an AbortSignal that you can use to abort your request if you want. In the example, for instance, the signal is passed to the `fetchTodos` method and then passed to fetch request. As you can understand, aborting a request is a piece of cake 🍰 Ok, I think now you have all the notions to abort an HTTP request in javascript and in ReactQuery too. If you want to go dive into the Abort Request with React query don't miss my Youtube channel 🚀 > 🎬 **Watch / View Embedded Resource:** [https://www.youtube.com/watch?v=BL_E7SrymWI](https://www.youtube.com/watch?v=BL_E7SrymWI) I think thats all from this article; I hope you enjoyed this content! See you soon folks Bye Bye 👋 p.s. you can find the code of the video [in the GitHub repository](https://github.com/Puppo/learning-react-query/tree/10-abort-request) _Photo by [Rahul Mishra](https://unsplash.com/@rahuulmiishra?utm_source=Devto&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=devto&utm_medium=referral)_ > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### Improve user experience with optimistic update - **Canonical URL**: https://delpuppo.net/blog/react-query/improve-user-experience-with-optimistic-update/ - **Article LLMs.txt**: https://delpuppo.net/blog/react-query/improve-user-experience-with-optimistic-update/llms.txt - **Published**: May 11, 2023 - **Series**: React Query (https://delpuppo.net/blog/series/react-query/) - **Tags**: react, reactquery, reacthooks - **Summary**: Hey Folks, Sometimes to improve the user experience you can decide to bet on the success of your... Hey Folks, Sometimes to improve the user experience you can decide to bet on the success of your code, so you can assume that the code will go in the right way to make your application faster in the eyes of your users. This approach is called Optimistic Update and can be handled in some lines of code using react query with the `useMutation` hook. Yes, this approach is like a bet with the code but it's also important to keep in mind to handle the errors in case of failure. To handle the optimistic update you have to handle an `useMutation` hook in your codebase. This hook exposes three events to handle to perform the optimistic update: `onMutate`, `onSuccess` and the `onError`. The `onMutate` event is called immediately when your code calls the `useMutation` hook. This event is used to create a snapshot of the current state before moving forward and updating the state with the new values. It's important to save the previous state because it permits you in case of failure to restore it in the future if needed. The `onSuccess` event is called in case of success of the mutation. If your code jumps in this event, you are safe and everything has gone in the right way. The `onError` event is called in case of failure of the mutation. If your code is inside of this event unfortunately something went wrong. In this case, you must handle the restoration of the state to the previous one and notify the user that something went wrong. To help you to understand better the case let's see an example ```ts import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useSnackbar } from 'notistack'; const editTodoRequest = async (todo: Todo): Promise => { await new Promise((resolve) => setTimeout(resolve, 3000)); const response = await fetch(`api/tasks/${todo.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(todo), }); if (!response.ok) { throw new ResponseError(`Failed to edit todo with id ${todo.id}`, response); } return await response.json(); }; type UseEditTodo = (todo: Todo) => void; export const useEditTodo = (): UseEditTodo => { const { enqueueSnackbar, closeSnackbar } = useSnackbar(); const client = useQueryClient(); const { mutate: editTodo } = useMutation< Todo, unknown, Todo, { oldTodos?: Todo[]; oldTodo?: Todo; messageKey: string | number; } >(editTodoRequest, { onMutate: (todo) => { const messageKey = enqueueSnackbar('Todo edited', { variant: 'success', }); const oldTodos = client.getQueryData([QUERY_KEY.todos]); const oldTodo = client.getQueryData([QUERY_KEY.todos, todo.id]); client.setQueryData([QUERY_KEY.todos, todo.id], todo); client.setQueryData([QUERY_KEY.todos], (oldTodos) => oldTodos?.map((oldTodo) => (oldTodo.id === todo.id ? todo : oldTodo)) ); return { oldTodos, oldTodo, messageKey, }; }, onSuccess: () => { client.invalidateQueries([QUERY_KEY.todos]); }, onError: (error, todo, ctx) => { if (!ctx) return; const { oldTodos, oldTodo, messageKey } = ctx; closeSnackbar(messageKey); const errorMessage = mapError(error); enqueueSnackbar( `Ops! There was an error on editing todo: ${errorMessage}`, { variant: 'error', } ); client.setQueryData([QUERY_KEY.todos, todo.id], oldTodo); client.setQueryData([QUERY_KEY.todos], oldTodos); }, }); return editTodo; }; ``` As you can notice, in the mutate event, the code creates a toast for the user, which indicates that everything has gone in the right way and after that, the code takes the current state, returns it and update the state with the new values. The return is important because it permits getting this data in the `onError` event in case of failure. The `onError` event, as you can notice, handles the restoration of the state, removes the success toast and shows a new one with the error message. This part is crucial if you want to handle an optimistic update because it gives feedback to the user in case of failure. Last but not least, you can also handle the `onSuccess` event if you want to invalidate a query after the mutation or for some other motivation. The `onSuccess` is not required always but depends on your case. Ok, I think now you have an idea of how the optimistic update works in react query, but if you want to dive into it check out my youtube video too. > 🎬 **Watch / View Embedded Resource:** [https://www.youtube.com/watch?v=IwkhNxIyyzA](https://www.youtube.com/watch?v=IwkhNxIyyzA) I think thats all from this article; I hope you enjoyed this content! See you soon folks Bye Bye 👋 p.s. you can find the code of the video [in the GitHub repository](https://github.com/Puppo/learning-react-query/tree/09-optimistic-update) _Photo by [Rahul Mishra](https://unsplash.com/@rahuulmiishra?utm_source=Devto&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=devto&utm_medium=referral)_ > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### React Query - Keys & Prefetching - **Canonical URL**: https://delpuppo.net/blog/react-query/react-query-keys-prefetching/ - **Article LLMs.txt**: https://delpuppo.net/blog/react-query/react-query-keys-prefetching/llms.txt - **Published**: May 3, 2023 - **Series**: React Query (https://delpuppo.net/blog/series/react-query/) - **Tags**: react, reactquery, reacthooks - **Summary**: Hey Folks, Today I want to share two important things if you are using ReactQuery. The first is how... Hey Folks, Today I want to share two important things if you are using ReactQuery. The first is how the keys are composed, and the second is how to prefetch data to reduce the user's wasted time. So let's start 🚀 ## Keys As you have already learned in this series, React query uses the keys to save the data of a specific API. And these keys must be unique for each specific API. When you create a `useQuery` hook, the first parameter is the query's key, which is an array. This array is used to compose the key, and it is in charge of you creating the key in the right way for your case. As said before, the key is an array, and in this way, you can create a key composed of different data. So, for instance, you can have a key for the list of the todos and a key for the detail of a specific todo (`["todos"]` and `["todo", 2]`.) As you can notice, you can put in the key whatever you want, string number or boolean; it is completely on your side; the important thing you must remember every time is that each key must be unique. This concept, and the key you choose, are also important if you want to prefetch the data because you must use the same key to save the data prefetched in the react query state. ## Prefetching In some scenarios, loading the data before the user asks for it could be interesting. This improves the user experience and reduces the waste time for them. But how can you do that with react query? First, you need to use the `QueryClient` object that exposes the `prefetchQuery` method. This method accepts two parameters: the query's key and the fetch request. In this way, you can handle a simple but powerful prefetching flow with few lines of code. Let's take a look at this example ```ts export const useTodos = (): UseTodos => { const client = useQueryClient(); const [userFilter, setUserFilter] = useState(null); const filterTodoByAssignee = useCallback( (todos: Todo[]) => { if (!userFilter) return todos; return todos.filter((todo) => todo.assigneeId === userFilter); }, [userFilter] ); const { data: todos = [], isLoading, isFetching, error, } = useQuery([QUERY_KEY.todos], fetchTodos, { refetchOnWindowFocus: false, retry: 2, select: filterTodoByAssignee, onSuccess: (data) => { data.forEach((todo) => { client.prefetchQuery([QUERY_KEY.todos, id], () => fetchTodo(id)); }); }, }); return { todos, isLoading, isFetching, error: mapError(error), setUserFilter, }; }; ``` As you can notice, in this example in the `onSuccess` hook, the prefetch feature starts to load the data of every todo in the result of the list. That allows React Query to save the data in the storage and reuse them in the future when the application asks for that data. For instance, in a todo detail hook like this ```ts const fetchTodo = async (id: Todo['id']): Promise => { const response = await fetch(`api/tasks/${id}`); if (!response.ok) { throw new ResponseError(`Failed to fetch todo with id ${id}`, response); } return await response.json(); }; export const useGetTodoById = (id: Todo['id']): UseTodo => { const { data: todo = null, isLoading, error, } = useQuery([QUERY_KEY.todos, id], () => fetchTodo(id), { refetchOnMount: false, refetchOnWindowFocus: false, retry: 2, }); return { todo, isLoading, error: mapError(error), }; }; ``` As you can see, the key of this `useQuery` is the same as the key in the prefetch example, and in this way, ReactQuery is able to understand if the data are already loaded or not. As you can imagine, with this simple feature, you can improve the quality of your application and maybe prefetch some data that could take too long to load. Ok, I think you have a great idea of how key and prefetching work in React Query. To learn more about them, check out also my Youtube video available here 👇 > 🎬 **Watch / View Embedded Resource:** [https://www.youtube.com/watch?v=svvx-pl_hTE](https://www.youtube.com/watch?v=svvx-pl_hTE) I think thats all from this article; I hope you enjoyed this content! See you soon folks Bye Bye 👋 p.s. you can find the code of the video [in the GitHub repository](https://github.com/Puppo/learning-react-query/tree/08-query-key-and-prefetch-data) _Photo by [Rahul Mishra](https://unsplash.com/@rahuulmiishra?utm_source=Devto&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=devto&utm_medium=referral)_ > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### React Query - Enable Query - **Canonical URL**: https://delpuppo.net/blog/react-query/react-query-enable-query/ - **Article LLMs.txt**: https://delpuppo.net/blog/react-query/react-query-enable-query/llms.txt - **Published**: Apr 26, 2023 - **Series**: React Query (https://delpuppo.net/blog/series/react-query/) - **Tags**: react, reactquery, reacthooks - **Summary**: Hey folks, Do you know that you can enable or disable a query in react query? Noooo! Ok, it's time to... Hey folks, Do you know that you can enable or disable a query in react query? Noooo! Ok, it's time to learn it! Some specific hooks must be enabled only if there are some data in the application; for instance, some queries must run only if the users are logged in. To solve this problem, the `useQuery` hooks accept an option called `enabled` that accepts a boolean to determine if a condition is true or false. Let's take a look at this example ```ts import { useQuery } from '@tanstack/react-query'; import { QUERY_KEY } from 'src/app/constants/queryKeys'; import { Todo } from 'src/app/models'; import { User, useUser } from '../../../../auth/useUser'; import { ResponseError } from '../../../../utils/Errors/ResponseError'; const fetchTodos = async (user: User): Promise => { const response = await fetch(`api/tasks?assigneeId=${user.user.id}`, { headers: { Authorization: `Bearer ${user.accessToken}`, }, }); if (!response.ok) { throw new ResponseError('Failed to fetch my todos', response); } return await response.json(); }; interface UseMyTodos { todos: Todo[]; error?: string; } function mapError(error: unknown | undefined): undefined | string { if (!error) return undefined; if (error instanceof ResponseError) return error.response.statusText; if (error instanceof Error) return error.message; return 'Unknown error'; } export const useMyTodos = (): UseMyTodos => { const { user } = useUser(); const { data: todos = [], error } = useQuery( [QUERY_KEY.myTodos], () => fetchTodos(user!), { refetchOnWindowFocus: false, retry: 2, enabled: !!user, } ); return { todos, error: mapError(error), }; }; ``` As you can notice, in this case, a custom hook calls the API and gets all the todos assigned to the current users. The key point of this example stands in the enabled option. As you can see, this `useQuery` is called only if there is a user; else, the query is stopped. This feature permits developers to be sure to call a specific API only if a condition is satisfied, preventing a bad API request. This feature is pretty simple but very powerful, as you can imagine! To find out more about the `enabled` option take a look at my youtube video about it > 🎬 **Watch / View Embedded Resource:** [https://www.youtube.com/watch?v=5JSD_b2slJs](https://www.youtube.com/watch?v=5JSD_b2slJs) Ok, that's all from the enabled option. I hope you enjoyed this content! See you soon folks Bye Bye 👋 p.s. you can find the code of the video [in the GitHub repository](https://github.com/Puppo/learning-react-query/tree/07-enable-query) _Photo by [Rahul Mishra](https://unsplash.com/@rahuulmiishra?utm_source=Devto&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=devto&utm_medium=referral)_ > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### React Query - Filter Your Data - **Canonical URL**: https://delpuppo.net/blog/react-query/react-query-filter-your-data/ - **Article LLMs.txt**: https://delpuppo.net/blog/react-query/react-query-filter-your-data/llms.txt - **Published**: Apr 19, 2023 - **Series**: React Query (https://delpuppo.net/blog/series/react-query/) - **Tags**: react, reactquery, reacthooks - **Summary**: Hey folks, Did you know that you can filter your data in react query? Noooo! Ok, it's time to learn... Hey folks, Did you know that you can filter your data in react query? Noooo! Ok, it's time to learn it! To filter the data in your `useQuery` hook, you have to handle the `select` option. This option accepts a function that is used to filter the data. The function has one parameter, and this parameter contains the result of the query; the scope of the select function is to manipulate the query's result and returns a new result that respects some rules. Let's take a look at this example ```ts import { useQuery } from '@tanstack/react-query'; import { Dispatch, SetStateAction, useCallback, useState } from 'react'; import { QUERY_KEY } from 'src/app/constants/queryKeys'; import { Todo } from 'src/app/models'; import { ResponseError } from '../../../../utils/Errors/ResponseError'; const fetchTodos = async (): Promise => { const response = await fetch('api/tasks'); if (!response.ok) { throw new ResponseError('Failed to fetch todos', response); } return await response.json(); }; interface UseTodos { todos: Todo[]; isLoading: boolean; isFetching: boolean; error?: string; setUserFilter: Dispatch>; } function mapError(error: unknown | undefined): undefined | string { if (!error) return undefined; if (error instanceof ResponseError) return error.response.statusText; if (error instanceof Error) return error.message; return 'Unknown error'; } export const useTodos = (): UseTodos => { const [userFilter, setUserFilter] = useState(null); const filterTodoByAssignee = useCallback( (todos: Todo[]) => { if (!userFilter) return todos; return todos.filter((todo) => todo.assigneeId === userFilter); }, [userFilter] ); const { data: todos = [], isLoading, isFetching, error, } = useQuery([QUERY_KEY.todos], fetchTodos, { refetchOnWindowFocus: false, retry: 2, select: filterTodoByAssignee, }); return { todos, isLoading, isFetching, error: mapError(error), setUserFilter, }; }; ``` As you can notice, this example has some key points to analyse. First, the `filterTodoByAssignee` function is the select function passed to the `useQuery` hook. This function checks the `userFilter` state, and if it is empty, the function returns the entire list; else, it filters the result and returns only the todos that have the `assigneeId` that matches with the `userFilter` value. It's important, in this case, to use the `useCallback` hook and pass to it the right dependencies to prevent a decrease in the performance of the application. Then another important thing is the `setUserFilter` function that it's used to set the `userFilter` value. This function is exposed by the custom hook to permit the users to change the filter. And that's all; in these two simple steps, you have a `select` function to filter the dataset got from the query. You can use this solution also to create a client-side pagination in your react query application (for the server-side pagination, there will be a post in the future) If you want to find out more, check my youtube video about this topic > 🎬 **Watch / View Embedded Resource:** [https://www.youtube.com/watch?v=H4l1QAA4Dxw](https://www.youtube.com/watch?v=H4l1QAA4Dxw) Ok, that's all from the select option! I hope you enjoyed this content! See you soon folks Bye Bye 👋 p.s. you can find the code of the video [in the GitHub repository](https://github.com/Puppo/learning-react-query/tree/06-query-filters) _Photo by [Rahul Mishra](https://unsplash.com/@rahuulmiishra?utm_source=Devto&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=devto&utm_medium=referral)_ > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### React Query - Authentication Flow - **Canonical URL**: https://delpuppo.net/blog/react-query/react-query-authentication-flow/ - **Article LLMs.txt**: https://delpuppo.net/blog/react-query/react-query-authentication-flow/llms.txt - **Published**: Apr 12, 2023 - **Series**: React Query (https://delpuppo.net/blog/series/react-query/) - **Tags**: react, reactquery, reacthooks - **Summary**: Every application should handle an authentication flow; in this article, you'll learn how to build an... Every application should handle an authentication flow; in this article, you'll learn how to build an authentication flow in your React Application with React Query. ## Sign Up The first step to build an authentication flow is the sign-up action. As you have already learned in this series, you should build a mutation to do this action. A possible solution could be this ```ts async function signUp(email: string, password: string): Promise { const response = await fetch('/api/auth/signup', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }) }) if (!response.ok) throw new ResponseError('Failed on sign up request', response); return await response.json(); } type IUseSignUp = UseMutateFunction export function useSignUp(): IUseSignUp { const queryClient = useQueryClient(); const navigate = useNavigate(); const { enqueueSnackbar } = useSnackbar(); const { mutate: signUpMutation } = useMutation( ({ email, password }) => signUp(email, password), { onSuccess: (data) => { // TODO: save the user in the state navigate('/'); }, onError: (error) => { enqueueSnackbar('Ops.. Error on sign up. Try again!', { variant: 'error' }); } }); return signUpMutation } ``` By creating a mutation like that, you build the signUp in a very simple and clear way. Now using the `useSignUp` hook, you can get the mutation and call the signUp request to create a new user in your system. As you can notice, the code is pretty simple; the `signUp` method calls the API to post the new user's data and return the user data saved in the database. Then using the `useMutation` hook, you can build the mutation to handle the signUp action. If everything goes ok, the `onSuccess` hook calls the navigation to the home page; otherwise, the `onError` hook shows a toast with an error. In the code, there is a TODO that indicates something missing; we'll get back to this line in the future of this post. ## Sign In The second step to build if you are building an authentication flow is SignIn. In this case, SignIn is pretty similar to SignUp; the only things that change are the endpoint and the scope of the hook. So the code can be this ```ts async function signIn(email: string, password: string): Promise { const response = await fetch('/api/auth/signin', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }) }) if (!response.ok) throw new ResponseError('Failed on sign in request', response); return await response.json(); } type IUseSignIn = UseMutateFunction export function useSignIn(): IUseSignIn { const queryClient = useQueryClient(); const navigate = useNavigate(); const { enqueueSnackbar } = useSnackbar(); const { mutate: signInMutation } = useMutation( ({ email, password }) => signIn(email, password), { onSuccess: (data) => { // TODO: save the user in the state navigate('/'); }, onError: (error) => { enqueueSnackbar('Ops.. Error on sign in. Try again!', { variant: 'error' }); } }); return signInMutation } ``` I don't want to spend much time describing this hook because it is very similar to the SignUp but only with the references for the SignIn. Also in this case, there is a TODO that we'll remove in the future of the post. ## The user The core part of an authentication flow is where you save the user in the state. To do that, in this case, the best way is to create a new hook called `useUser` which is the owner of the user data. The `useUser` hook must have the user's data, and it has to save the user's data in the local storage and retrieve them when the user refreshes the page or gets back in the future. Let's start with the code that handles the local storage. Typically, this code is created with small functions with a specific goal like the next. ```ts import { User } from './useUser'; const USER_LOCAL_STORAGE_KEY = 'TODO_LIST-USER'; export function saveUser(user: User): void { localStorage.setItem(USER_LOCAL_STORAGE_KEY, JSON.stringify(user)); } export function getUser(): User | undefined { const user = localStorage.getItem(USER_LOCAL_STORAGE_KEY); return user ? JSON.parse(user) : undefined; } export function removeUser(): void { localStorage.removeItem(USER_LOCAL_STORAGE_KEY); } ``` In this way, you can create a small module that handles all the local storage functions for the user. Now it's time to see how you can build the `useUser` hook. Let's start with the code ```ts async function getUser(user: User | null | undefined): Promise { if (!user) return null; const response = await fetch(`/api/users/${user.user.id}`, { headers: { Authorization: `Bearer ${user.accessToken}` } }) if (!response.ok) throw new ResponseError('Failed on get user request', response); return await response.json(); } export interface User { accessToken: string; user: { email: string; id: number; } } interface IUseUser { user: User | null; } export function useUser(): IUseUser { const { data: user } = useQuery( [QUERY_KEY.user], async (): Promise => getUser(user), { refetchOnMount: false, refetchOnWindowFocus: false, refetchOnReconnect: false, initialData: userLocalStorage.getUser, onError: () => { userLocalStorage.removeUser(); } }); useEffect(() => { if (!user) userLocalStorage.removeUser(); else userLocalStorage.saveUser(user); }, [user]); return { user: user ?? null, } } ``` The `getUser` function is simple; it provides the HTTP request to get the user info; if the user is null, return null otherwise, it calls the HTTP endpoint. The `useQuery` hook is similar to the others seen before, but there are two new configurations to understand. - **refetchOnMount** : this option is important to prevent the hook reloads the data each time it is used - **initialData** : this option is used to load the data from the local storage; the initialData accepts a function that returns the initial value; if the initial value is defined, react query uses this value to refresh the data. Now you have all the blocks of the authentication flow, but it's time to link `useSignUp` and `useSignIn` with the `useUser` hook. Using the [QueryClient](https://tanstack.com/query/v4/docs/react/reference/QueryClient) you can set the data of a specific query by the [setQueryData](https://tanstack.com/query/v4/docs/react/reference/QueryClient#queryclientsetquerydata) function. So the previous TODOs comments change in this way ```ts export function useSignUp(): IUseSignUp { const queryClient = useQueryClient(); const navigate = useNavigate(); const { enqueueSnackbar } = useSnackbar(); const { mutate: signUpMutation } = useMutation( ({ email, password }) => signUp(email, password), { onSuccess: (data) => { queryClient.setQueryData([QUERY_KEY.user], data); navigate('/'); }, onError: (error) => { enqueueSnackbar('Ops.. Error on sign up. Try again!', { variant: 'error' }); } }); return signUpMutation } export function useSignIn(): IUseSignIn { const queryClient = useQueryClient(); const navigate = useNavigate(); const { enqueueSnackbar } = useSnackbar(); const { mutate: signInMutation } = useMutation( ({ email, password }) => signIn(email, password), { onSuccess: (data) => { queryClient.setQueryData([QUERY_KEY.user], data); navigate('/'); }, onError: (error) => { enqueueSnackbar('Ops.. Error on sign in. Try again!', { variant: 'error' }); } }); return signInMutation } ``` With two simple lines of code, you can set the user in the `useUser` state because the key used to set the query data is the same as the `useUser`. Then, with an `useEffect` in the `useUser` hook, you can remove or set the user data in the local storage when the user changes ```ts export function useUser(): IUseUser { const { data: user } = useQuery( [QUERY_KEY.user], async (): Promise => getUser(user), { refetchOnMount: false, refetchOnWindowFocus: false, refetchOnReconnect: false, initialData: userLocalStorage.getUser, onError: () => { userLocalStorage.removeUser(); } }); useEffect(() => { if (!user) userLocalStorage.removeUser(); else userLocalStorage.saveUser(user); }, [user]); return { user: user ?? null, } } ``` To complete the authentication flow, the only missing thing is the logout. You can build it with a custom hook called `useSignOut`; its implementation is straightforward and could be done in this way ```ts import { useQueryClient } from '@tanstack/react-query'; import { useCallback } from 'react'; import { useNavigate } from 'react-router-dom'; import { QUERY_KEY } from '../constants/queryKeys'; type IUseSignOut = () => void export function useSignOut(): IUseSignOut { const queryClient = useQueryClient(); const navigate = useNavigate(); const onSignOut = useCallback(() => { queryClient.setQueryData([QUERY_KEY.user], null); navigate('/auth/sign-in'); }, [navigate, queryClient]) return onSignOut } ``` As you can notice, the hook returns a simple function that clears the value in the user state and navigates to the `sign-in` page. Ok, perfect. Now you have all the notions of building an authentication flow with React Query, but If you want to find out more, watch my youtube video about authentication with React Query > 🎬 **Watch / View Embedded Resource:** [https://www.youtube.com/watch?v=JNoiAumC94g](https://www.youtube.com/watch?v=JNoiAumC94g) Ok, that's all from authentication. I hope you enjoyed this content! See you soon folks Bye Bye 👋 p.s. you can find the code of the video [in the GitHub repository](https://github.com/Puppo/learning-react-query/tree/05-auth) _Photo by [Rahul Mishra](https://unsplash.com/@rahuulmiishra?utm_source=Devto&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=devto&utm_medium=referral)_ > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### Playwright - UI Mode - **Canonical URL**: https://delpuppo.net/blog/playwright/playwright-ui-mode/ - **Article LLMs.txt**: https://delpuppo.net/blog/playwright/playwright-ui-mode/llms.txt - **Published**: Apr 6, 2023 - **Series**: Playwright Getting Started (https://delpuppo.net/blog/series/playwright/) - **Tags**: playwright, e2e - **Summary**: Hi folks, In release 1.32, Playwright released a new cool feature for us, the UI Mode! This feature... Hi folks, In release [1.32](https://playwright.dev/docs/release-notes#version-132), Playwright released a new cool feature for us, the UI Mode! This feature is still in beta yet, but you can use it by adding a simple option in the CLI `--ui`. ```bash $ npx playwright test --ui ``` The result of this command is a new window in your display! ![Playwright UI Mode view](https://cdn.hashnode.com/res/hashnode/image/upload/v1680593766315/ee493e5a-d6e6-4f93-b283-456851c50058.png) Using this view, you can run and debug your test. But let's dive into it. The view is split into two parts: the left part contains the list of the tests, and the right part is the crucial view to check and debug the tests. If you are familiar with the Playwright tracer, the right section is similar to the tracer view. But what can you do with this view? First, you can run one or many tests using the run button at the right of each test or group of tests. ![Run test](https://cdn.hashnode.com/res/hashnode/image/upload/v1680594130557/fcf3cece-112d-46da-b716-f058170a0b04.png) Playwright runs the tests by clicking this button, and you can see the result. A green tick means the test is ok, and a red icon with a circle X means there is an error in the test. ![Test feedback](https://cdn.hashnode.com/res/hashnode/image/upload/v1680594313494/1fffab38-1142-4573-aa95-f326fa4163b0.png) Now if you click on one of the tests in the right section, you can see what happened during its execution. In this case, let's dive into the wrong test. In the top section, you can see the timeline of the test; you can go over it with the mouse and see all the browser views during the execution. ![Timeline](https://cdn.hashnode.com/res/hashnode/image/upload/v1680594568820/16595bb7-db59-480b-839d-8f367a870ca6.png) In the action tab, you can find all the actions done by the test, for instance: the page creation, the navigation, each click on the page and so on. You can navigate in the history by clicking on one of these actions, and each time you click the action, the panel on the right shows the view at that specific moment. In this way, you can understand what happened during the test execution. ![Actions](https://cdn.hashnode.com/res/hashnode/image/upload/v1680594923651/e0a0565c-e49f-4aeb-8b2b-e20bec947696.png) In the bottom bar, you can access different views: Source, Console, Network, and Log. I think the last three views are pretty simple, and I want to focus on the Source view. In the Source view, you can see all the code used by Playwright to run the test, and if you click on one of the actions, Playwright highlights the line of code that runs that action. If your test is wrong, Playwright shows you the error in the specific line of code where the error is thrown. In this way, you can immediately see the mistake and try fixing it. ![Source](https://cdn.hashnode.com/res/hashnode/image/upload/v1680595280660/c2500776-e23f-4619-851f-3bbfd6c455b3.png) The last cool feature introduced in this view is the possibility to open the Source code directly from the view! If you click the icon at the right of the Run icon, Playwright opens the file using Visual Studio Code. ![Open code to VsCode](https://cdn.hashnode.com/res/hashnode/image/upload/v1680602335785/1880962f-7e9c-467d-ba33-e9047512cac8.png) Last but not least, Playwright keeps syncing your codebase with the Playwright UI Runner, so in this way, you can change and fix errors and retry the test immediately. If you want to find out more or watch the Playwright UI Runner on working, check out my Youtube video. [https://www.youtube.com/watch?v=ErkRgqmHCmc](https://www.youtube.com/watch?v=ErkRgqmHCmc) I think this solution and the integration of Playwright with VsCode are awesome, and Playwright is changing the Developer Experience in the best possible way! Ok, that's all folks! I hope you enjoyed this content, and now your testing phase is more comfortable and easier! See you later! Bye Bye 👋 > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### React Query - DevTools - **Canonical URL**: https://delpuppo.net/blog/react-query/react-query-devtools/ - **Article LLMs.txt**: https://delpuppo.net/blog/react-query/react-query-devtools/llms.txt - **Published**: Apr 5, 2023 - **Series**: React Query (https://delpuppo.net/blog/series/react-query/) - **Tags**: react, reactquery, reacthooks - **Summary**: Hey Folks, In this post, you'll learn how to debug and check whatever happens in your React Query... Hey Folks, In this post, you'll learn how to debug and check whatever happens in your React Query application. It's normal when you begin to learn or use a tool; check the tools around it to understand the developer experience so you can decide whether to continue with it. The React query team knows that and has decided to build a tool to help the developer that wants to work with React Query. This tool is called `react-query-devtools` and you can install it in a simple step. Open your terminal and type ```bash $ npm i @tanstack/react-query-devtools ``` Now, in your project, you can use it and get all the info required to debug your application. This tool is simple to use. In your application, you must import it and render it where you render the `ReactQueryProvider`. ```ts import { QueryClientProvider } from '@tanstack/react-query'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import React from "react"; import { queryClient } from './react-query/client'; import Router from './Router'; function App() { return ( ... ); } export default App; ``` Easy peasy, no? With ReactQueryDevtools, you don't have to pay attention to the environment to render or not the component because it provides it by default. It renders the component only if the condition `process.env.NODE_ENV === 'development'` is true. You can customize the component or force its render also in production mode if you want. To find out more about these topics, you can check the [documentation](https://tanstack.com/query/latest/docs/react/devtools). The benefit of using this component in your application is that it allows seeing what happens in ReactQuery at runtime. You can check the data saved in the state, how many application parts use the different queries, etc. You can also reset the state or remove part of it to re-fetch the data. Yeap, it exposes many good features to debug and check your React Query application, and it is a good tool for every developer that works with React Query. Here you can find an example of ReactQueryDevtool at work ![Devtool view](https://cdn.hashnode.com/res/hashnode/image/upload/v1680621083189/ae19ed79-87ac-4be6-8099-58c8e4d606c7.png) If you want to find out more, watch my Youtube video about React Query Devtool to see it in action. > 🎬 **Watch / View Embedded Resource:** [https://youtu.be/qXycRUetOX4](https://youtu.be/qXycRUetOX4) Ok, that's all! I think you have an idea of what React Query Devtools is and how you can integrate it into your application! I hope you enjoyed this content! See you soon folks Bye Bye 👋 p.s. you can find the code of the video [in the GitHub repository](https://github.com/Puppo/learning-react-query/tree/04-devtools) _Photo by [Rahul Mishra](https://unsplash.com/@rahuulmiishra?utm_source=Devto&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=devto&utm_medium=referral)_ > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### OramaSearch, a full-text search in your React application - **Canonical URL**: https://delpuppo.net/blog/oramasearch-a-full-text-search-in-your-react-application/ - **Article LLMs.txt**: https://delpuppo.net/blog/oramasearch-a-full-text-search-in-your-react-application/llms.txt - **Published**: Mar 30, 2023 - **Tags**: orama, search, react - **Summary**: In some applications, you need to create a full-text search to improve the search of data. In some... In some applications, you need to create a full-text search to improve the search of data. In some scenarios, you build this using an API and leave the problem to the backend. But in other cases, you need to guarantee the feature without an internet connection, or you are building a hybrid application, or you need to reduce these kinds of searches directly in the browser to reduce the computation cost in the server. A simple example could be to perform a search on your blog to see which articles respect some criteria. To do this, in the last period, [Orama Search](https://oramasearch.com/) has born. A simple npm library that allows you to create a "small" typed database in your browser (and not only) and exposes a simple method that permits you to search data in this database. So, in this article, you'll discover how to use Orama Search in your React application and learn how it is easy to integrate it into your next applications. Before starting a quick explanation of the application, because I don't want to cover all the processes in this article but only the Orama integration. The application is a simple React page where you can search for one or more Italian cities by a term. The terms could search the cities by names or by regions. The application on startup loads all the possible cities and create an Orama database in the browser. Then you can search using the form the city you prefer. I wrapped the Orama logic inside of a [React Context](https://react.dev/reference/react/createContext), and you can interact with it using two hooks. Here you can find the result ![App example](https://cdn.hashnode.com/res/hashnode/image/upload/v1679570164667/87822b46-2914-4816-8912-7cce8222a543.png) If you want to read this article with the full code of the application, you can find it [in the GitHub repository](https://github.com/Puppo/orama-search-getting-started). _N.B. in this article, I'll use_ [_ni_](https://www.npmjs.com/package/@antfu/ni) _instead of npm, yarn or pnpm, but you can use whichever you prefer without any problems._ ## Installation The first step to use Orama in your application is to install it. It's a piece of cake; in your terminal type: ```sh ni @orama/orama ``` I think this step doesn't need a big explanation. But this is the door to start using Orama in your application. With this command, now in your `node_modules`, you can find the Orama packages, and now it's time to start using it! ## Create Database First of all, to use Orama you have to create a database. To do that, Orama exposes a method called `create`. This method accepts a schema that describes the database structure and some options to configure the database. For this example, in addition to the schema, I want to show you how you can work with a different language (Italian, in this case) and how you can configure Orama to do that. Orama, to handle this process, uses [stemmer](https://github.com/words/stemmer) out-of-the-box. So to create a simple database in this case and to move your first steps with Orama, you can go to the folder `src/core/useOrama` where you'll find all the code to handle Orama in your application. Let's start from the `src/core/useOrama/context.ts` file ```ts import { Orama, Schema } from "@orama/orama"; import { createContext } from "react"; import { SchemaToTypes } from "./types"; export type OramaContext = { isCreated: boolean; isIndexed: boolean; db?: Orama; setData: (data: SchemaToTypes[]) => Promise; }; export const OramaContext = createContext({ isCreated: false, isIndexed: false, setData: async values => undefined, }); ``` As you can see, the context of Orama for this application is simple. Two flags to understand the status of the database, the database and the `setData` function to insert the data in the database. Now you can move to the OramaProvider inside of the `src/core/useOrama/Provider.tsx` file. ```ts import { create, insertMultiple, Orama, Schema } from "@orama/orama"; import { PropsWithChildren, useCallback, useEffect, useMemo, useState } from "react"; import { OramaContext } from "./contex"; import { SchemaToTypes } from "./types"; type IOramaProviderProps = PropsWithChildren<{ schema: S options?: Omit[0], 'schema'> }> export const OramaProvider = ({ schema, options, children }: IOramaProviderProps) => { const [isIndexed, setIsIndexed] = useState(false); const [orama, setOrama] = useState(undefined) const setData = useCallback(async (values: SchemaToTypes[]) => { if (!orama) throw new Error(`Orama database is not created!`) setIsIndexed(false); await insertMultiple(orama, values); setIsIndexed(true); }, [orama, setIsIndexed]) const context = useMemo(() => ({ isCreated: !!orama, isIndexed, db: orama, setData }), [setData, orama, isIndexed]) useEffect(() => { async function init() { const db = await create({ schema, ...(options ?? {}), }); setOrama(db); } init(); }, []); return {children}; }; ``` In this file, the code starts to be tricker. First of all, the provider accepts two properties, the Orama schema and the Orama options. Orama uses these properties to create the database for the application. Then the provider does some little things. First, it initializes two states to save the database and the isInidex flag. This flag is used to understand if the data used for the search are saved in the database. Then the provider creates the `setData` function used by the application to insert the data in the database. With an effect, it creates the database using the [create](https://docs.oramasearch.com/usage/create) method exposed by Orama, and then creates and returns the context using a `useMemo` to improve the performance and the `OramaContext.Provider` components. If you are familiar with React and Javascript/Typescript, I think everything is simple and clear after one or two views of the code. The last step is to render the Provider in the application. This is in the `src/App.tsx`. ```ts import { createTheme, ThemeProvider } from '@mui/material/styles'; import { stemmer } from '@orama/orama/stemmer/it'; import { CITIES_SCHEMA } from './core/cities/oramaSchema'; import { OramaProvider } from './core/useOrama/Provider'; import Search from './page/Search'; const theme = createTheme(); function App() { return (), }, }, }}> } export default App ``` Here there are two important things. First, the schema: ```ts export const CITIES_SCHEMA = { city: "string", lat: "number", lng: "number", country: "string", iso2: "string", admin_name: "string", capital: "string", population: "number", population_proper: "number", } as const; ``` Second, the Orama configuration. In this case, the configuration imports stemmer and indicates to Orama that the database contains Italian words. Orama works in English by default, but you can use it in other languages. You can find out more in the [documentation](https://docs.oramasearch.com/text-analysis/stemming). ## Init Database To initialize the database, you have to see the `src/core/useOrama/useOramaSetData.tsx` file. This file contains the hook that exposes the function to set the data in the database. This is the code ```ts import { Schema } from "@orama/orama"; import { useContext } from "react"; import { OramaContext } from "./contex"; export const useOramaSetData = () => { const { setData, isCreated }: OramaContext = useContext(OramaContext); return { canSetData: isCreated, setData }; }; ``` As you can notice, the hook is pretty simple, get the `setData` function and the `isCreated` flag, and exposes them. Here the important concept stands in the Provider file where you defined the `setData` function. Let's pick this part of the code again ```ts const setData = useCallback(async (values: SchemaToTypes[]) => { if (!orama) throw new Error(`Orama database is not created!`) setIsIndexed(false); await insertMultiple(orama, values); setIsIndexed(true); }, [orama, setIsIndexed]) ``` In this code, the `insertMultiple` function is an important concept for Orama. With the `insertMultiple` function, you can insert an array of rows in your database, and then Orama will use these data for future searches. There is also the insert method to insert only one row per time, but you can find more info [in this reference](https://docs.oramasearch.com/usage/insert). Then, it's time to use this method on the Search page to initialize the database with the data. To do that, go into the `src/Search/index.ts` file, and you can find the import of the hook and how to use it in an `effect`. ```ts ... import { useOramaSetData } from '../../core/useOrama/useOramaSetData'; import { getCities } from '../../core/cities/httpCities'; ... export default function Search() { ... const { canSetData, setData } = useOramaSetData(); ... useEffect(() => { if (!canSetData) return; async function initDb() { const cities = await getCities() setData(cities) } initDb() }, [canSetData]); ... } ``` Now, your database is ready to perform the searches 🚀 ## Search in the Database The core part of Orama is the search! Now it's time to dive into it. To handle the search, you'll create another hook called `useOramaSearch`. The search is pretty simple in Orama. There is a `search` method that accepts the Orama database, the terms to search and some possible options. You can find more [in this reference](https://docs.oramasearch.com/usage/search/introduction). The implementation of the search is this ```ts import { Schema, search as searchWithOrama } from "@orama/orama"; import { useCallback, useContext } from "react"; import { OramaContext } from "./contex"; import { Results } from "./types"; export const useOramaSearch = () => { const context: OramaContext = useContext(OramaContext); const search = useCallback( async (term: string, properties: string[] | '*' = '*'): Promise> => { if (!context.db) throw new Error("Database not ready"); const { length } = properties; const boost = properties === '*' ? { city: 2, } : properties.reduce((acc, key, index) => { const boostValue = (length - index) * 2; acc[key] = boostValue; return acc; }, {} as Record); const result = await searchWithOrama(context.db, { term, properties, boost }) as unknown as Results; return result; }, [context] ); return { isReady: context.isCreated && context.isIndexed, search, }; }; ``` There are some interesting points in this code. As you can notice, the search method accepts the terms and a list of properties. The first is the search terms that the user wants to search; the second is the list of properties that you want Orama to use during the search (using `*` Orama searches in all the properties). There is another strange option called boost in this example. This option is a configuration to indicate to Orama the importance of one or more fields in the search. The last step is to use this hook in the Search page to run the search. In the `src/page/Search/index.tsx` this implementation is done in this way. ```ts ... import { useOramaSearch } from '../../core/useOrama/useOramaSearch'; ... export default function Search() { const { isReady, search } = useOramaSearch(); ... const onSubmit: FormEventHandler = useCallback(async (e) => { e.preventDefault(); const form = e.target as HTMLFormElement; const searchTerms = form.elements.namedItem('searchTerms') as HTMLInputElement; const results = await search(searchTerms.value, [searchType]); setResults(results) }, [search, searchType, setResults]) ... } ``` The last info about the search is the search result. It is composed in this way - **elapsed** : this object contains the statistic of the search; here, you can find how much time Orama has used to provide the result - **hits** : this is the list of the elements that respect the search; by default, Orama returns the first ten elements; you can paginate the result, passing the limit and the offset options in the search method. - **count** : here; you can find the number of elements that match the terms And perfect now the application is ready to search the cities. You can test the result [in this reference](https://puppo.github.io/orama-search-getting-started/) 🚀 ## Conclusion As you can notice, integrating Orama into your application is pretty simple. The project has born some months ago and obviously it is not perfect and with huge features, but it works well, and the team will release many new features in the further future. All the hooks and the context seen in this article could be early replaced by an npm package called [use-orama](https://github.com/nearform-oss/use-orama); it is a working progress 🚧. And that's all! I hope Orama can help your applications search data quickly in the future, and I think it will become a great product in the next few months! If you are interested in it, you can learn more about it in the official [documentation](https://docs.oramasearch.com/). And don't forget to follow [Orama](https://twitter.com/OramaSearch) on Twitter and [Michere Riva](https://twitter.com/MicheleRivaCode) its CTO. 🚧 N.b. The project is in beta version at this moment, so breaking changes or some bugs are opportunities to contribute to the project if you want 😎 [in the GitHub repository](https://github.com/oramasearch/orama) you can find the GitHub Repo 🚧 > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### React Query - useIsFetching & useIsMutation - **Canonical URL**: https://delpuppo.net/blog/react-query/react-query-useisfetching-useismutation/ - **Article LLMs.txt**: https://delpuppo.net/blog/react-query/react-query-useisfetching-useismutation/llms.txt - **Published**: Mar 29, 2023 - **Series**: React Query (https://delpuppo.net/blog/series/react-query/) - **Tags**: react, reactquery, reacthooks - **Summary**: Hey folks, Today it is time to talk about two hooks exposed by react query: useIsFetching and... Hey folks, Today it is time to talk about two hooks exposed by react query: useIsFetching and useIsMutation. Each of these hooks could be used to understand if there is a fetching request or if there is a mutation request ongoing in the application. They are useful if we need to create a global loader that appears when there are one or more requests on going. But how can you use them? Let's start with the useIsFetching. ```ts import { useIsFetching } from '@tanstack/react-query'; export default function Loader() { const isFetching = useIsFetching(); if (!isFetching) return null; return <>Fetching... } ``` As you can see, the syntax is pretty simple. You can import the hook from the library and you can use the hook in your components. The hook returns only a boolean value that indicates if there are one or more fetching requests in the application. Then with this data, you can decide if you have to show a loader or not. Easy peasy! Now it's time to move to the useIsMutation hook. This hook is similar to the previous one, the only different concept is that this hook handles the mutation requests. Let's see an example! ```ts import { useIsMutating } from '@tanstack/react-query'; export default function Loader() { const isMutating = useIsMutating(); if (!isMutating) return null; return <>Mutating... } ``` As you can notice, the syntax is the same as the previous one, the only difference stands in the name of the hook and in its concept. If you want to find more about these two hooks see my Youtube video about them. > 🎬 **Watch / View Embedded Resource:** [https://youtu.be/dWuUjDNB6Yc](https://youtu.be/dWuUjDNB6Yc) Ok, I think you have an idea of how these two hooks work. I hope you enjoyed this content. See you soon folks Bye Bye 👋 p.s. you can find the code of the video [in the GitHub repository](https://github.com/Puppo/learning-react-query/tree/03-isfetching-and-mutating) _Photo by [Rahul Mishra](https://unsplash.com/@rahuulmiishra?utm_source=Devto&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=devto&utm_medium=referral)_ > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### React Query - useMutation - **Canonical URL**: https://delpuppo.net/blog/react-query/react-query-usemutation/ - **Article LLMs.txt**: https://delpuppo.net/blog/react-query/react-query-usemutation/llms.txt - **Published**: Mar 22, 2023 - **Series**: React Query (https://delpuppo.net/blog/series/react-query/) - **Tags**: react, reactquery, reacthooks - **Summary**: Hey Folks, It's time to talk about the second core concept in React Query, mutation. What is... Hey Folks, It's time to talk about the second core concept in React Query, mutation. ## What is it? Mutations are actions that a user can do in your application. You can imagine a mutation as an action to change or create something. To understand better in the code what a mutation is, let's start with a code snippet ```ts import { useMutation } from '@tanstack/react-query'; const postTodo = async (text: Todo['text']): Promise => { const response = await fetch('api/tasks', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ text }), }); if (!response.ok) { throw new ResponseError('Failed to insert new todo', response); } return await response.json(); }; export const useAddTodo = (): UseAddTodo => { const { mutate: addTodo, isLoading, error } = useMutation(postTodo, { onSuccess: () => { // Success actions }, onError: (error) => { // Error actions }, }); return { addTodo, }; }; ``` As you can see, the mutation is a simple hook with two params: - The function to handle the request - The options to handle, in this case, the success and the error hooks, but also to configure the mutation (retry, retry delay and so on). The result has three main objects: - mutate: this is the action to run the mutation in your code - isLoading: this flag indicates if the mutation is ongoing - error: this indicates the errors in case there is an error in the request Using mutations in your React applications, you can handle all those actions to mutate the data and simplify the management of the states of these requests. Another important concept to know when you work with mutation is the [QueryClient](https://tanstack.com/query/v4/docs/react/reference/useQueryClient). Using the QueryClient, you can invalidate a query already provided and say to react query to ask again for the data because you are sure that those data are not yet valid after the mutation. To do that, you have to use the `useQueryClient` hook to retrieve the queryClient and using the `invalidateQueries` method, you can invalidate the react query cache for a specific query or multiple queries. Here you can find an example ```ts import { useMutation, useQueryClient } from '@tanstack/react-query'; import { QUERY_KEY } from '../../../../constants/queryKeys'; export const useAddTodo = (): UseAddTodo => { const client = useQueryClient(); const { mutate: addTodo } = useMutation(postTodo, { onSuccess: () => { client.invalidateQueries([QUERY_KEY.todos]); }, }); ... }; ``` Ok, I think you have an idea of how useMutation and useQueryClient work, but if you want to dive into them, don't forget to see my Youtube video. [https://www.youtube.com/watch?v=Cpxoxj9sTXA](https://www.youtube.com/watch?v=Cpxoxj9sTXA) Ok, That's all! I hope you enjoyed this content, and I'll get back to you soon with another concept of React Query. Bye Bye 👋 p.s. you can find the code of the video [in the GitHub repository](https://github.com/Puppo/learning-react-query/tree/02-mutations) _Photo by [Rahul Mishra](https://unsplash.com/@rahuulmiishra?utm_source=Devto&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=devto&utm_medium=referral)_ > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### React Query - useQuery - **Canonical URL**: https://delpuppo.net/blog/react-query/react-query-usequery/ - **Article LLMs.txt**: https://delpuppo.net/blog/react-query/react-query-usequery/llms.txt - **Published**: Mar 15, 2023 - **Series**: React Query (https://delpuppo.net/blog/series/react-query/) - **Tags**: react, reactquery, reacthooks - **Summary**: Hey Folks, It's time to take a journey on react query. Don't you know it? Perfect, you are in the... Hey Folks, It's time to take a journey on [react query](https://tanstack.com/query/latest). Don't you know it? Perfect, you are in the right place 😁 ## Introduction What is React Query? React query is an npm library created by [@TannerLinsley](https://twitter.com/tannerlinsley). It's a state manager for react that simplifies many tasks like handling the HTTP request state, saving data in the client to prevent many requests, sharing data in your application using hooks and so on. You'll discover more in this series about it, learn how to use it, and appreciate its simplicity in your react applications. ## useQuery The first core concept is useQuery. Through it, you can retrieve data from a source and handle all the states of this request in a very simple way. Let's see an example ```ts import { useQuery } from '@tanstack/react-query'; const fetchTodos = async (): Promise => { const response = await fetch('api/tasks'); if (!response.ok) { throw new ResponseError('Failed to fetch todos', response); } return await response.json(); }; export const useTodos = (): UseTodos => { const { data: todos = [], isLoading, isFetching, error, } = useQuery(['todos'], fetchTodos, { refetchOnWindowFocus: false, retry: 2, }); ... }; ``` In this example, you can see the essentials of useQuery. UseQuery is a react hook; it takes 3 params: - The query key - The query function - The configurations Let's start with the first one. The query key is a key used by react query to identify your queries; through the key, react query is able to store the result and use it in different parts of the application. The key is used to identify the query, and with it you can also reset the query or change the value by code using the React Query Client. The query function is the method used to retrieve data from a source (rest, graphQL, etc. etc.). It's simple; a function that returns a kind of data could be a simple function or, in most cases, a promise. Configurations, ok these are simple :) There are many possible options to run the query in different ways (amount of retries, when refreshing the data, how to cache the data and so on..) The result of this hook has three important objects instead - data; this object contains the result of your query function. Please, pay attention that the data could be undefined too; this is because on the first call when the request is pending, data is not yet presented - isLoading; this flag indicates when react query is loading data. There is also the isFetching flag, which is important if you are creating infinite scrolling. The isFetching flag indicates that there is a request pending, which is perfect if the app is asking for the next info - error: this object contains the error if your request has a problem; using it, you can get the error and create a pretty info message for the user Ok, you have an idea of how useQuery works and what its potentialities are, but if you are more interested in it, you can watch my video to learn more about it. > 🎬 **Watch / View Embedded Resource:** [https://www.youtube.com/watch?v=q2r2yoq5zaQ](https://www.youtube.com/watch?v=q2r2yoq5zaQ) ok, that's all! I'll get back to you with another react query feature soon. I hope you enjoyed this content. Bye Bye 👋 p.s. you can find the code of the video [in the GitHub repository](https://github.com/Puppo/learning-react-query/tree/01-queries) _Photo by [Rahul Mishra](https://unsplash.com/@rahuulmiishra?utm_source=Devto&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=devto&utm_medium=referral)_ > 🎬 **Watch / View Embedded Resource:** [https://dev.to/puppo](https://dev.to/puppo) --- ### Why I love GitLens in my VsCode - Interactive Rebase - **Canonical URL**: https://delpuppo.net/blog/gitlens/why-i-love-gitlens-in-my-vscode-interactive-rebase/ - **Article LLMs.txt**: https://delpuppo.net/blog/gitlens/why-i-love-gitlens-in-my-vscode-interactive-rebase/llms.txt - **Published**: Feb 28, 2023 - **Series**: GitLens (https://delpuppo.net/blog/series/gitlens/) - **Tags**: git, vscode, gitlens, gitkraken - **Summary**: One of the processes I typically do in my daily work is interactive rebase. I commit a lot, and then... One of the processes I typically do in my daily work is interactive rebase. I commit a lot, and then before pushing the result, I try to make the history more readable. If you have to do that by CLI, you should follow many steps: ```bash > git log --oneline ``` This command returns the list of the commit ![Git commit history](https://cdn.hashnode.com/res/hashnode/image/upload/v1677330009538/2176c8e6-c851-4ee9-9dfb-14ec242a7219.png) Now, you have to get the SHA of the commit from where you want to start the interactive rebase and run the command ```bash > git rebase -i bf2478f ``` Now you can handle your rebase from your best editor, in my case VsCode. ```bash pick 4fcdf0e add documentation pick c7a47a9 add documentation # Rebase bf2478f..c7a47a9 onto bf2478f (2 commands) # # Commands: # p, pick = use commit # r, reword = use commit, but edit the commit message # e, edit = use commit, but stop for amending # s, squash = use commit, but meld into previous commit # f, fixup [-C | -c] = like "squash" but keep only the previous # commit's log message, unless -C is used, in which case # keep only this commit's message; -c is same as -C but # opens the editor # x, exec = run command (the rest of the line) using shell # b, break = stop here (continue rebase later with 'git rebase --continue') # d, drop = remove commit # l, label