<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Rogier van den Berg</title>
    <description>The latest articles on Forem by Rogier van den Berg (@rogiervandenberg).</description>
    <link>https://forem.com/rogiervandenberg</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F338534%2F52e25cd6-c26c-4537-9d2d-69be713951df.jpg</url>
      <title>Forem: Rogier van den Berg</title>
      <link>https://forem.com/rogiervandenberg</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/rogiervandenberg"/>
    <language>en</language>
    <item>
      <title>Building a Battery-Powered Pomodoro Timer with Deep Sleep</title>
      <dc:creator>Rogier van den Berg</dc:creator>
      <pubDate>Sun, 04 Jan 2026 11:21:41 +0000</pubDate>
      <link>https://forem.com/rogiervandenberg/building-a-battery-powered-pomodoro-timer-with-deep-sleep-1h64</link>
      <guid>https://forem.com/rogiervandenberg/building-a-battery-powered-pomodoro-timer-with-deep-sleep-1h64</guid>
      <description>&lt;p&gt;The Pomodoro Technique is simple but effective: 25 minutes of pure focus, followed by a 5-minute break. Back in 2013, I built my first custom timer to automate this. Now, over a decade later, it's time for a serious upgrade.&lt;/p&gt;

&lt;p&gt;In this post, I'll show you how I converted my old bulky design into a modern, ultra-low-power version using an OLED display and Deep Sleep technology. I'm sharing the code for both the ESP32 and the energy-efficient &lt;a href="https://www.seeedstudio.com/Seeed-XIAO-BLE-nRF52840-p-5201.html" rel="noopener noreferrer"&gt;Seeed XIAO nRF52840&lt;/a&gt;, including a custom 3D-printed case!&lt;/p&gt;

&lt;h2&gt;
  
  
  The History: Version 2013
&lt;/h2&gt;

&lt;p&gt;My original project was built around a classic Arduino Uno and a 7-segment display. All put together in a 3D-printed box, held together with tape as printers then were not that good yet. While it looked cool, the wiring was a nightmare—I had to use shift registers (74HC595) to manage the pins, and power consumption was high, meaning it always had to be plugged in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F877ltoyeo6wg3ek74uv3.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F877ltoyeo6wg3ek74uv3.jpeg" alt=" " width="640" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The code was functional but verbose, handling all the bit-shifting manually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The old way: Managing a 7-segment display through shift registers&lt;/span&gt;
&lt;span class="n"&gt;byte&lt;/span&gt; &lt;span class="n"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mh"&gt;0xc0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0xf9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0xa4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0xb0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x92&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x82&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0xf8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0xff&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;latchPin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;shiftOut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataPin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clockPin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MSBFIRST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;digit&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;latchPin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It worked, but it wasn't elegant—and certainly not portable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Upgrade: Version 2026
&lt;/h2&gt;

&lt;p&gt;The new version does exactly the same thing, but &lt;em&gt;smarter&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvsa288nk1j42htnlidaq.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvsa288nk1j42htnlidaq.jpeg" alt=" " width="640" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What's Changed?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Display&lt;/strong&gt;: A crisp I2C OLED screen (needs only 2 wires for data!).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Power&lt;/strong&gt;: Battery support, and thanks to "Deep Sleep," the microcontroller turns off completely when the timer finishes. One press of the button wakes it up instantly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audio&lt;/strong&gt;: A triumphant melody plays when your session is done.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory&lt;/strong&gt;: The timer remembers your last setting, even after sleeping!&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Power Advantage
&lt;/h3&gt;

&lt;p&gt;Here's the magic of Deep Sleep:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;State&lt;/th&gt;
&lt;th&gt;ESP32 Current&lt;/th&gt;
&lt;th&gt;nRF52840 Current&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Active (counting)&lt;/td&gt;
&lt;td&gt;~40mA&lt;/td&gt;
&lt;td&gt;~5mA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deep Sleep&lt;/td&gt;
&lt;td&gt;~10µA&lt;/td&gt;
&lt;td&gt;~1µA&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;With the nRF52840, a small 500mAh LiPo battery could theoretically last for &lt;strong&gt;months&lt;/strong&gt; in standby, waking up only when you press the button.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Need
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Microcontroller&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://www.amazon.nl/dp/B0DKF9NCN1" rel="noopener noreferrer"&gt;ESP32 (DevKitC)&lt;/a&gt; (accessible, pin headers, ready to prototype), &lt;a href="https://www.seeedstudio.com/Seeed-Studio-XIAO-ESP32C6-p-5884.html" rel="noopener noreferrer"&gt;Seeed Studio XIAO ESP32-C6&lt;/a&gt; (cheap!), or &lt;a href="https://www.seeedstudio.com/Seeed-XIAO-BLE-nRF52840-p-5201.html" rel="noopener noreferrer"&gt;Seeed XIAO nRF52840&lt;/a&gt; (best for battery life)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Display&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://www.amazon.nl/dp/B01L9GC470" rel="noopener noreferrer"&gt;0.96 inch OLED Display&lt;/a&gt; (I2C SSD1306)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Button&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Simple push button&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Audio&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Passive Piezo buzzer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Power&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;USB cable or a 3.7V LiPo battery (ideal for the XIAO)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;3D printed (files in the repository)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F029rwgy8jc1po8evb5b8.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F029rwgy8jc1po8evb5b8.jpeg" alt=" " width="640" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wiring Guide
&lt;/h2&gt;

&lt;p&gt;The wiring is straightforward. Here is the pinout for both board options:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;ESP32 Pin&lt;/th&gt;
&lt;th&gt;XIAO nRF52840 Pin&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;OLED SDA&lt;/td&gt;
&lt;td&gt;GPIO 33&lt;/td&gt;
&lt;td&gt;D4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OLED SCL&lt;/td&gt;
&lt;td&gt;GPIO 32&lt;/td&gt;
&lt;td&gt;D5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Button&lt;/td&gt;
&lt;td&gt;GPIO 25 (to GND)&lt;/td&gt;
&lt;td&gt;D1 (to GND)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Buzzer (+)&lt;/td&gt;
&lt;td&gt;GPIO 27&lt;/td&gt;
&lt;td&gt;D2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Buzzer (-)&lt;/td&gt;
&lt;td&gt;GND&lt;/td&gt;
&lt;td&gt;GND&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Both the button and buzzer connect to ground on the other side. The button uses the internal pull-up resistor, so no external resistor is needed. I soldered the battery directly to the board in the final product, as the XIAO supports battery charging.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Case (3D Print)
&lt;/h2&gt;

