<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
      <title>Efe Karasakal&#x27;s Blog</title>
      <link>https://efe.dev</link>
      <description>Software Engineering, Tech, and More</description>
      <generator>Zola</generator>
      <language>en</language>
      <atom:link href="https://efe.dev/rss.xml" rel="self" type="application/rss+xml"/>
      <lastBuildDate>Sat, 16 May 2026 00:00:00 +0000</lastBuildDate>
      <item>
          <title>My travel equipment</title>
          <pubDate>Sat, 16 May 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://efe.dev/blog/my-travel-equipment/</link>
          <guid>https://efe.dev/blog/my-travel-equipment/</guid>
          <description xml:base="https://efe.dev/blog/my-travel-equipment/">&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;efe.dev&#x2F;blog&#x2F;my-travel-equipment&#x2F;.&#x2F;cover.jpeg&quot; alt=&quot;Mount Fuji, 2025&quot; &#x2F;&gt;
&lt;em&gt;Mount Fuji, 2025&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;My wife and I have traveled to over 20 countries. After one of our first trips together, we realized that carrying large suitcases didn’t fit our style of traveling. So we made our first dedicated “travel” purchase: Osprey backpacks.
That eventually led us to discover the “Onebag” community, a group of people who do all kinds of travel with a single backpack as overhead cabin baggage. They also share their packing lists, which can be great inspiration. Learning from others helped us a lot in refining our own style. We added our own flavor too, bought new gear, experimented with different setups, etc.&lt;&#x2F;p&gt;
&lt;p&gt;At this point, I’d say we’re proficient one-and-a-half-baggers.&lt;&#x2F;p&gt;
&lt;p&gt;For me, travel equipment is either something I bought specifically for travel or something I bring on most trips.
We don’t impulse buy often, so I’d like to think there aren’t many “useless” items on this list (although who knows).&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.osprey.com&#x2F;de&#x2F;farpoint-40-s26&quot;&gt;Farpoint™ 40 Travel Pack&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This has been our main “onebag” for years. We use it as overhead cabin baggage.
If you’re used to large suitcases, a single bag can feel small, but I assure you, 40L is a massive amount of space. Its solid structure, combined with the sternum strap, makes carrying heavier loads fairly easy. It’s also compliant with most airlines’ overhead baggage size requirements (though always check your airline’s specific limits).
That said, this bag is big and heavy. It weighs roughly 1.5 kg by itself, which can be a problem since many airlines (especially in Southeast Asia) enforce a 7 kg cabin baggage limit.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.osprey.com&#x2F;de&#x2F;osprey-daylite-expandable-travel-pack-26l-f24&quot;&gt;Osprey 26+6L&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;We recently bought this after running into issues with the 7 kg cabin baggage limits on Southeast Asian airlines.
IMO, this is the perfect onebag backpack (also ranked #1 in the r&#x2F;onebag tier list). It’s 26L by default but expands to 32L, while weighing only around 800 grams. It works great as overhead cabin baggage, but also as a personal item since its dimensions are relatively compact. It’s also affordable compared to similar options from other brands.
I don’t really have major downsides for this bag, but coming from a backpack with a waist strap, this one definitely feels heavier on the shoulders. If you plan to carry your bag for extended periods, it might not be the best fit.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.osprey.com&#x2F;ultralight-stuff-pack-ulstuffdays23-264&quot;&gt;Osprey Ultralight Stuff Pack&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;We bought this bag early in our travels, but didn’t use it for quite some time.
However, after trying a lot of different bags, we’ve finally settled on this as our favorite “daypack”, and half bag (as in one-and-a-half-bag).
It can fit a lot of stuff in it, and when not used, you can just pack it.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.amazon.de&#x2F;-&#x2F;en&#x2F;Atacama-Premium-Compression-Packing-Cube&#x2F;dp&#x2F;B0CKJ3W9KY&#x2F;ref=sr_1_1?crid=1C9DA4SKH6YP&amp;amp;dib=eyJ2IjoiMSJ9.87h-CRXiilHackjY9t8RGqpBYQqOvKlLSa7u8k3MO_djNs07fzQQVmMlmQfwX8EfdNWGm_1clThg4rRsUxC5FEUSCtr53Qa-En7Zn4p6ZNH64cxm0yQUI1pMpe6IXLqtX3jjNEckf7wNkvRpzTvuz9GeJFaB6uOYT48Gi9zKSbs-MpehlhZJljiE_7DtzdgY_zmra65zjNrx4iEZ_tfVIr-D5FmcDcq0rFCIjbNoeIuQI-fLQ6v2072Prx_fJxs1uxwThKdE76bGFInnn47O3SazyuqKFwFCeZun6eWgtlw.qKldi2j0UGg3YJ16IIoumTMbG7nGIOAk4YpRZqAeYsI&amp;amp;dib_tag=se&amp;amp;keywords=Atacama%2BPremium%2BCompression%2BPacking%2BCubes%2BSet&amp;amp;qid=1778942910&amp;amp;sprefix=atacama%2Bpremium%2Bcompression%2Bpacking%2Bcubes%2Bset%2Caps%2C295&amp;amp;sr=8-1&amp;amp;th=1&quot;&gt;Atacama Premium Compression Packing Cubes Set&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;We used these compression packing cubes for a long time; however, flying more frequently to Asia, where the 7 kg limitation is the norm, we slowly stopped using them because the entire set is several hundred grams.
However, if weight is no concern, they are pretty solid.
It’s really easy to compress stuff with them, they are sturdy, and they offer a separate space for dirty laundry, which is awesome.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.osprey.com&#x2F;ultralight-packing-cube-set-ulpackcbsets23-489&quot;&gt;Osprey Ultralight Packing Cube Set&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;We bought these because they are extremely lightweight. The entire set is less than a hundred grams.
I usually use the smallest bag as a tech pocket, and the bigger ones for clothes.
After experimenting with travelling without packing cubes, IMO they are a must, especially if you are changing places often.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.codeofbell.com&#x2F;products&#x2F;x-pod-sling-pack&quot;&gt;Code of bell X-POD II&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This is an amazing crossbody bag, and I’ve used it in a couple of my trips.
However, I’ve realized the Osprey Ultralight Stuff Pack to be a better fit for my use case.
It really, really is an awesome bag though.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;store.steampowered.com&#x2F;steamdeck&quot;&gt;Steam Deck OLED&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Not much to say here, Steam Deck rocks.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.amazon.de&#x2F;Apple-iPad-Air-Chip-Intelligence&#x2F;dp&#x2F;B0DZ76JZ9Q&quot;&gt;iPad Air 11 inch (M3)&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;My iPad is my (current) favorite travel companion.
I play games on it (some favs: Planescape: Torment, KOTOR 1 and 2, Civ VI and VII),
read books, and watch stuff.
I bring a Type C -&amp;gt; HDMI cable and connect it to the hotel TVs.
I use it for lightweight programming via Cloud services.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.amazon.com&#x2F;Croing-PCS-Portable-Bidet-Travel&#x2F;dp&#x2F;B0B3QZQ71R&quot;&gt;CROING portable bidet&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;It is a must :-)&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;culoclean.com&#x2F;de&#x2F;&quot;&gt;CuloClean Foldable Bidet&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This product turns any bottle into a bidet.
Although it is a great product, we just couldn’t get used to it.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.dr-beckmann.com&#x2F;en-hk&#x2F;magic-leaves&#x2F;&quot;&gt;Dr. Beckmann Magic Leaves Detergant&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Since we pack light, we usually have to wash our laundry when travelling.
IMO, this is the best way of bringing detergent with you.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.amazon.de&#x2F;-&#x2F;en&#x2F;Scrubba-Outdoor-Portable-Probably-Smallest&#x2F;dp&#x2F;B01N68XF0O&quot;&gt;Scrubba Wash Bag&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Scrubba is a portable “washing machine”.
It is essentially a drybag with some bumps inside that help with cleaning the clothes.
It makes a great combo with the magic leaves above.
However, after travelling for a while, we’ve realized that the hard part of washing clothes without a washing machine is not the washing part, but the drying part.
So we often don’t bring it with us.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.amazon.de&#x2F;gp&#x2F;aw&#x2F;d&#x2F;B0B9443SWF&quot;&gt;Fit-flip Microfiber Towel&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Microfiber towels are amazing, they absorb well, dry significantly fast, and they are highly compact.
That being said we usually don’t bring them along nowadays as we usually don’t need towels, and when we do we just use a Turkish bath towel.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Turkish Bath Towel&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Turkish bath towels are super absorbent and lightweight.
IMO they also serve well as thin blankets.
We have “high quality” and generic ones, and IMO they are both fine, so I wouldn’t bother buying expensive ones.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.amazon.de&#x2F;-&#x2F;en&#x2F;PROfezzion-Aeroplane-Compatible-Smartphones-Hands-Free&#x2F;dp&#x2F;B0CTJMS4RG&quot;&gt;PROfezzion Travel Plane Holder Mag Safe&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;These are phone holders for the plane.
While they work very well, I usually don’t bring mine along anymore.
Nowadays planes have decent entertainment systems, and if that’s not enough I usually use my iPad.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.amazon.de&#x2F;COCASES-Universal-Compatible-Smartphones-Removable&#x2F;dp&#x2F;B0FKFT4MFD&quot;&gt;Cocases Universal Mobile Phone Chain&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This is a great strap for securing your phone while taking photos.
Definitely a must bring for us.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.uniqlo.com&#x2F;us&#x2F;en&#x2F;products&#x2F;E465185-000&#x2F;&quot;&gt;Uniqlo Airism Tshirts&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Uniqlo Airism Tshirts have been my “travel” tshirts for a while.
Great breathability, affordable prices, decent look.
However, I’m a bit concerned about their build quality.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www2.hm.com&#x2F;de_de&#x2F;productpage.0974629024.html&quot;&gt;H&amp;amp;M Coolmax Shorts&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;These are my favorite shorts of all time for travelling.
They are very comfortable, with a breathable fabric.
The pockets have zippers, which gives you peace of mind when travelling.
Since they come in basic colors, they are also a great fit for capsule wardrobes.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.uniqlo.com&#x2F;us&#x2F;en&#x2F;products&#x2F;E454326-000&#x2F;&quot;&gt;Uniqlo Airsoft Boxers&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Light, comfortable boxers. They dry quickly after washing. Great product overall.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.uniqlo.com&#x2F;us&#x2F;en&#x2F;products&#x2F;E479755-000&quot;&gt;Uniqlo Ultra Light Down Jacket&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Comfortable, lightweight, water repellent, packable jacket.
They are not very warm though, so you might want another layer.
We don’t bring them along as much anymore, as we’d like to bring more “stylish” jackets.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.amazon.de&#x2F;dp&#x2F;B0FFY6229G&quot;&gt;Travel Slippers Foldable Quick Drying&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;These **slippers can pack flat, dry really fast, and are non-slip.
They are one of my fav purchases.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Crocs&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I think Crocs are great and very flexible.
However, they are also very bulky.
I’m thinking about replacing them with something more lightweight.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.apple.com&#x2F;de&#x2F;airpods-pro&#x2F;&quot;&gt;AirPods Pro 3&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Ok, I didn’t buy my AirPods for travelling, but I couldn’t travel without them.
I love them overall, they pair well with my other products, are lightweight, comfortable, and have decent ANC.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;eu.hollyland.com&#x2F;products&#x2F;lark-m2s&quot;&gt;Hollyland Lark M2s&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Great wireless microphone. Works well with cameras and phones.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;sockwellusa.com&#x2F;products&#x2F;mens-elevation?variant=9460551516207&quot;&gt;Sockwell Compression Socks&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;We never fly without our plane socks from Sockwell.
The compression feels a bit strange eventually, but after countless flights, I could wear them all day long.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.amazon.de&#x2F;-&#x2F;en&#x2F;YoMaris-Foldable-Reusable-Certificates-Adventure&#x2F;dp&#x2F;B06Y488QLT&quot;&gt;Foldable Water Bottles&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;One of the best purchases we’ve ever made were these water bottles.
They are super lightweight, and take little to no space.
They are so good people asked us where we bought them.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.amazon.de&#x2F;-&#x2F;en&#x2F;Philips-elektrische-Zahnb%C3%BCrste-USB-Ladung-HY1200&#x2F;dp&#x2F;B0CG6TYC7P&quot;&gt;Philips One Travel Toothbrush&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Great, lightweight electric brush suitable for travelling.
However, looking at recent comments, apparently they’ve replaced the USB-C port with something else, which would make me reconsider.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Generic Travel Adapter&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;A travel adapter is a must when abroad.
Ours is a generic one with some sockets and USB ports.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;primulaproducts.com&#x2F;products&#x2F;coffee-brew-buddy&quot;&gt;Primula Brew Buddy&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Brew Buddy is a portable brewing device for making coffee&#x2F;tea.
I think it is a decent product, but I usually don’t use them anymore.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Generic Inflatable Neck Pillow&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I know some people use them and are content, but inflatable neck pillows just didn’t work for us.
So we bought different products.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.amazon.de&#x2F;-&#x2F;en&#x2F;SOMLAW-Aeroplane-Portable-Sleeping-Earplugs&#x2F;dp&#x2F;B0F131R2RN&quot;&gt;SOMLAW Travel Neck Pillow&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Good quality, affordable neck pillow. Works very well.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.amazon.de&#x2F;Cabeau-Evolution-TNE-Travel-Pillow&#x2F;dp&#x2F;B0BY3RK5CC&quot;&gt;Cabeau Travel Pillow TNE S3&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;These are decent neck pillows that can be mounted on the plane seat, further stabilizing the pillow.
I think they are a bit overpriced though.
The pillowcase has stitches coming out after handwashing it (more like soaking it) once.
The pillow itself is comfortable, but doesn’t justify almost the triple price.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.amazon.de&#x2F;-&#x2F;en&#x2F;Umisleep-Weighted-Pressure-Blocking-Adjustable&#x2F;dp&#x2F;B09DXXWVL4&quot;&gt;Weighted Sleep Mask Umisleep 3D&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;We’ve used generic sleep masks before (that came out of travelling sets), and these are definitely an upgrade.
They are way more comfortable and the pressure helps.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.amazon.de&#x2F;-&#x2F;en&#x2F;FINTIE-Passport-Cover-Vaccination-Certificate&#x2F;dp&#x2F;B07BVZSPNN&quot;&gt;Fintie Passport Cover&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;After getting caught in the rain in Prague, we bought these covers and never looked back.
It’s been more than 3 years and tens of travels, and they are still holding well.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.amazon.de&#x2F;-&#x2F;en&#x2F;Frelaxy-Backpacks-Schoolbags-Waterproof-Reflectors&#x2F;dp&#x2F;B089VHZNWY&quot;&gt;Frelaxy Rain Cover&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Simple bag covers for water protection.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.amazon.de&#x2F;-&#x2F;en&#x2F;ANYOO-Lightweight-Waterproof-Breathable-Multipurpose&#x2F;dp&#x2F;B083F9XZHD&quot;&gt;Anyoo Rain Jacket&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Great, high-quality rain jackets.
We now realize our way of travelling doesn’t often require rain jackets, though.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.on.com&#x2F;en-gb&#x2F;products&#x2F;cloud-6-wp-3mf1006&#x2F;mens&#x2F;black-eclipse-shoes-3MF10060106&quot;&gt;Cloud 6 Waterproof Goretex Shoes&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;It’s a tough job picking a “one shoe” as our travel setup has limited space.
I have three main criteria for my use case: decent and flexible style, nice to walk on as we walk ~20km some days, water resistant.&lt;&#x2F;p&gt;
&lt;p&gt;I know some people prefer quickly drying shoes instead of water-resistant ones, but this works well for me.
Cloud 6 fits my criteria, but so would many other shoes.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.amazon.de&#x2F;Hanging-Toiletry-Bag-Toiletries-Accessories&#x2F;dp&#x2F;B07JMD9XFJ&quot;&gt;Hanging Toiletry Bag&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;We don’t have this particular bag, but one that is very similar.
These are great for organizing, and for using anywhere thanks to their hooks, but they are also bulky and somewhat heavy.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.amazon.de&#x2F;Racqua-Barefoot-Hiking-Diving-Walking&#x2F;dp&#x2F;B08PNQP61W&quot;&gt;Racquet Composite Mesh Barefoot&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Lovely water shoes, served us well so far.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.gl-inet.com&#x2F;products&#x2F;gl-mt3000&#x2F;&quot;&gt;GL.iNet GL-MT3000 (Beryl AX) Mobile Travel Router&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I love my Beryl AX! Best travel router out there.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.philips.de&#x2F;c-m-pe&#x2F;oneblade-trimmen-stylen-und-rasieren&quot;&gt;Philips OneBlade&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Philips OneBlade makes a great travel trimmer&#x2F;shaver.
It’s smaller than similar products, and the battery lasts a long time.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>I&#x27;m now a Convex Champion</title>
          <pubDate>Fri, 15 May 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://efe.dev/blog/convex-champion/</link>
          <guid>https://efe.dev/blog/convex-champion/</guid>
          <description xml:base="https://efe.dev/blog/convex-champion/">&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;efe.dev&#x2F;blog&#x2F;convex-champion&#x2F;.&#x2F;cover.jpeg&quot; alt=&quot;Convex&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;i-m-now-a-convex-champion&quot;&gt;I’m now a Convex Champion&lt;&#x2F;h3&gt;
&lt;p&gt;I just became a Convex Champion, and I’m pretty stoked about it.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.convex.dev&#x2F;&quot;&gt;Convex&lt;&#x2F;a&gt; is a reactive database for building
modern web apps. I built &lt;a href=&quot;.&#x2F;convex-multiplayer-game&quot;&gt;
a small multiplayer online game
&lt;&#x2F;a&gt; with it and was really impressed by the developer experience.&lt;&#x2F;p&gt;
&lt;p&gt;Since then, I’ve been advocating for Convex as a fan: &lt;a href=&#x27;..&#x2F;convex-multiplayer-game-talk&#x27;&gt;I gave two community talks about it&lt;&#x2F;a&gt;, &lt;a href=&#x27;https:&#x2F;&#x2F;youtu.be&#x2F;m72lrbINkak&#x27;&gt;published a YouTube tutorial&lt;&#x2F;a&gt;, did some docs improvements in their repo (&lt;a href=&#x27;https:&#x2F;&#x2F;github.com&#x2F;get-convex&#x2F;convex-backend&#x2F;pull&#x2F;133&#x27;&gt;1&lt;&#x2F;a&gt;, &lt;a href=&#x27;https:&#x2F;&#x2F;github.com&#x2F;get-convex&#x2F;convex-backend&#x2F;pull&#x2F;124&#x27;&gt;2&lt;&#x2F;a&gt;), and &lt;a href=&#x27;https:&#x2F;&#x2F;github.com&#x2F;get-convex&#x2F;convex-backend&#x2F;pull&#x2F;165&#x27;&gt;even fixed a self-hosting bug&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;So when they reached out to me about this program, my immediate thought was: I’ve already been advocating for Convex. Why not do it as a Champion?&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.convex.dev&#x2F;champions&quot;&gt;Convex Champions&lt;&#x2F;a&gt; is an amazing
program that brings together people who are interested in Convex. It also comes
with some pretty cool benefits, so make sure to check it out. Thanks to everyone
on the Convex team for inviting me into this community and for building such an
amazing product!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Convex 101 by Building a Multiplayer Game</title>
          <pubDate>Tue, 05 Aug 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://efe.dev/blog/convex-multiplayer-game-talk/</link>
          <guid>https://efe.dev/blog/convex-multiplayer-game-talk/</guid>
          <description xml:base="https://efe.dev/blog/convex-multiplayer-game-talk/">&lt;div style=&quot;width: 100%; height: 500px;&quot;&gt;
  &lt;iframe
    src=&quot;https:&#x2F;&#x2F;www.canva.com&#x2F;design&#x2F;DAGupo4hCT8&#x2F;0jRica4mxo_PElFlLG30HA&#x2F;view?embed&quot;
    width=&quot;100%&quot;
    height=&quot;100%&quot;
    style=&quot;border: 0;&quot;
  &gt;&lt;&#x2F;iframe&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;I gave this talk in Munich, Germany at the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.meetup.com&#x2F;munich-typescript-meetup&#x2F;events&#x2F;308962810&quot;&gt;Munich Typescript Meetup: Community Edition&lt;&#x2F;a&gt; and at &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.meetup.com&#x2F;munich-typescript-meetup&#x2F;events&#x2F;313511838&#x2F;&quot;&gt;Munich TypeScript: Fullstack Apps Without the Drama&lt;&#x2F;a&gt;.
I shared my experience building my small multiplayer game &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;geowar.io&quot;&gt;GeoWar.io&lt;&#x2F;a&gt; using &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;convex.dev&quot;&gt;Convex&lt;&#x2F;a&gt; over a weekend.
It was an incredible experience speaking in front of tens of people.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Building a Multiplayer Game with Convex Over a Weekend</title>
          <pubDate>Mon, 28 Jul 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://efe.dev/blog/convex-multiplayer-game/</link>
          <guid>https://efe.dev/blog/convex-multiplayer-game/</guid>
          <description xml:base="https://efe.dev/blog/convex-multiplayer-game/">&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;efe.dev&#x2F;blog&#x2F;convex-multiplayer-game&#x2F;convex.png&quot; alt=&quot;Convex Logo&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I first heard about &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.convex.dev&#x2F;&quot;&gt;Convex&lt;&#x2F;a&gt; through &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;t3.chat&#x2F;&quot;&gt;t3.chat&lt;&#x2F;a&gt; months ago, and ever since
then, it’s been buried deep in my “someday”
learning list.
When I finally had a weekend to nerd out, I decided to give it a go.
I already had an app idea in mind that seemed like a perfect fit for Convex: a real-time, multiplayer browser clicker
game.
A small homage to &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.erepublik.com&#x2F;en&quot;&gt;eRepublik&lt;&#x2F;a&gt;, which I loved playing as a kid.&lt;&#x2F;p&gt;
&lt;p&gt;In this article, I will share my experience building a multiplayer game with Convex over a weekend, walking you through the steps I took, the challenges I faced, and the solutions I found.
During which we will cover the basics of Convex.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;what-s-the-project&quot;&gt;What’s the Project?&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;efe.dev&#x2F;blog&#x2F;convex-multiplayer-game&#x2F;geowar.png&quot; alt=&quot;GeoWar is a multiplayer game based on world conquest&quot; &#x2F;&gt;
&lt;em&gt;Ah yes, the Swedish Empire where the sun never sets&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This was the end result: &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.geowar.io&#x2F;&quot;&gt;GeoWar.io&lt;&#x2F;a&gt;, a game based on world conquest.
The rules are simple, you automatically get assigned to your country of origin.
You either defend or attack a territory by clicking on it.
Each territory has a point of its own and if you reduce a territory’s points to zero, you conquer it.
Countries that are currently invaded are marked with stripes.&lt;&#x2F;p&gt;
&lt;p&gt;The entire backend is built with Convex. The frontend is built with &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;nextjs.org&#x2F;&quot;&gt;Next.js&lt;&#x2F;a&gt; and the map is made with &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.amcharts.com&#x2F;&quot;&gt;amcharts&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;what-even-is-convex&quot;&gt;What even is Convex?&lt;&#x2F;h3&gt;
&lt;p&gt;Convex gives you a backend entirely with TypeScript, including database, backend functions, authentication and real-time
syncing.
No servers, no REST APIs, no manual WebSockets.
End-to-end type safety makes for a great developer experience.
It also has an extensive set of libraries that help you to build your app faster.&lt;&#x2F;p&gt;
&lt;p&gt;Building with Convex instead of a traditional backend allowed me to focus on the game logic instead of struggling with boilerplate code.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;our-first-schema&quot;&gt;Our first schema&lt;&#x2F;h3&gt;
&lt;p&gt;Convex stores JSON-like documents and can be used with or without a schema. Here’s an example schema from my database, I
simplified it a bit to focus on the core concepts:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#5C6A72, #BABED8); background-color: light-dark(#FDF6E3, #292D3E);&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt; convex&#x2F;schema.ts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#DF69BA, #89DDFF);&quot;&gt;import&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt; {&lt;&#x2F;span&gt;&lt;span&gt;defineSchema&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; defineTable&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F85552, #89DDFF);&quot;&gt; from&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #C3E88D);&quot;&gt;convex&#x2F;server&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#DF69BA, #89DDFF);&quot;&gt;import&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt; {&lt;&#x2F;span&gt;&lt;span&gt;v&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F85552, #89DDFF);&quot;&gt; from&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #C3E88D);&quot;&gt;convex&#x2F;values&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#DF69BA, #89DDFF);&quot;&gt;export&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F85552, #89DDFF);&quot;&gt; default&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt; defineSchema&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;    countries&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt; defineTable&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;        name&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; v&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt; Name of the country&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;        points&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; v&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt;number&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt; If the point reaches 0, the country gets conquered&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;        code&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; v&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt;union&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt; ISO 3166-1 alpha-2 country code&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            v&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt;literal&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #C3E88D);&quot;&gt;DE&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            v&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt;literal&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #C3E88D);&quot;&gt;US&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt;            &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt; ... other countries&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        )&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;    }&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt;index&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #C3E88D);&quot;&gt;by_code&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #C3E88D);&quot;&gt;code&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt; An index by country code&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt; ... other schemas&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The validator builder &lt;code&gt;v&lt;&#x2F;code&gt; is used to define the type of documents in each table. It supports various simple types,
as well as more complex structures like string literals, records or optional fields.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;efe.dev&#x2F;blog&#x2F;convex-multiplayer-game&#x2F;tables.png&quot; alt=&quot;Viewing tables in Convex Dashboard&quot; title=&quot;Viewing tables in Convex Dashboard&quot; &#x2F;&gt;
&lt;em&gt;What the schema above looks like in Convex Dashboard&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;We interact with our database through &lt;strong&gt;mutations&lt;&#x2F;strong&gt; and &lt;strong&gt;queries&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Mutations&lt;&#x2F;strong&gt; are used to make changes to data in the database.
They can also handle authentication checks or other business logic, and may return a response to the client.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Queries&lt;&#x2F;strong&gt; are the core of your backend API.
They retrieve data from the database, optionally perform authentication or business logic, and return the result to the
client.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;mutating-data&quot;&gt;Mutating data&lt;&#x2F;h3&gt;
&lt;p&gt;Let’s start with a mutation.
Again, this is a simplified version of a real code snippet.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#5C6A72, #BABED8); background-color: light-dark(#FDF6E3, #292D3E);&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt; convex&#x2F;user.ts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#DF69BA, #89DDFF);&quot;&gt;import&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt; {&lt;&#x2F;span&gt;&lt;span&gt;mutation&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F85552, #89DDFF);&quot;&gt; from&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #C3E88D);&quot;&gt;.&#x2F;_generated&#x2F;server&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#DF69BA, #89DDFF);&quot;&gt;import&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt; {&lt;&#x2F;span&gt;&lt;span&gt;v&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F85552, #89DDFF);&quot;&gt; from&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #C3E88D);&quot;&gt;convex&#x2F;values&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#DF69BA, #89DDFF);&quot;&gt;export&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F57D26, #C792EA);&quot;&gt; const&lt;&#x2F;span&gt;&lt;span&gt; handleClick&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F57D26, #89DDFF);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt; mutation&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;    args&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;        targetCountryCode&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; v&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt;union&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            v&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt;literal&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #C3E88D);&quot;&gt;DE&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            v&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt;literal&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #C3E88D);&quot;&gt;US&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt;            &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt; ... other countries&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        )&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;    }&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt;    handler&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F57D26, #C792EA);&quot;&gt; async&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt; (&lt;&#x2F;span&gt;&lt;span&gt;ctx&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; args&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F57D26, #C792EA);&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt;        &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt; Get the corresponding country&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#F57D26, #C792EA);&quot;&gt;        const&lt;&#x2F;span&gt;&lt;span&gt; targetCountry&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F57D26, #89DDFF);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F85552, #89DDFF);&quot;&gt; await&lt;&#x2F;span&gt;&lt;span&gt; ctx&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;db&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;            .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt;query&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #C3E88D);&quot;&gt;countries&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;            .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt;withIndex&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #C3E88D);&quot;&gt;by_code&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt; (&lt;&#x2F;span&gt;&lt;span&gt;q&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F57D26, #C792EA);&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; q&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt;eq&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #C3E88D);&quot;&gt;code&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; args&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;targetCountryCode&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;            .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt;first&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt;        &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt; Rest of the business logic removed for simplicity&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt;        &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt; Update the country&amp;#39;s points&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#F85552, #89DDFF);&quot;&gt;        await&lt;&#x2F;span&gt;&lt;span&gt; ctx&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;db&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt;patch&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;targetCountry&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;_id&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;            points&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; targetCountry&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;points&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F57D26, #89DDFF);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DF69BA, #F78C6C);&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;        }&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;    }&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Let’s break down what’s happening here:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;We created a new public &lt;strong&gt;mutation&lt;&#x2F;strong&gt;, which gets exposed automatically.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;args&lt;&#x2F;strong&gt; takes care of validation and types for us, using v similarly to how we defined our schemas.&lt;&#x2F;li&gt;
&lt;li&gt;We first queried our database with &lt;code&gt;ctx.db.query&lt;&#x2F;code&gt; using the indexed field we defined before.&lt;&#x2F;li&gt;
&lt;li&gt;Then we updated this record with &lt;code&gt;ctx.db.patch&lt;&#x2F;code&gt;, reducing the points by one&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The code is pretty standard and simple. However, in a typical backend setup, this simple looking code would have a
hidden danger: &lt;strong&gt;race conditions&lt;&#x2F;strong&gt;. Luckily, Convex solves this for us.&lt;&#x2F;p&gt;
&lt;p&gt;Let’s imagine two players clicking simultaneously:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Both read the country &lt;code&gt;DE&lt;&#x2F;code&gt; at ten points&lt;&#x2F;li&gt;
&lt;li&gt;Both calculate &lt;code&gt;points - 1 = 9&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Both update the record as &lt;code&gt;points = 9&lt;&#x2F;code&gt;, meaning one update overwrites the other, losing data.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This is a classic &lt;strong&gt;race condition&lt;&#x2F;strong&gt;, more specifically a &lt;strong&gt;lost update problem&lt;&#x2F;strong&gt;, where two users overwrite each
other’s updates.&lt;&#x2F;p&gt;
&lt;p&gt;…but how does Convex solve this issue for us?&lt;&#x2F;p&gt;
&lt;p&gt;Convex runs every mutation as an isolated, serializable transaction.
Convex mutations are also deterministic, so they can be retried.
This allows you to write your code as if everything happens in order, even if it’s not.&lt;&#x2F;p&gt;
&lt;p&gt;No locks, no manually written transactions, all of it just works magically.&lt;&#x2F;p&gt;
&lt;p&gt;If you want to learn more about how Convex works under the hood, I recommend reading
their &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.convex.dev&#x2F;understanding&#x2F;&quot;&gt;Understand Convex&lt;&#x2F;a&gt; section.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;reading-data&quot;&gt;Reading data&lt;&#x2F;h3&gt;
&lt;p&gt;That’s enough boring talk. Let’s take a look at a query:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#5C6A72, #BABED8); background-color: light-dark(#FDF6E3, #292D3E);&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt; convex&#x2F;user.ts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#DF69BA, #89DDFF);&quot;&gt;import&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt; {&lt;&#x2F;span&gt;&lt;span&gt;query&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F85552, #89DDFF);&quot;&gt; from&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #C3E88D);&quot;&gt;.&#x2F;_generated&#x2F;server&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#DF69BA, #89DDFF);&quot;&gt;export&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F57D26, #C792EA);&quot;&gt; const&lt;&#x2F;span&gt;&lt;span&gt; getAllCountries&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F57D26, #89DDFF);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt; query&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;    args&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt;    handler&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F57D26, #C792EA);&quot;&gt; async&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt; (&lt;&#x2F;span&gt;&lt;span&gt;ctx&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F57D26, #C792EA);&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#F57D26, #C792EA);&quot;&gt;        const&lt;&#x2F;span&gt;&lt;span&gt; countries&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F57D26, #89DDFF);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F85552, #89DDFF);&quot;&gt; await&lt;&#x2F;span&gt;&lt;span&gt; ctx&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;db&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt;query&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #C3E88D);&quot;&gt;countries&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt;collect&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#F85552, #89DDFF);&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span&gt; countries&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;    }&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Just like our mutation, this creates a public &lt;code&gt;query&lt;&#x2F;code&gt;.
Queries are how we read data using Convex. Through WebSockets, they subscribe to any changes that happen in our
database.&lt;&#x2F;p&gt;
&lt;p&gt;Let’s start using our Convex functions in our frontend starting with our mutation.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#5C6A72, #BABED8); background-color: light-dark(#FDF6E3, #292D3E);&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#DF69BA, #89DDFF);&quot;&gt;import&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt; {&lt;&#x2F;span&gt;&lt;span&gt;useMutation&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F85552, #89DDFF);&quot;&gt; from&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #C3E88D);&quot;&gt;convex&#x2F;react&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#DF69BA, #89DDFF);&quot;&gt;import&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt; {&lt;&#x2F;span&gt;&lt;span&gt;api&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F85552, #89DDFF);&quot;&gt; from&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #C3E88D);&quot;&gt;..&#x2F;..&#x2F;convex&#x2F;_generated&#x2F;api&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#F57D26, #C792EA);&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; handleClick&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F57D26, #89DDFF);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt; useMutation&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;api&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;user&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;handleClick&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Out of the box, Convex supports many UI libraries&#x2F;frameworks, this allows you to focus on building your business logic.
Here we see the &lt;code&gt;useMutation&lt;&#x2F;code&gt; hook of &lt;code&gt;convex&#x2F;react&lt;&#x2F;code&gt;. As an argument of this hook, we pass our autogenerated api object.&lt;&#x2F;p&gt;
&lt;p&gt;Notice how &lt;code&gt;api.user.handleClick&lt;&#x2F;code&gt; maps to our function &lt;code&gt;handleClick&lt;&#x2F;code&gt;, inside the &lt;code&gt;convex&#x2F;user&lt;&#x2F;code&gt; file.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;efe.dev&#x2F;blog&#x2F;convex-multiplayer-game&#x2F;typed.png&quot; alt=&quot;Everything is already typed!&quot; &#x2F;&gt;
&lt;em&gt;Everything is already typed!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#5C6A72, #BABED8); background-color: light-dark(#FDF6E3, #292D3E);&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt; Trigger the mutation&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#F85552, #89DDFF);&quot;&gt;await&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt; handleClick&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    token&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    targetCountryCode&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Since our arguments are already typed, everything is type safe in development time, and we have our validation in place
for any runtime surprises.
This makes for a great DX.&lt;&#x2F;p&gt;
&lt;p&gt;This should give us the basics of clicking on a territory and reducing its points.
However, we would have a slight delay in user interactions because as of writing this Convex only has servers in the US.
We are building a game, we should improve that.&lt;&#x2F;p&gt;
&lt;p&gt;Luckily, we can manipulate Convex’s local state to implement &lt;strong&gt;optimistic updates&lt;&#x2F;strong&gt;, a technique where the UI pretends
the update succeeded before the server confirms.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#5C6A72, #BABED8); background-color: light-dark(#FDF6E3, #292D3E);&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#F57D26, #C792EA);&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; handleClick&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F57D26, #89DDFF);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt; useMutation&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;api&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;user&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;handleClick&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt;withOptimisticUpdate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;    (&lt;&#x2F;span&gt;&lt;span&gt;localQueryStore&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; args&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F57D26, #C792EA);&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt;        &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt; Get the local state&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#F57D26, #C792EA);&quot;&gt;        const&lt;&#x2F;span&gt;&lt;span&gt; localCountries&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F57D26, #89DDFF);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; localQueryStore&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt;getQuery&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;api&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;user&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;getAllCountries&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt;        &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt; Rest of the business logic removed for simplicity&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt;        &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt; Update the local state&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        localQueryStore&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt;setQuery&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;api&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;user&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;getAllCountries&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; updatedCountries&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;    }&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now, let’s use our query to get live data.
We could use the query counterpart of &lt;code&gt;useMutation&lt;&#x2F;code&gt;, aka &lt;code&gt;useQuery&lt;&#x2F;code&gt;, but since we are using Next.js, we could benefit
from some Next-specific hooks.
This way we can use the full power of server-side rendering.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#5C6A72, #BABED8); background-color: light-dark(#FDF6E3, #292D3E);&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt; Preload the query in a server component...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#F57D26, #C792EA);&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; preloadedCountries&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F57D26, #89DDFF);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F85552, #89DDFF);&quot;&gt; await&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt; preloadQuery&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;api&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;user&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;getAllCountries&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt; ... and use it in a client component&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#F57D26, #C792EA);&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; countries&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F57D26, #89DDFF);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt; usePreloadedQuery&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;preloadedCountries&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That’s it.
We don’t have states like &lt;code&gt;loading&lt;&#x2F;code&gt;, or an additional function like &lt;code&gt;refetch&lt;&#x2F;code&gt; to query the backend again.&lt;&#x2F;p&gt;
&lt;p&gt;Convex queries are &lt;strong&gt;reactive&lt;&#x2F;strong&gt; by default. Any change made to the database is reflected in our frontend.&lt;&#x2F;p&gt;
&lt;p&gt;We have everything in place ready for the world domination.
However, right now the users can spam clicks (or even worse they can bombard the WebSocket connection, like my friends!)&lt;&#x2F;p&gt;
&lt;p&gt;We could implement a simple client side solution, but that wouldn’t solve the core issue.
Convex endpoints are &lt;strong&gt;public&lt;&#x2F;strong&gt; unless we define them explicitly as internal.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;convex-components-to-rescue&quot;&gt;Convex components to rescue!&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.convex.dev&#x2F;components&quot;&gt;Convex components&lt;&#x2F;a&gt; are modular building blocks that allow us to add new features to
our Convex backends, and they happen to have a &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.convex.dev&#x2F;components&#x2F;rate-limiter&quot;&gt;Rate Limiter&lt;&#x2F;a&gt; component.
This makes it a &lt;em&gt;breeze&lt;&#x2F;em&gt; to add a rate limiter to our app.&lt;&#x2F;p&gt;
&lt;p&gt;We start by creating our Rate Limiter:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#5C6A72, #BABED8); background-color: light-dark(#FDF6E3, #292D3E);&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#DF69BA, #89DDFF);&quot;&gt;import&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt; {&lt;&#x2F;span&gt;&lt;span&gt;RateLimiter&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; SECOND&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F85552, #89DDFF);&quot;&gt; from&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #C3E88D);&quot;&gt;@convex-dev&#x2F;rate-limiter&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#F57D26, #C792EA);&quot;&gt;const&lt;&#x2F;span&gt;&lt;span&gt; rateLimiter&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F57D26, #89DDFF);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F85552, #89DDFF);&quot;&gt; new&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt; RateLimiter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;components&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;rateLimiter&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;    click&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;kind&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #C3E88D);&quot;&gt;token bucket&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt; rate&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DF69BA, #F78C6C);&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt; period&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; SECOND&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then check if the rate limit is exceeded or not in our mutation:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#5C6A72, #BABED8); background-color: light-dark(#FDF6E3, #292D3E);&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#DF69BA, #89DDFF);&quot;&gt;export&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F57D26, #C792EA);&quot;&gt; const&lt;&#x2F;span&gt;&lt;span&gt; handleClick&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F57D26, #89DDFF);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt; mutation&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;    args&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;        targetCountryCode&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; v&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt;union&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            v&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt;literal&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #C3E88D);&quot;&gt;DE&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            v&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt;literal&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #C3E88D);&quot;&gt;US&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt;            &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt; ... other countries&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        )&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;    }&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt;    handler&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F57D26, #C792EA);&quot;&gt; async&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt; (&lt;&#x2F;span&gt;&lt;span&gt;ctx&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; args&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F57D26, #C792EA);&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#F57D26, #C792EA);&quot;&gt;        const&lt;&#x2F;span&gt;&lt;span&gt; targetCountry&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F57D26, #89DDFF);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F85552, #89DDFF);&quot;&gt; await&lt;&#x2F;span&gt;&lt;span&gt; ctx&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;db&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;            .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt;query&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #C3E88D);&quot;&gt;countries&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;            .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt;withIndex&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #C3E88D);&quot;&gt;by_code&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt; (&lt;&#x2F;span&gt;&lt;span&gt;q&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F57D26, #C792EA);&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; q&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt;eq&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #C3E88D);&quot;&gt;code&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; args&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;targetCountryCode&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;            .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt;first&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#F57D26, #C792EA);&quot;&gt;        const&lt;&#x2F;span&gt;&lt;span&gt; id&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F57D26, #89DDFF);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #C3E88D);&quot;&gt;some-user-id&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#F57D26, #C792EA);&quot;&gt;        const&lt;&#x2F;span&gt;&lt;span&gt; status&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F57D26, #89DDFF);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F85552, #89DDFF);&quot;&gt; await&lt;&#x2F;span&gt;&lt;span&gt; rateLimiter&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt;limit&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;ctx&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #C3E88D);&quot;&gt;click&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;key&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; id&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#F85552, #89DDFF);&quot;&gt;        if&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F57D26, #89DDFF);&quot;&gt;!&lt;&#x2F;span&gt;&lt;span&gt;status&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;ok&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#F85552, #89DDFF);&quot;&gt;            throw&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F85552, #89DDFF);&quot;&gt; new&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt; Error&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #C3E88D);&quot;&gt;Rate limit exceeded by &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #89DDFF);&quot;&gt;${&lt;&#x2F;span&gt;&lt;span&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #89DDFF);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #C3E88D);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DFA000, #89DDFF);&quot;&gt;`&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt;        &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #676E95);font-style: italic;&quot;&gt; Rest of the business logic removed for simplicity&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#F85552, #89DDFF);&quot;&gt;        await&lt;&#x2F;span&gt;&lt;span&gt; ctx&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;db&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8DA101, #82AAFF);&quot;&gt;patch&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;targetCountry&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;_id&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;            points&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; targetCountry&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#939F91, #89DDFF);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;points&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#F57D26, #89DDFF);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#DF69BA, #F78C6C);&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;        }&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #F07178);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;    }&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#5C6A72, #89DDFF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There are a lot more Convex Components like crons, migrations, or presence data.&lt;&#x2F;p&gt;
&lt;p&gt;With that we’ve pretty much covered all the basics of Convex; while there’s still a lot to learn about Convex this is
enough for us to build a game.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;it-s-not-all-perfect&quot;&gt;It’s not all perfect&lt;&#x2F;h3&gt;
&lt;p&gt;Before wrapping up, I want to mention some “downsides” of using Convex.
After all, there’s no silver bullet in software engineering.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;some-features-are-lacking&quot;&gt;Some features are lacking&lt;&#x2F;h4&gt;
&lt;p&gt;There were some cases that felt like Convex was missing some basics.
Like how you can’t select only a certain fields from a table (similar to SQL’s &lt;code&gt;SELECT x&lt;&#x2F;code&gt;), which quickly increases the
database bandwidth usage. (In fact, I switched to self-hosted for this reason)
To be fair, our main query is poorly optimized, so the blame is partially on me. Their solution for this problem is to
split your tables.&lt;&#x2F;p&gt;
&lt;p&gt;Another example would be how you can’t get the remote IP address in a Convex mutation&#x2F;query.
I had to hack my way around safely getting the IP address, which was a bit annoying.&lt;&#x2F;p&gt;
&lt;p&gt;It’s worth noting “batteries included” solutions always lack &lt;strong&gt;something&lt;&#x2F;strong&gt; that we want.
This is also, hilariously, mentioned in &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.convex.sucks&#x2F;&quot;&gt;convex.sucks&lt;&#x2F;a&gt;. (no really, click on that link)&lt;&#x2F;p&gt;
&lt;h4 id=&quot;convex-might-require-a-different-mental-model&quot;&gt;Convex might require a different mental model&lt;&#x2F;h4&gt;
&lt;p&gt;This is not necessarily a downside, but Convex isn’t a traditional backend solution.
Especially since Convex mutations and queries must be &lt;strong&gt;deterministic&lt;&#x2F;strong&gt;, so there are some limitations to what you can
do.
Considering how Convex works, this limitation makes sense, but it might require a different mental model at times.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;that-s-it&quot;&gt;That’s it&lt;&#x2F;h3&gt;
&lt;p&gt;I think Convex is an awesome tool. Is it perfect? No, it’s not.
But amongst all the similar tools, I think it is the most convenient one.
The developers behind it are brilliant people, and they are active on all platforms.&lt;&#x2F;p&gt;
&lt;p&gt;So definitely give it a go and make sure to play some &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.geowar.io&#x2F;&quot;&gt;GeoWar&lt;&#x2F;a&gt; to increase my cloud bills.&lt;&#x2F;p&gt;
&lt;p&gt;Thanks for reading!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>What Makes Supabase Special?</title>
          <pubDate>Fri, 18 Jul 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://efe.dev/blog/supabase-special/</link>
          <guid>https://efe.dev/blog/supabase-special/</guid>
          <description xml:base="https://efe.dev/blog/supabase-special/">&lt;div style=&quot;width: 100%; height: 500px;&quot;&gt;
  &lt;iframe
    src=&quot;https:&#x2F;&#x2F;www.canva.com&#x2F;design&#x2F;DAGssg7W8Kk&#x2F;-fQOmnYIqGMxbldN8XfLwQ&#x2F;view?embed&quot;
    width=&quot;100%&quot;
    height=&quot;100%&quot;
    style=&quot;border: 0;&quot;
  &gt;&lt;&#x2F;iframe&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;I hosted the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;luma.com&#x2F;89ncojfr&quot;&gt;Launch week 15 Supabase&lt;&#x2F;a&gt; event in Izmir, Turkey.
I gave a short talk on what makes Supabase special and why I love using it.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>geowar.io - A Realtime Multiplayer Game</title>
          <pubDate>Fri, 04 Jul 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://efe.dev/projects/geowar-io/</link>
          <guid>https://efe.dev/projects/geowar-io/</guid>
          <description xml:base="https://efe.dev/projects/geowar-io/">&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;efe.dev&#x2F;projects&#x2F;geowar-io&#x2F;cover.png&quot; alt=&quot;A screenshot of GeoWar&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.geowar.io&quot;&gt;GeoWar.io&lt;&#x2F;a&gt; is a realtime multiplayer game with the goal of conquering the world.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;idea&quot;&gt;Idea&lt;&#x2F;h3&gt;
&lt;p&gt;I used to play a great browser game called &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.erepublik.com&#x2F;en&quot;&gt;eRepublik&lt;&#x2F;a&gt;. I think it is one of the best browser games I’ve ever played.
It inspired this idea with the theme of countries and world domination.&lt;&#x2F;p&gt;
&lt;p&gt;The name is inspired by GeoGuessr, a game where you guess locations based on street view images.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;goals&quot;&gt;Goals&lt;&#x2F;h3&gt;
&lt;p&gt;I wanted to learn more about &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.convex.dev&#x2F;&quot;&gt;Convex&lt;&#x2F;a&gt; it is an amazing piece of technology that allows you to build realtime applications with ease.
So I finally decided to build this idea with Convex.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;tech-stack&quot;&gt;Tech Stack&lt;&#x2F;h3&gt;
&lt;p&gt;Typescript, React, TailwindCSS, amcharts, Convex&lt;&#x2F;p&gt;
&lt;h3 id=&quot;key-features&quot;&gt;Key Features&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot; checked=&quot;&quot;&#x2F;&gt;
Realtime multiplayer interactions&lt;&#x2F;li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot; checked=&quot;&quot;&#x2F;&gt;
A smooth and interactive world amp&lt;&#x2F;li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot; checked=&quot;&quot;&#x2F;&gt;
Basic game mechanics (e.g attacking, defending, conquering)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot; checked=&quot;&quot;&#x2F;&gt;
Leaderboards and stats&lt;&#x2F;li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&#x2F;&gt;
Advanced game mechanics (e.g advanced attacks, chat, alliances)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;learnings&quot;&gt;Learnings&lt;&#x2F;h3&gt;
&lt;p&gt;I learned a lot about Convex and how to build realtime applications with it.&lt;&#x2F;p&gt;
&lt;p&gt;I already used amcharts in my previous project, but I learned a lot about how to use it in a more advanced way. I built some customizations around it which made the experience smoother.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;aftermath&quot;&gt;Aftermath&lt;&#x2F;h3&gt;
&lt;p&gt;I &lt;strong&gt;really&lt;&#x2F;strong&gt; enjoyed learning about Convex. I already made some contributions to their documentation while I was building this project.
I plan to more contributions to Convex in the future and use it in more projects.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>How I Finally Made Work Journaling a Habit</title>
          <pubDate>Tue, 29 Apr 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://efe.dev/blog/work-journal-habit/</link>
          <guid>https://efe.dev/blog/work-journal-habit/</guid>
          <description xml:base="https://efe.dev/blog/work-journal-habit/">&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;efe.dev&#x2F;blog&#x2F;work-journal-habit&#x2F;header.jpg&quot; alt=&quot;What logging with gj looks like — zero fluff, sent straight to Notion.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;As a kid, I loved the idea of keeping a journal. I’d buy a fresh notebook every year, write for two days, and forget about it completely.&lt;&#x2F;p&gt;
&lt;p&gt;Fast-forward to adulthood: I’m a software engineer, and that habit hadn’t improved. The first time I heard about work journals and “brag documents,” I was intrigued. I gave it a shot, then dropped it. Again.&lt;&#x2F;p&gt;
&lt;p&gt;But this time, I was determined to make it stick. It took a few rounds of trial and error, but eventually, I found a system that worked. Here’s what actually helped me turn work journaling from a good intention into a real habit.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;i-stopped-trying-to-be-perfect&quot;&gt;I stopped trying to be perfect&lt;&#x2F;h3&gt;
&lt;p&gt;I skipped a day of journaling? Whatever. I would just go back and journal what I could remember. Once I gave myself permission to be inconsistent, I ironically became more consistent.&lt;&#x2F;p&gt;
&lt;p&gt;That mindset shift - from perfect to “good enough” - made the habit sustainable.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;i-used-tools-i-actually-enjoy&quot;&gt;I used tools I actually enjoy&lt;&#x2F;h3&gt;
&lt;p&gt;There’s no “best” tool for journaling - there’s just the one you’ll actually use. For me, that was Notion. It felt natural because I already used it for docs and personal notes. For others, it might be a physical notebook, a Markdown file, or even a &lt;code&gt;notes.txt&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;If opening the tool feels annoying, you won’t stick with it.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;i-reduced-the-friction&quot;&gt;I reduced the friction&lt;&#x2F;h3&gt;
&lt;p&gt;If you’ve read &lt;em&gt;Atomic Habits&lt;&#x2F;em&gt; by James Clear, you’ve probably seen this idea: make the habit so easy it’s hard to avoid.&lt;&#x2F;p&gt;
&lt;p&gt;That’s what I did. &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;efekrskl&#x2F;gj&quot;&gt;I built a tiny CLI app in Rust called &lt;code&gt;gj&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; that lets me log entries straight from the terminal to Notion. The terminal is the one tool I use constantly - adding journaling into that flow meant zero friction.&lt;&#x2F;p&gt;
&lt;p&gt;Merged a big PR? I log it in two seconds, right where I am. No context switch, no excuses.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;efe.dev&#x2F;blog&#x2F;work-journal-habit&#x2F;snippet.png&quot; alt=&quot;What logging with gj looks like — zero fluff, sent straight to Notion.&quot; title=&quot;What logging with `gj` looks like — zero fluff, sent straight to Notion.&quot; &#x2F;&gt;
&lt;em&gt;What logging with &lt;code&gt;gj&lt;&#x2F;code&gt; looks like — zero fluff, sent straight to Notion.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I’m not saying you should use my tool — just find what makes it effortless for you.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;i-started-seeing-the-impact-and-that-kept-me-going&quot;&gt;I started seeing the impact — and that kept me going&lt;&#x2F;h3&gt;
&lt;p&gt;Once I had some consistency, what really kept the habit alive wasn’t the tool or routine — it was the effect it had on me. Because journaling became easy, I started logging more often: small wins, tricky bugs, even small things like helping a teammate. Most of it wouldn’t show up in Jira — but it mattered.&lt;&#x2F;p&gt;
&lt;p&gt;Seeing that progress, in my own words, helped me feel more confident. It didn’t erase impostor syndrome, but it gave me something real to look back on.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Now, it’s not just a habit. It’s a quiet tool that helps me stay focused, reflect on what I’ve done, and feel a little more confident on the hard days.&lt;&#x2F;p&gt;
&lt;p&gt;If you’ve tried and failed before, that’s fine. What worked for me might not work for you — the key is finding something you’ll actually stick with.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>gj - dead simple journaling CLI</title>
          <pubDate>Thu, 10 Apr 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://efe.dev/projects/gj-cli/</link>
          <guid>https://efe.dev/projects/gj-cli/</guid>
          <description xml:base="https://efe.dev/projects/gj-cli/">&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;efe.dev&#x2F;projects&#x2F;gj-cli&#x2F;cover.png&quot; alt=&quot;An example CLI prompt with gj&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;efekrskl&#x2F;gj&quot;&gt;gj is an open-source journaling CLI&lt;&#x2F;a&gt; that allows you to write journal entries directly from the terminal and save them to Notion.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;idea&quot;&gt;Idea&lt;&#x2F;h3&gt;
&lt;p&gt;I came up with the idea for gj after struggling with sticking to work journaling. &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.notion.com&#x2F;&quot;&gt;Notion&lt;&#x2F;a&gt; is already a great tool for journaling.
After reading &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;jamesclear.com&#x2F;atomic-habits&quot;&gt;Atomic Habits by James Clear&lt;&#x2F;a&gt; I realized, there was still some friction in the process of writing journal entries.
So I decided to build a tool that would remove that friction and take advantage of me already using my terminal every day.&lt;&#x2F;p&gt;
&lt;p&gt;My original approach was to build a hook that would save your commits and turn them into journal entries.
However, I wasn’t sure if this would be legal or not. Instead, I kept how one would journal similar to how one would write a commit message.&lt;&#x2F;p&gt;
&lt;p&gt;“gj” is a play on the common phrase “good job” that you would say to yourself after completing a task or writing a journal entry.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;goals&quot;&gt;Goals&lt;&#x2F;h3&gt;
&lt;p&gt;The goal was to build a simple CLI tool that allows you to write your journals directly from the terminal and save them to Notion.&lt;&#x2F;p&gt;
&lt;p&gt;Additionally, I wanted to learn &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.rust-lang.org&#x2F;&quot;&gt;Rust&lt;&#x2F;a&gt; and build a tool with it. I think it is an amazing language and I hope to use it more in the future.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;tech-stack&quot;&gt;Tech Stack&lt;&#x2F;h3&gt;
&lt;p&gt;Well, Rust. And Notion’s API.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;key-features&quot;&gt;Key Features&lt;&#x2F;h3&gt;
&lt;p&gt;These were my must-haves:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot; checked=&quot;&quot;&#x2F;&gt;
Journal from the terminal with a simple syntax (&lt;code&gt;gj &quot;hello world&quot;&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot; checked=&quot;&quot;&#x2F;&gt;
Automatically create the Notion page and database&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Some nice-to-haves:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&#x2F;&gt;
Brag functionality (summarize entries and automatically create a brag document)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&#x2F;&gt;
Support non-Notion databases (e.g. local files, other APIs)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;learnings&quot;&gt;Learnings&lt;&#x2F;h3&gt;
&lt;p&gt;Although a simple project through gj I learned a lot about Rust and how to build a CLI tool with it.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;aftermath&quot;&gt;Aftermath&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;efe.dev&#x2F;projects&#x2F;gj-cli&#x2F;notion.png&quot; alt=&quot;The calendar view in Notion with gj entries&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Due to some limitations in Notion’s API, gj was not “as automated” as I wanted it to be. It still ended up being a useful tool.
It attracted some users and even some contributors.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>YouTube Channel</title>
          <pubDate>Tue, 25 Feb 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://efe.dev/projects/youtube-channel/</link>
          <guid>https://efe.dev/projects/youtube-channel/</guid>
          <description xml:base="https://efe.dev/projects/youtube-channel/">&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;efe.dev&#x2F;projects&#x2F;youtube-channel&#x2F;cover.jpg&quot; alt=&quot;Youtube channel profile picture&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;@efekarasakaldev&quot;&gt;This is my YouTube channel&lt;&#x2F;a&gt; where I share software engineering content.&lt;&#x2F;p&gt;
&lt;p&gt;In under a year, it has reached ~5k subscribers, and over 250k views. I’m really proud of the channel and the surrounding community!&lt;&#x2F;p&gt;
&lt;p&gt;Special thanks to &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.jetbrains.com&#x2F;community&#x2F;content-creators&#x2F;&quot;&gt;JetBrains and their Content Creators Program&lt;&#x2F;a&gt;.
I’ve been using their products my entire careers, and it is a great opportunity to partner with them.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>ferne.io - Mark Your Travels</title>
          <pubDate>Sat, 04 Jan 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://efe.dev/projects/ferne-io/</link>
          <guid>https://efe.dev/projects/ferne-io/</guid>
          <description xml:base="https://efe.dev/projects/ferne-io/">&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;efe.dev&#x2F;projects&#x2F;ferne-io&#x2F;cover.png&quot; alt=&quot;My profile page on ferne.io&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Update 25.01.2026: Since I’m not actively developing ferne anymore, I’ve moved it under https:&#x2F;&#x2F;ferne.efe.dev.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;ferne.efe.dev&quot;&gt;ferne.io&lt;&#x2F;a&gt; is an online platform where you can mark your travels on a world map, share your profile, and collect achievements.
I &lt;strong&gt;love&lt;&#x2F;strong&gt; to travel, and I thought it would be fun to create such a platform to record my adventures.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;idea&quot;&gt;Idea&lt;&#x2F;h3&gt;
&lt;p&gt;I initially came up with this idea because I was already using some existing solutions for tracking my travels. However, I was unhappy with how
convoluted these solutions were. I wanted something minimalistic, shareable and embeddable.&lt;&#x2F;p&gt;
&lt;p&gt;I chose the name “ferne” because in German “die Ferne” can be used to describe “the distance” or “the faraway places”. It is also a play on the word &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;de.wikipedia.org&#x2F;wiki&#x2F;Fernweh&quot;&gt;Fernweh&lt;&#x2F;a&gt;, which means “a longing for far-off places”.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;goals&quot;&gt;Goals&lt;&#x2F;h3&gt;
&lt;p&gt;Besides the obvious goal of having a platform to mark my travels, I also wanted to: build a product from zero to production with &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;supabase.com&#x2F;&quot;&gt;Supabase&lt;&#x2F;a&gt;.
I think it is an amazing technology that allows you to ship products quickly. I had limit experience with it, but their docs are well-structured. They also have a
generous free tier, which is great for small projects like this one.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;tech-stack&quot;&gt;Tech Stack&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Frontend: Typescript, Nextjs, tailwindcss, shadcn&#x2F;ui, amcharts&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Backend: Supabase (PostgreSQL, Auth, Storage)&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;key-features&quot;&gt;Key Features&lt;&#x2F;h3&gt;
&lt;p&gt;These features were my &lt;strong&gt;must-haves&lt;&#x2F;strong&gt; for the first version of the platform:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot; checked=&quot;&quot;&#x2F;&gt;
An interactive, 3D world map to mark your travels&lt;&#x2F;li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot; checked=&quot;&quot;&#x2F;&gt;
A profile page to share your travels (there for a user system)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot; checked=&quot;&quot;&#x2F;&gt;
An embeddable widget to show your travels on other websites (so that I can use it &lt;a href=&quot;&#x2F;blog&#x2F;visited-countries&quot;&gt;in my own blog&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Some nice-to-have features:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot; checked=&quot;&quot;&#x2F;&gt;
Travel stats (e.g. number of countries visited)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot; checked=&quot;&quot;&#x2F;&gt;
Different types of world maps&lt;&#x2F;li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot; checked=&quot;&quot;&#x2F;&gt;
Some customizations on marking your travels&lt;&#x2F;li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&#x2F;&gt;
Achievements&lt;&#x2F;li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&#x2F;&gt;
Landmarks&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;learnings&quot;&gt;Learnings&lt;&#x2F;h3&gt;
&lt;p&gt;I learned a lot while building this project, especially about Supabase.
This was my first time shipping a product with it, and I was impressed by how easy it was to set up the database, authentication, and storage.
Their local development setup is also pretty good.&lt;&#x2F;p&gt;
&lt;p&gt;Through this project I got to know &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.amcharts.com&#x2F;&quot;&gt;amcharts&lt;&#x2F;a&gt; which is an extremely powerful charts &amp;amp; maps library.
It did take a few trials and errors to get the map working the way I wanted, but in the end it was worth it.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;aftermath&quot;&gt;Aftermath&lt;&#x2F;h3&gt;
&lt;p&gt;I am still using ferne.io to mark my travels.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Visited Countries</title>
          <pubDate>Sun, 30 Jun 2024 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://efe.dev/blog/visited-countries/</link>
          <guid>https://efe.dev/blog/visited-countries/</guid>
          <description xml:base="https://efe.dev/blog/visited-countries/">&lt;p&gt;Traveling is easily one of my top hobbies.
Visiting new countries, experiencing diverse cultures, interacting with locals, and learning about their ways of life broadens my perspective immensely.
There are many countries I hope to visit in the future, and I aim to check off more destinations each year. In the future, I’m also going to share my experiences through blog posts.&lt;&#x2F;p&gt;
&lt;p&gt;Below you can see an iframe from &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;ferne.efe.dev&quot;&gt;ferne.io&lt;&#x2F;a&gt;. I’ve built ferne, so I could keep track of the countries I’ve visited. &lt;a href=&quot;&#x2F;projects&#x2F;ferne-io&quot;&gt;Here’s&lt;&#x2F;a&gt; more about the project.&lt;&#x2F;p&gt;
&lt;div style=&quot;width: 100%; height: 650px;&quot;&gt;
  &lt;iframe
    src=&quot;https:&#x2F;&#x2F;ferne.efe.dev&#x2F;embed?visitedLocations=TR,CY,ES,PT,FR,IT,MT,DE,AT,CZ,SE,DK,HU,SI,HR,ID,VN,MC,QA,JP,VA,KR,TW,HK&amp;mapType=orthographic&quot;
    width=&quot;100%&quot;
    height=&quot;100%&quot;
    style=&quot;border: 0;&quot;
  &gt;&lt;&#x2F;iframe&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
    </channel>
</rss>