&lt;p&gt;I designed a compact housing to fit everything neatly. The design includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Slot for Seeed Studio XIAO nRF52840, with recessed USB port&lt;/li&gt;
&lt;li&gt;Cutout for the OLED display&lt;/li&gt;
&lt;li&gt;Button hole&lt;/li&gt;
&lt;li&gt;Battery compartment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The board is clamped in place by a bracket and a pin from the top of the enclosure, the battery rests inside a compartment and the display hangs in a small gutter, with lips behind the screen coming from the top of the enclosure. Together with a snap fit this keeps everything neatly in place.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjsakmamateq2op6ubw8r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjsakmamateq2op6ubw8r.png" alt=" " width="800" height="765"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Download the STL files&lt;/strong&gt;: Available in the &lt;code&gt;enclosure/&lt;/code&gt; folder of the &lt;a href="https://github.com/rogiervandenberg/pomodorotimer" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Code
&lt;/h2&gt;

&lt;p&gt;I made two versions, first based on ESP32 for prototyping, later with the nRF52840 in the final product, to have a more power efficient solution.&lt;/p&gt;

&lt;p&gt;Both versions share the same logic:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Wake up&lt;/strong&gt; → Show last selected duration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Button press&lt;/strong&gt; → Cycle through 5, 15, 25, 45 minutes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wait 2 seconds&lt;/strong&gt; → Auto-start countdown&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Count down&lt;/strong&gt; → Display remaining minutes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Finish&lt;/strong&gt; → Play melody, show "KLAAR!" (Dutch for "Ready!" 🇳🇱), go to sleep&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The key difference is &lt;em&gt;how&lt;/em&gt; they sleep and remember settings.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option A: ESP32 Deep Sleep
&lt;/h3&gt;

&lt;p&gt;The ESP32 uses &lt;code&gt;RTC_DATA_ATTR&lt;/code&gt; to store variables in RTC memory (survives deep sleep) and &lt;code&gt;esp_deep_sleep_start()&lt;/code&gt; to enter low-power mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Store setting in RTC memory - survives deep sleep!&lt;/span&gt;
&lt;span class="n"&gt;RTC_DATA_ATTR&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;countdownSetting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;goToSleep&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ssd1306_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SSD1306_DISPLAYOFF&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;esp_sleep_enable_ext0_wakeup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buttonPin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// Wake on button press&lt;/span&gt;
  &lt;span class="n"&gt;esp_deep_sleep_start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Option B: Seeed XIAO nRF52840
&lt;/h3&gt;

&lt;p&gt;The nRF52840 uses System OFF mode for even lower power consumption. It stores settings in the GPREGRET register:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Store setting in GPREGRET - survives System OFF!&lt;/span&gt;
&lt;span class="n"&gt;NRF_POWER&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;GPREGRET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;countdownSetting&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;goToSleep&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ssd1306_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SSD1306_DISPLAYOFF&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Configure button to wake from System OFF&lt;/span&gt;
  &lt;span class="n"&gt;nrf_gpio_cfg_sense_input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;digitalPinToPinName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buttonPin&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;NRF_GPIO_PIN_PULLUP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;NRF_GPIO_PIN_SENSE_LOW&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;NRF_POWER&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;SYSTEMOFF&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// Enter System OFF mode&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the nRF52840 wakes up, it's essentially a reset—the code starts fresh from &lt;code&gt;setup()&lt;/code&gt;. We check GPREGRET to restore the previous setting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Clone the repository&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   git clone https://github.com/rogiervandenberg/pomodorotimer.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Install the Arduino libraries&lt;/strong&gt; (via Library Manager):&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Adafruit GFX Library&lt;/li&gt;
&lt;li&gt;Adafruit SSD1306&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Install the board package&lt;/strong&gt;:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;ESP32: Add the ESP32 boards URL in Arduino IDE preferences&lt;/li&gt;
&lt;li&gt;nRF52840: Follow the &lt;a href="https://wiki.seeedstudio.com/XIAO_BLE/" rel="noopener noreferrer"&gt;Seeed XIAO BLE guide&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Upload the appropriate sketch&lt;/strong&gt; to your board&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Print the case&lt;/strong&gt; and assemble everything&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;After 13 years, some things have changed dramatically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;I2C is supercool&lt;/strong&gt;: Managing shift registers was fun in 2013, but modern I2C displays with clean libraries make life so much easier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deep Sleep changes everything&lt;/strong&gt;: The ability to run on battery for months opens up entirely new use cases. My little clock is now fully portable!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One button is enough&lt;/strong&gt;: Constraint breeds creativity. The single-button interface forces simplicity.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This project proves that sometimes the best gadget is the one that does exactly one thing well. No notifications, no apps, no distractions—just a timer and a button.&lt;/p&gt;

&lt;p&gt;The full code is available on GitHub: &lt;a href="https://github.com/rogiervandenberg/pomodorotimer" rel="noopener noreferrer"&gt;rogiervandenberg/pomodorotimer&lt;/a&gt;&lt;br&gt;
The 3D enclosure is available on &lt;a href="https://makerworld.com/en/models/2198138-pomodoro-timer-seeed-studio-xiao-enclosure#profileId-2387709" rel="noopener noreferrer"&gt;Makerworld&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Happy building and stay focused!&lt;/strong&gt; 🍅&lt;/p&gt;

</description>
      <category>programming</category>
      <category>esp32</category>
      <category>3dprinting</category>
      <category>diy</category>
    </item>
    <item>
      <title>Revolutionize Your Unit Testing with Testcontainers and Docker</title>
      <dc:creator>Rogier van den Berg</dc:creator>
      <pubDate>Fri, 31 May 2024 09:07:17 +0000</pubDate>
      <link>https://forem.com/rogiervandenberg/revolutionize-your-unit-testing-with-testcontainers-and-docker-4h73</link>
      <guid>https://forem.com/rogiervandenberg/revolutionize-your-unit-testing-with-testcontainers-and-docker-4h73</guid>
      <description>&lt;p&gt;Recently, when working on a project I ran into a situation in which I needed to handle a complex object/data structure. This object had to be able to be saved into a database (dispersed over multiple different tables), retrieved, and reconstructed exactly as intended.&lt;/p&gt;

&lt;p&gt;In order to verify things were built as intended, I created Unit Tests that would write and read this object into my database. But I did not want to use my development database for this.&lt;/p&gt;

&lt;p&gt;Why? The traditional approach of using a development database for testing often leads to several issues: interference with development work, data corruption, inconsistent results, and overlapping tests affecting each other.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;But what if there was a way to conduct these tests in a controlled, isolated, and repeatable environment, using a real database? 🤔&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Enter Testcontainers!
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why Testcontainers?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://testcontainers.com/"&gt;Testcontainers&lt;/a&gt; is a very neat open source framework/project I just discovered. It enables developers to create unit tests using throwaway, lightweight instances of e.g. a database running in Docker containers.&lt;/p&gt;

&lt;p&gt;This approach ensures that every test runs in a fresh, isolated environment, eliminating the problems associated with using a shared development database. No more conflicting tests, no more corrupt data, and consistent, reliable results every time you run your tests.&lt;/p&gt;

&lt;p&gt;And it works for multiple languages, like Go, Python and Javascript and it supports many external dependencies, like Postgres (as I'm using in my case), MySQL, Git, several Cloud providers, Redis, Elastic and many more 🤯&lt;/p&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;

&lt;p&gt;Look at the following code that I copied from the &lt;a href="https://testcontainers.com/guides/getting-started-with-testcontainers-for-nodejs/#_write_the_tests_and_solution"&gt;documentation&lt;/a&gt; of Testcontainers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Client&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PostgreSqlContainer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@testcontainers/postgresql&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createCustomerTable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createCustomer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getCustomers&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./customer-repository&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Customer Repository&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;postgresContainer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;postgresClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;beforeAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;postgresContainer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PostgreSqlContainer&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;postgresClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;postgresContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getConnectionUri&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;postgresClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createCustomerTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;postgresClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;afterAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;postgresClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;postgresContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should create and return multiple customers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customer1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;John Doe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customer2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Jane Doe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;postgresClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;customer1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;postgresClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;customer2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getCustomers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;postgresClient&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;customer1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;customer2&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code is a test that tests a customer repository and does a couple of actions against a real database. The amazing thing is that this test is not mocking anything, but &lt;strong&gt;always runs with fresh and new real database&lt;/strong&gt; in a Docker container! So no more mocks or conflicting tests and a clean database every time you run your tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it out yourself
&lt;/h2&gt;

&lt;p&gt;I think Testcontainers make running specific test cases easier, while having optimal isolation, consistency (and thus reliability) and simplicity (and thus better to maintain and manage).&lt;/p&gt;

&lt;p&gt;So, check out → &lt;a href="https://testcontainers.com/"&gt;https://testcontainers.com/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>testing</category>
      <category>database</category>
      <category>javascript</category>
    </item>
    <item>
      <title>The location of iOS simulator builds has moved</title>
      <dc:creator>Rogier van den Berg</dc:creator>
      <pubDate>Mon, 26 Sep 2022 18:29:00 +0000</pubDate>
      <link>https://forem.com/rogiervandenberg/the-ios-simulator-build-has-moved-390n</link>
      <guid>https://forem.com/rogiervandenberg/the-ios-simulator-build-has-moved-390n</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;iOS Simulator builds are not somewhere underneath &lt;code&gt;~/Library/Developer/Xcode/DerivedData&lt;/code&gt; anymore, but in &lt;code&gt;~/Library/Developer/CoreSimulator/Devices&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simulator build for e.g. Meta/Facebook App review
&lt;/h2&gt;

&lt;p&gt;If you build an iOS or Android app that integrates with the Facebook SDK, you need to have your app reviewed before being able to go live.&lt;/p&gt;

&lt;p&gt;As part of this App Review process voor iOS, you have to submit a &lt;em&gt;simulator binary package&lt;/em&gt; of your app to Facebook.&lt;/p&gt;

&lt;p&gt;Part of &lt;a href="https://developers.facebook.com/docs/ios/create-a-simulator-build/"&gt;the instruction&lt;/a&gt; is to run the app in your simulator, and then look into &lt;code&gt;~/Library/Developer/Xcode/DerivedData folder&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But, since XCode 14 this does not work anymore, and up until now the Facebook documentation is not up to date yet. 😱 So the whole day I've been looking for "Where does XCode (or Flutter command line in my case) drop my compiled app, that is run on my Simulator?".&lt;/p&gt;

&lt;p&gt;As it occurs, that is &lt;code&gt;~/Library/Developer/CoreSimulator/Devices/&amp;lt;&amp;lt;hash of your device&amp;gt;&amp;gt;/data/Containers/Bundle/Application/&amp;lt;&amp;lt;hash of your app&amp;gt;&amp;gt;/&amp;lt;&amp;lt;name of target&amp;gt;&amp;gt;.app&lt;/code&gt; 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus; one-liner to get a Zipfile of the simulator build
&lt;/h2&gt;

&lt;p&gt;How to get a zipfile of your simulator build, that you can send to Facebook right away?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run your app in the emulator&lt;/li&gt;
&lt;li&gt;Execute the following line in your shell:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ditto &lt;span class="nt"&gt;-ck&lt;/span&gt; &lt;span class="nt"&gt;--sequesterRsrc&lt;/span&gt; &lt;span class="nt"&gt;--keepParent&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; ~/Library/Developer/CoreSimulator/Devices/&lt;span class="k"&gt;*&lt;/span&gt;/data/Containers/Bundle/Application/&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.app | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 1&lt;span class="sb"&gt;`&lt;/span&gt; ~/Desktop/your_app_&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +&lt;span class="s2"&gt;"%Y%m%d_%H%M"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;.zip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will take the most recent app from underneath your &lt;code&gt;CoreSimulator&lt;/code&gt; folder, puts it in the most small and optimized archive for Facebook and drops it at your desktop, while having a timestamp. Feel free to change this one-liner to your liking 😉&lt;/p&gt;

&lt;p&gt;When having the zipfile on your desktop, it's smart to check if it's working before submitting to Facebook. &lt;/p&gt;

&lt;p&gt;To test the Zipfile:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extract the zip&lt;/li&gt;
&lt;li&gt;Install &lt;code&gt;ios-sim&lt;/code&gt; if not done yet: &lt;code&gt;npm install -g ios-sim&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Test your file with: &lt;code&gt;ios-sim launch --devicetypeid com.apple.CoreSimulator.SimDeviceType.iPhone-14 ~/Desktop/Runner.app&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Good luck! 👋&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to explain the delight of Open Source software contributions to a non-developer?</title>
      <dc:creator>Rogier van den Berg</dc:creator>
      <pubDate>Thu, 17 Mar 2022 13:45:59 +0000</pubDate>
      <link>https://forem.com/rogiervandenberg/how-to-explain-the-delight-of-open-source-software-contributions-to-a-non-developer-2b0b</link>
      <guid>https://forem.com/rogiervandenberg/how-to-explain-the-delight-of-open-source-software-contributions-to-a-non-developer-2b0b</guid>
      <description>&lt;p&gt;Being happy that my contribution to the Request Logger middleware of &lt;a href="https://go-chi.io/#/"&gt;Go’s chi router&lt;/a&gt; got accepted and merged 🎉 I found out that it was difficult to explain the 'coolness' of this to my non-dev colleagues.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;So I explained it as follows:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;At &lt;a href="https://www.embrosa.com"&gt;our company&lt;/a&gt; we use open source software components to build our own products (think of them as pre made ingredients when cooking). But when doing so sometimes you run into something that is not yet possible/existing in one of these ingredients. 🤷‍♂️&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;How cool is it that you then can (instead of waiting or searching for an alternative) fix it yourself (make your spice ingredient a bit spicier and with multiple colors for instance 🌶 🌈) ánd give your additions back to the community to be available to the rest of the world?&lt;/em&gt; 🌍&lt;/p&gt;

&lt;p&gt;&lt;em&gt;That just happened, I change a piece of logging software to fix something I ran into myself, and gave it back!&lt;/em&gt; 🎉 #humblebrag &lt;a href="https://github.com/go-chi/httplog/pull/8"&gt;https://github.com/go-chi/httplog/pull/8&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;It’s always a great feeling improving some software you use yourself and giving it back for others to use too!&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>joy</category>
      <category>humblebrag</category>
    </item>
    <item>
      <title>Easy Google Cloud Logging from your Golang project in Google Cloud Run</title>
      <dc:creator>Rogier van den Berg</dc:creator>
      <pubDate>Wed, 16 Mar 2022 09:37:02 +0000</pubDate>
      <link>https://forem.com/rogiervandenberg/easy-google-cloud-logging-from-your-golang-project-in-google-cloud-run-1pap</link>
      <guid>https://forem.com/rogiervandenberg/easy-google-cloud-logging-from-your-golang-project-in-google-cloud-run-1pap</guid>
      <description>&lt;p&gt;&lt;em&gt;In my journey of learning Go, for almost a day I have been searching and thinking in the wrong direction, and I want to prevent you from doing the same&lt;/em&gt; 😅&lt;/p&gt;

&lt;p&gt;I am building a HTTP Service running in &lt;a href="https://cloud.run/"&gt;Google Cloud Run&lt;/a&gt; in  &lt;a href="https://golang.org/"&gt;Go&lt;/a&gt;  and wanted an easy way to log stuff to  &lt;a href="https://cloud.google.com/logging"&gt;Google Cloud Logging&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cloud Run Logs
&lt;/h2&gt;

&lt;p&gt;Cloud Run has two types of logs, and these are automatically sent to Cloud Logging:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Request logs: logs of requests sent to Cloud Run services. These logs are created automatically.&lt;/li&gt;
&lt;li&gt;Container logs: logs emitted from the container instances, typically from your own code, when writing to &lt;code&gt;stdout&lt;/code&gt; or &lt;code&gt;stderr&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Loggers
&lt;/h2&gt;

&lt;p&gt;If you start searching on how to do logging, &lt;a href="https://github.com/avelino/awesome-go#logging"&gt;you will find many possibilities&lt;/a&gt;, this makes it very difficult to decide and/or even to know which one to use.&lt;/p&gt;

&lt;p&gt;Even worse, because I'm making a HTTP service, I was searching in the direction of &lt;a href="https://github.com/go-chi/chi#middleware-handlers"&gt;creating middleware&lt;/a&gt; for my &lt;a href="https://go-chi.io/"&gt;chi router&lt;/a&gt;. 🤦‍♂️&lt;/p&gt;

&lt;h2&gt;
  
  
  KISS
&lt;/h2&gt;

&lt;p&gt;But then it hit me: As Cloud Run already automatically takes care of the HTTP request logging, I only needed to log certain important 'events'. Thus, I could just write to &lt;code&gt;os.stdout&lt;/code&gt; in the correct format. &lt;/p&gt;

&lt;p&gt;And even better, there is a very easy to use  &lt;a href="https://github.com/apsystole/log"&gt;log package&lt;/a&gt;  for that:, having great benefits like no dependencies, uses ' &lt;a href="https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#LogSeverity"&gt;severity&lt;/a&gt; ' to support the Cloud Logging filters.&lt;/p&gt;

&lt;p&gt;The only implementation is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"github.com/apsystole/log"&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"my message"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;// {"message":"my message","severity":"INFO"}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"my message"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="c"&gt;// {"message":"my message","severity":"ERROR"}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bottom line: keep it simple, stupid ;)&lt;/p&gt;

</description>
      <category>learning</category>
      <category>go</category>
      <category>gcp</category>
      <category>googlecloud</category>
    </item>
    <item>
      <title>Why I'm writing on dev.to from now on</title>
      <dc:creator>Rogier van den Berg</dc:creator>
      <pubDate>Tue, 15 Mar 2022 09:39:37 +0000</pubDate>
      <link>https://forem.com/rogiervandenberg/why-im-writing-on-devto-from-now-on-o7p</link>
      <guid>https://forem.com/rogiervandenberg/why-im-writing-on-devto-from-now-on-o7p</guid>
      <description>&lt;p&gt;As most developers having an own self made website, every time you think of blogging or technical writing, you end up creating a new website or optimization for yourself first.  😅 So did I originally; I was tweaking my website more than doing actual writing 🙄&lt;/p&gt;

&lt;p&gt;Until at some point I decided to take a third party blogging tool, that focuses on writing and takes care of "the rest", preferably having the least amount of friction between an idea and a published post.&lt;/p&gt;

&lt;p&gt;So I looked for a hosted solution...&lt;/p&gt;

&lt;h2&gt;
  
  
  dev.to / DEV
&lt;/h2&gt;

&lt;p&gt;Enter &lt;a href="https://dev.to/"&gt;DEV&lt;/a&gt;. It is an amazing platform to just start writing. To see if it would fit me, I wrote a couple of things here. The platform has a very simple layout, writing is quick and effortless and it takes no time to get your idea out there.&lt;/p&gt;

&lt;p&gt;Furthermore it's free, it gives good SEO ranking and allows you to connect your privately owned site too with canonical URLs.&lt;/p&gt;

&lt;p&gt;Finally, the organization behind DEV are sympathetic. They promote and facilitate many initiatives (e.g. International Women's Day, minorities in tech), seem very inclusive, open sourced their tech and have a merchandise shop!&lt;/p&gt;

&lt;h2&gt;
  
  
  Hashnode
&lt;/h2&gt;

&lt;p&gt;But then, there is also &lt;a href="https://hashnode.com/"&gt;Hashnode&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Some people on Twitter nudged me over to look at that. It has cool things like your own website (with custom pages!) on a custom domain. All for free too! So I created an account, created my site and started cross posting my blogs. They even have import tools to get started quickly with already written content.&lt;/p&gt;

&lt;p&gt;But some things are not so good. For instance the overall navigation and "getting around": The difference between your profile (with a feed, about section, etc) and your website/blog for example. What to put where? It seems you have your "profile" and settings on the Hashnode platform, besides your website/blog that also has a profile with settings. &lt;strong&gt;Things are clunky and confusing&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Another thing I did not like was the speed. Every click takes about 3 seconds before the page is ready loading. It just is not snappy. &lt;/p&gt;

&lt;p&gt;But, in the end you get your free custom domain, free website with custom pages, etc. etc. so I did not complain. Even better, if I want to in the future, I can easily use their Newsletter feature to add that to my Hashnode site too!&lt;/p&gt;

&lt;p&gt;So I even almost decided to go for Hashnode as my default platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who's reading?
&lt;/h2&gt;

&lt;p&gt;But then, today, I saw some Analytics of exact same posts on both dev.to and Hashnode. dev.to is found and read way more than Hashnode. Sometimes almost 100 times better!&lt;/p&gt;

&lt;p&gt;For instance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://dev.to/rogiervandenberg/deno-in-docker-on-google-cloud-run-2f26"&gt;small piece&lt;/a&gt; about how to deploy &lt;a href="https://deno.com/blog/v1"&gt;Deno&lt;/a&gt; on Cloud run: 484 views on Dev.to, 1 on Hashnode.&lt;/li&gt;
&lt;li&gt;When Rails 6 was new, &lt;a href="https://dev.to/rogiervandenberg/create-new-rails-6-project-with-docker-56io"&gt;something&lt;/a&gt; about creating a new project with Docker: 2886 views, 12 hearts and 3 comments on Dev.to. 52 views and no responses on Hashnode 🤷‍♂️&lt;/li&gt;
&lt;li&gt;I made &lt;a href="https://github.com/rogiervandenberg/nextjs-typescript-bulma-boilerplate"&gt;this boilerplate&lt;/a&gt; for a new Next.js project with Bulma and Typescript and wrote about it. Dev.to got 2031 views and 4 reactions. Hashnode just 21 views.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;So, if I want to write something, not only for myself, but also for the world to be able to take an advantage of it, I'd better use dev.to.&lt;/p&gt;

&lt;p&gt;And it gives me added benefits as well: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Loading of pages is nice and quick (as it should be)&lt;/li&gt;
&lt;li&gt;There is one place to manage and discover content (not a separate own site, etc.)&lt;/li&gt;
&lt;li&gt;No more brain clutter / decision fatigue with thinking about what customizations to make on my website (because that does not matter anymore)&lt;/li&gt;
&lt;li&gt;Just posting on ONE spot → &lt;a href="https://dev.to/rogiervandenberg"&gt;https://dev.to/rogiervandenberg&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Am I missing things on DEV?
&lt;/h3&gt;

&lt;p&gt;Well yes, it would still be nice to point my custom domain to my dev.to profile. Furthermore, an automated backup of my content to e.g. a github repo (as Hashnode has) would be great.&lt;/p&gt;

&lt;p&gt;Happy writing! 👋&lt;/p&gt;

</description>
      <category>writing</category>
      <category>blog</category>
      <category>platform</category>
    </item>
    <item>
      <title>Run Postgres from Docker for easy development</title>
      <dc:creator>Rogier van den Berg</dc:creator>
      <pubDate>Wed, 16 Feb 2022 08:18:10 +0000</pubDate>
      <link>https://forem.com/rogiervandenberg/run-postgres-from-docker-for-easy-development-4ahp</link>
      <guid>https://forem.com/rogiervandenberg/run-postgres-from-docker-for-easy-development-4ahp</guid>
      <description>&lt;p&gt;&lt;em&gt;Postgres is a great database server that you can install locally on your computer for development. However, there are various ways in which you can install PostgreSQL on your machine, e.g. by using the the &lt;a href="https://www.postgresql.org/download/macosx/"&gt;installer&lt;/a&gt;, &lt;a href="https://formulae.brew.sh/formula/postgresql"&gt;homebrew&lt;/a&gt; or &lt;a href="https://postgresapp.com/"&gt;postgres.app&lt;/a&gt;. But managing your local Postgres is a real pain. "What was the command again to stop/start the service?", "Do you need to have it always be running in the background (even when you are not working)?", "How to handle multiple versions next to each other?", "How to handle updates?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;→ What if I tell you, &lt;strong&gt;you can run Postgres in one command, in any version WITHOUT having to install it in your machine? WITH the benefit of persisting data?&lt;/strong&gt; That would be a total game changer! 😃&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker
&lt;/h2&gt;

&lt;p&gt;Docker allows you to put software in a container and run it with a single command. You could make your own image that packs an operating system, your settings/preferences and your application into a container for easy deployment, but, you can also use a pre-build image to run things like a database engine!&lt;/p&gt;

&lt;h2&gt;
  
  
  Run a local Postgres with Docker
&lt;/h2&gt;

&lt;p&gt;Let's say I need to have Postgres in the specific (old) version 9.6 and the databases should be persisted outside the container on my local computer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install Docker
&lt;/h3&gt;

&lt;p&gt;First, make sure you have Docker installed on your computer: &lt;a href="https://docs.docker.com/get-docker/"&gt;https://docs.docker.com/get-docker/&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a local directory to persist the data in
&lt;/h3&gt;

&lt;p&gt;In my example, I'm going to write the database to my &lt;code&gt;/tmp&lt;/code&gt; folder, but this can be anywhere.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mkdir /tmp/database&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A better place would be a subfolder in your project, or in your home-dir.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Start Postgres with Docker
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-p&lt;/span&gt; 5432:5432 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;password &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-v&lt;/span&gt; /tmp/database:/var/lib/postgresql/data:delegated  &lt;span class="se"&gt;\&lt;/span&gt;
postgres:9.6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let me break down what this does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run a container and clean up afterwards and allow the process to be interactive (so you can cancel it when you're done)&lt;/li&gt;
&lt;li&gt;Allow connections to the Postgres inside the container on port 5432 from the outside of the container on port 5432&lt;/li&gt;
&lt;li&gt;Set the default password for the postgres user to &lt;code&gt;password&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Sync all stored data of Postgres inside the container into our local folder &lt;code&gt;/tmp/database&lt;/code&gt; (make sure this folder exists!). "Delegated" means that the container’s view of the file system will be authoritative, as we won't be changing the data from outside the container, so the database will be quick.&lt;/li&gt;
&lt;li&gt;Use the Postgres 9.6 image&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Connect!
&lt;/h3&gt;

&lt;p&gt;Now, you can connect to &lt;code&gt;localhost&lt;/code&gt; on port &lt;code&gt;5432&lt;/code&gt;, with default username &lt;code&gt;postgres&lt;/code&gt;, password &lt;code&gt;password&lt;/code&gt; and default database &lt;code&gt;postgres&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A great and easy way to connect to your database is with the &lt;a href="https://tableplus.com/"&gt;TablePlus app&lt;/a&gt; (for Mac, Windows, Linux and more!):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NBSI9c_x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1644938235070/_L_MoPGAQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NBSI9c_x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1644938235070/_L_MoPGAQ.png" alt="image.png" width="500" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Stopping
&lt;/h3&gt;

&lt;p&gt;If you're done working, just &lt;code&gt;CTRL-C&lt;/code&gt; out of your database. The container will be cleaned up and Postgres is gone again. But, your data is still save on your own computer.&lt;/p&gt;

&lt;p&gt;Next time you execute the above &lt;code&gt;docker run&lt;/code&gt; command, Postgres will be fired up again; Really fast if it is still cached on your computer, otherwise quite fast by downloading the image again:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hC3mHBlv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1644938844472/3KwA9BZm7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hC3mHBlv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1644938844472/3KwA9BZm7.png" alt="image.png" width="800" height="226"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>docker</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Next.js with Bulma in Typescript boilerplate</title>
      <dc:creator>Rogier van den Berg</dc:creator>
      <pubDate>Tue, 03 Nov 2020 13:18:10 +0000</pubDate>
      <link>https://forem.com/rogiervandenberg/next-js-with-bulma-in-typescript-boilerplate-107p</link>
      <guid>https://forem.com/rogiervandenberg/next-js-with-bulma-in-typescript-boilerplate-107p</guid>
      <description>&lt;p&gt;&lt;em&gt;Recently I (re-)discovered Next.js, and I love it. And for making styles, I really like Bulma. And I also prefer to write my Javascript code in Typescript.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So, instead of needing to reinvent the wheel for every (toy) project, I decided to make myself Typescript boiler plate for Next.js with Bulma using Typescript to quickly kick off a new project.&lt;/p&gt;

&lt;p&gt;You might like it too!😊&lt;/p&gt;

&lt;p&gt;Have fun with it: &lt;a href="https://github.com/rogiervandenberg/nextjs-typescript-bulma-boilerplate"&gt;https://github.com/rogiervandenberg/nextjs-typescript-bulma-boilerplate&lt;/a&gt;&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>bulma</category>
      <category>typescript</category>
      <category>boilerplate</category>
    </item>
    <item>
      <title>Add syntax highlighting to 'cat' in your terminal</title>
      <dc:creator>Rogier van den Berg</dc:creator>
      <pubDate>Tue, 30 Jun 2020 09:44:41 +0000</pubDate>
      <link>https://forem.com/rogiervandenberg/add-syntax-highlighting-to-cat-in-your-terminal-1kcp</link>
      <guid>https://forem.com/rogiervandenberg/add-syntax-highlighting-to-cat-in-your-terminal-1kcp</guid>
      <description>&lt;p&gt;I use my terminal everyday, and frequently I am way faster with it then navigating around with my mouse in Finder.&lt;/p&gt;

&lt;p&gt;Within your terminal the &lt;code&gt;cat&lt;/code&gt; command is very useful to quickly peek inside a file, but when you're looking at source code, this is a bit difficult to read. I just thought "&lt;em&gt;Wouldn't there be syntax highlighting for my terminal?&lt;/em&gt;". As it turns out, there is. 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  How to set it up
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;With &lt;a href="https://brew.sh/"&gt;Brew&lt;/a&gt;, install Pygments: &lt;code&gt;brew install pygments&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Next, add the following to your .zshrc/.bashrc file: &lt;code&gt;alias cat="pygmentize -g"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Open a new terminal window, or source your .zshrc file: &lt;code&gt;source ~/.zshrc&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, when you e.g. run &lt;code&gt;cat Dockerfile&lt;/code&gt; on a Dockerfile, things look very neat!&lt;/p&gt;

</description>
      <category>syntax</category>
      <category>highlight</category>
      <category>cat</category>
      <category>terminal</category>
    </item>
    <item>
      <title>Create new Rails 6 project with Docker</title>
      <dc:creator>Rogier van den Berg</dc:creator>
      <pubDate>Thu, 11 Jun 2020 15:59:13 +0000</pubDate>
      <link>https://forem.com/rogiervandenberg/create-new-rails-6-project-with-docker-56io</link>
      <guid>https://forem.com/rogiervandenberg/create-new-rails-6-project-with-docker-56io</guid>
      <description>&lt;p&gt;If you want to set-up a new Rails project, but you don't want to go through the hassle of setting op Ruby, node, Yarn, Rails itself, etc. you could do it with Docker. That way you can really fast start a new project, without possible version conflicts, installs, etc. on your computer...&lt;/p&gt;

&lt;p&gt;Let's go! 🚀&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisite: Have Docker installed
&lt;/h2&gt;

&lt;p&gt;If not, &lt;a href="https://hub.docker.com/search/?type=edition&amp;amp;offering=community"&gt;install Docker&lt;/a&gt; first&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the directory for your project
&lt;/h2&gt;

&lt;p&gt;Open a terminal, create a project folder somewhere and go into that directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;my_rails_project &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;my_rails_project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setup your project
&lt;/h2&gt;

&lt;p&gt;First, you need to start a container to open your folder with, to install Rails.&lt;br&gt;
Run your container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PWD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;:/usr/src &lt;span class="nt"&gt;-w&lt;/span&gt; /usr/src &lt;span class="nt"&gt;-ti&lt;/span&gt; starefossen/ruby-node /bin/bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Within the container, install Rails first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gem &lt;span class="nb"&gt;install &lt;/span&gt;rails
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After installing Rails, create you project. Because your container working directory is bound to you project folder, the Rails project will be created there:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rails new &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(mind the . at the end! This will install the project in the current directory)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;After creating the project, close your container with &lt;code&gt;exit&lt;/code&gt;.  Your container will be closed and removed. And you'll end up with a shiny new Rails install, without having Rails, Ruby, etc. on your computer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Dockerfile
&lt;/h2&gt;

&lt;p&gt;Next, to Run your project, create a &lt;code&gt;Dockerfile&lt;/code&gt; file in the root of your project, and paste the following (or whatever setup you'd prefer) in that file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; starefossen/ruby-node&lt;/span&gt;

&lt;span class="c"&gt;# Set the workdir inside the container&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /usr/src/app&lt;/span&gt;

&lt;span class="c"&gt;# Set the gemfile and install&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; Gemfile* ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;bundle &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# Copy the main application.&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . ./&lt;/span&gt;

&lt;span class="c"&gt;# Expose port 3000 to the Docker host, so we can access it&lt;/span&gt;
&lt;span class="c"&gt;# from the outside.&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;

&lt;span class="c"&gt;# The main command to run when the container starts. Also&lt;/span&gt;
&lt;span class="c"&gt;# tell the Rails dev server to bind to all interfaces by&lt;/span&gt;
&lt;span class="c"&gt;# default.&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Build you container
&lt;/h2&gt;

&lt;p&gt;Build a shiny new Docker container, based on a &lt;a href="https://hub.docker.com/r/starefossen/ruby-node"&gt;Ruby + Node Docker Image&lt;/a&gt;, having your project in there, executed on launch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; my_project &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Run your container
&lt;/h2&gt;

&lt;p&gt;Execute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-p&lt;/span&gt; 3000:3000 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;:/usr/src/app/ &lt;span class="se"&gt;\&lt;/span&gt;
my_project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This runs your built container, forwarding port 3000 and connects the current folder (your project folder) to the one inside the container (so you can edit files while it is running).&lt;/p&gt;

&lt;h2&gt;
  
  
  Check it out
&lt;/h2&gt;

&lt;p&gt;Open your browser en go to &lt;a href="http://localhost:3000"&gt;http://localhost:3000&lt;/a&gt; Yay! You’re on Rails! 🚂&lt;/p&gt;

&lt;h3&gt;
  
  
  Log in to your container
&lt;/h3&gt;

&lt;p&gt;If you want to go inside your container (to create controllers, views, models, etc.) run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-q&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; /bin/bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will execute an interactive (so you can actually use it) /bin/bash inside the running container. &lt;/p&gt;

&lt;p&gt;That's all for now! 👋&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Image credit: &lt;a href="https://www.flickr.com/photos/jasonwhite/20560352082/"&gt;Jason White&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>docker</category>
      <category>container</category>
    </item>
    <item>
      <title>Deno in Docker on Google Cloud Run</title>
      <dc:creator>Rogier van den Berg</dc:creator>
      <pubDate>Mon, 18 May 2020 18:19:50 +0000</pubDate>
      <link>https://forem.com/rogiervandenberg/deno-in-docker-on-google-cloud-run-2f26</link>
      <guid>https://forem.com/rogiervandenberg/deno-in-docker-on-google-cloud-run-2f26</guid>
      <description>&lt;p&gt;This weekend I read a lot about &lt;a href="https://deno.land"&gt;Deno&lt;/a&gt;, "&lt;em&gt;A secure runtime for JavaScript and TypeScript&lt;/em&gt;." It's built on V8 and Rust, by Ryan Dahl, the creator of Node.js and it would basically &lt;a href="https://deno.land/v1"&gt;'fix' many things that are 'wrong' about Node.js and would be a "fun and productive scripting environment"&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It intrigued me, so I took a first look 😊&lt;/p&gt;

&lt;h2&gt;
  
  
  Playing around
&lt;/h2&gt;

&lt;p&gt;After some &lt;a href="https://deno.land/manual"&gt;playing&lt;/a&gt; and reading (&lt;a href="https://www.freecodecamp.org/news/the-deno-handbook/"&gt;this is an amazing source!&lt;/a&gt;) I figured it would be nice to have an easy starting point for a Deno project running inside Docker and also on Google Cloud Run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deno in Docker (on Cloud Run)
&lt;/h2&gt;

&lt;p&gt;I wanted to make a concrete example of Deno, running on Cloud Run. So I build just that. A "Hello world" of Deno, with a Dockerfile (that pre fetches all dependencies) to easily run it anywhere + deployment to Cloud Run.&lt;/p&gt;

&lt;p&gt;You can see the result here: &lt;a href="https://github.com/rogiervandenberg/deno-docker-cloudrun"&gt;https://github.com/rogiervandenberg/deno-docker-cloudrun&lt;/a&gt; Of course with an easy single click deployment button 🎉&lt;/p&gt;

</description>
      <category>deno</category>
      <category>docker</category>
      <category>gcp</category>
      <category>cloudrun</category>
    </item>
    <item>
      <title>Google Task queues on GCP with Google Cloud Functions</title>
      <dc:creator>Rogier van den Berg</dc:creator>
      <pubDate>Fri, 21 Feb 2020 15:53:05 +0000</pubDate>
      <link>https://forem.com/rogiervandenberg/google-task-queues-on-gcp-with-google-cloud-functions-3a2l</link>
      <guid>https://forem.com/rogiervandenberg/google-task-queues-on-gcp-with-google-cloud-functions-3a2l</guid>
      <description>&lt;p&gt;At our company &lt;a href="https://www.embrosa.com"&gt;Embrosa&lt;/a&gt; we have many microservices, that all handle one specific part of our platform. An example is that when a user profile is changed, some of this data is synchronized to other services, by using a Firestore triggered Cloud Function.&lt;/p&gt;

&lt;p&gt;This looks like this:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--u6crOQ9k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/3j1f46mcojjgad5hkpz4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--u6crOQ9k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/3j1f46mcojjgad5hkpz4.png" alt="Cloud Functions without Task queue" width="749" height="101"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  We needed queueing
&lt;/h2&gt;

&lt;p&gt;But, we found that in some situations (e.g. when bulk updating user profiles) the logic for syncing to our backend and 3rd party API's were causing errors (rate errors, too many connections, etc.).&lt;/p&gt;

&lt;p&gt;When we would mass-update 100 profiles, 100 cloud functions would try to do their magic. And sometimes, the receiving&lt;br&gt;
Service could not keep up. So we ended up with out-of-sync data as there was no retry (Cloud Functions are triggered once (in general)).&lt;/p&gt;

&lt;p&gt;Therefore, we wanted to implement a queueing mechanism, that does 3 things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Quickly offload the tasks to be executed, so triggered functions can be done quickly&lt;/li&gt;
&lt;li&gt;Rate limit calling external services&lt;/li&gt;
&lt;li&gt;Ensure that the other services are successfully called, or retried when failing.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Google Cloud Tasks
&lt;/h2&gt;

&lt;p&gt;Luckily we ran into Google Cloud Tasks. With that you can execute, dispatch and deliver tasks asynchronously, while having rate and retry controls among other things. And since last year Cloud Tasks supports Creating HTTP Target tasks 🎉&lt;/p&gt;

&lt;p&gt;Creating tasks is well documented, but what I missed in the documentation was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to send data from one (Typescript) Cloud function to receive in another&lt;/li&gt;
&lt;li&gt;How to make authenticated requests, to keep calling my Cloud Functions secure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, in this post I will guide you, step by step, how to setup a Task queue, a way to send JSON from one Cloud Function to another and how to make calling Cloud functions secure with authentication.&lt;/p&gt;

&lt;p&gt;It will look like this:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--R-CSrsIu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/3hqxdr7ojnqdgxiohh88.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--R-CSrsIu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/3hqxdr7ojnqdgxiohh88.png" alt="Cloud Functions using Task Queue" width="393" height="130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I assume that you already know how to work with Google Cloud Functions or FireBase Cloud Functions (it is almost the same in this example).&lt;/p&gt;
&lt;h3&gt;
  
  
  Setup the queue
&lt;/h3&gt;

&lt;p&gt;Make sure you have &lt;a href="https://cloud.google.com/sdk/docs/initializing"&gt;Cloud SDK&lt;/a&gt; and that you are logged in to your project.&lt;/p&gt;

&lt;p&gt;Run the following to set up your queue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gcloud tasks queues create my-queue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will ask you to enable the Cloud Tasks API (Yes, we want that) and if you don't have any app engine or regions set up in your project, it will ask you to do so.&lt;/p&gt;

&lt;p&gt;This is necessary as your Task queue is based on app engine settings.&lt;/p&gt;

&lt;p&gt;If you want to, you can make some adjustments to how your queue behaves:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gcloud tasks queues update my-queue --max-concurrent-dispatches=5 --max-attempts=3 --min-backoff=5s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check how your queue looks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gcloud tasks queues describe my-queue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The 'enqueueing/sending function'
&lt;/h3&gt;

&lt;p&gt;The function that creates the tasks can be normal Cloud Functions, triggered by anything, like GCS, HTTP, Firestore, PubSub triggers, etc.&lt;/p&gt;

&lt;p&gt;For Typescript/Javascript you need to make sure you are using the &lt;a href="https://www.npmjs.com/package/@google-cloud/tasks"&gt;Cloud Tasks Node.js Client: @google-cloud/tasks&lt;/a&gt;. On the NPM page &lt;a href="https://www.npmjs.com/package/@google-cloud/tasks#using-the-client-library"&gt;there is an explanation on how to use the library&lt;/a&gt; but there are two important things to add to make authenticated calls:&lt;/p&gt;

&lt;h4&gt;
  
  
  OIDC token
&lt;/h4&gt;

&lt;p&gt;GCP Services like Cloud Functions, etc. can automatically validate the inbound OIDC token. This allows for authentication and authorization through IAM policies for the user or service account associated with that token. We use this to securely trigger our receiving function over HTTPS without allowing it to be public.&lt;/p&gt;

&lt;p&gt;When setting up the Cloud Function that will be triggered by the task queue, check for the Service account that it uses. Use that email address as the serviceAccountEmail to specify in setting the task on the queue. This is most probably '&lt;a href="//PROJECT_ID@appspot.gserviceaccount.com"&gt;PROJECT_ID@appspot.gserviceaccount.com&lt;/a&gt;'.&lt;/p&gt;

&lt;h4&gt;
  
  
  Content Type JSON
&lt;/h4&gt;

&lt;p&gt;This seems logical once knowing, but if you omit to specify the correct headers, the receiving cloud function cannot use all data you send.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example
&lt;/h4&gt;

&lt;p&gt;The function setting data on the queue could be looking similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;v2beta3&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@google-cloud/tasks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;v2beta3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CloudTasksClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;serviceAccountEmail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PROJECT_ID@appspot.gserviceaccount.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;project&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my-queue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;europe-west1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://europe-west1-PROJECT_ID.cloudfunctions.net/requestQueuedUpdateBackend&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formattedParent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;queuePath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;project&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;httpRequest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;httpMethod&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;oidcToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;serviceAccountEmail&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sending task:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;formattedParent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;task&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Created task &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change PROJECT_ID into your project ID and take note of the OIDC token and the headers for setting the request to JSON. &lt;/p&gt;

&lt;p&gt;If you execute a function with the above snippet, it will enqueue a task on 'my-queue'.&lt;/p&gt;

&lt;h3&gt;
  
  
  The 'triggered function'
&lt;/h3&gt;

&lt;p&gt;The function that is triggered by the Task Queue will do the actual work. This function will be triggered over HTTP, and therefore must not allow unauthenticated invocations (to prevent abuse).&lt;/p&gt;

&lt;p&gt;Furthermore, you want to be able to receive data from the sending function.&lt;/p&gt;

&lt;p&gt;The code could be as simple as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;myFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Received: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;204&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the incoming data is JSON, we have the original payload object again. So you can make use of &lt;code&gt;payload.foo&lt;/code&gt; for instance (= "bar").&lt;/p&gt;

&lt;p&gt;When this function is deployed, take a look at its details (click on the link in of your Cloud Function in the &lt;a href="https://console.cloud.google.com/functions/"&gt;https://console.cloud.google.com/functions/&lt;/a&gt; overview. Check if the Service account is indeed '&lt;a href="//PROJECT_ID@appspot.gserviceaccount.com"&gt;PROJECT_ID@appspot.gserviceaccount.com&lt;/a&gt;'.&lt;/p&gt;

&lt;h4&gt;
  
  
  Keeping it safe
&lt;/h4&gt;

&lt;p&gt;If you look at the &lt;a href="https://console.cloud.google.com/iam-admin/iam"&gt;IAM Permissions of your project&lt;/a&gt; you can see there is a member with the name "Cloud Tasks Service Account" and the role "Cloud Tasks Service Agent". We only want this member to be allowed to call our Cloud Function. Its email address will be &lt;a href="mailto:service-PROJECTNUMBER@gcp-sa-cloudtasks.iam.gserviceaccount.com"&gt;service-PROJECTNUMBER@gcp-sa-cloudtasks.iam.gserviceaccount.com&lt;/a&gt;    → remember / copy that address.&lt;/p&gt;

&lt;p&gt;In the overview of Cloud Functions, look for your cloud function that is triggered by the Task queue, check its checkbox and look at permissions.&lt;/p&gt;

&lt;p&gt;In the block of "Cloud Functions Invoker" make sure there is NOT a member "AllUsers" (publicly accessible). Furthermore, add a member to your Cloud Function Permissions ('&lt;a href="mailto:service-PROJECTNUMBER@gcp-sa-cloudtasks.iam.gserviceaccount.com"&gt;service-PROJECTNUMBER@gcp-sa-cloudtasks.iam.gserviceaccount.com&lt;/a&gt;') and give it the role "Cloud Functions Invoker" ("Ability to invoke HTTP functions with restricted access.").&lt;/p&gt;

&lt;h2&gt;
  
  
  That's it!
&lt;/h2&gt;

&lt;p&gt;You now have set up 3 things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A queue that dispatches tasks (receives tasks and calls Google Cloud Functions) with the help of Google Cloud Tasks.&lt;/li&gt;
&lt;li&gt;A Cloud Function that enqueues a task onto that queue (the enqueueing/sending function)&lt;/li&gt;
&lt;li&gt;A Cloud Function that receives the payload over HTTP from the queue, in a secure way.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At Embrosa we are using this in the following way:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OPQ37Wqu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/vbw3ael4ndvrwnb1wq01.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OPQ37Wqu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/vbw3ael4ndvrwnb1wq01.png" alt="Cloud Functions using Task Queue" width="749" height="131"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And now, the "other services" are not breaking anymore when we have suddenly a surge in the amount of things done on the left side of the diagram, because Cloud Tasks is taking care of limiting the amount of calls and retrying if something might fail.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was published originally on &lt;a href="https://www.rogiervandenberg.nl/google-task-queues-on-gcp-with-google-cloud-functions/"&gt;my website&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
