<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Vsevolod</title>
    <description>The latest articles on DEV Community by Vsevolod (@vseplet).</description>
    <link>https://dev.to/vseplet</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%2F935140%2F807ef961-dc46-4b46-bb9b-451697398a98.jpeg</url>
      <title>DEV Community: Vsevolod</title>
      <link>https://dev.to/vseplet</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vseplet"/>
    <language>en</language>
    <item>
      <title>Tired of bloated installers and complex DevOps pipelines? I built pport.top — a terminal-based messenger — to demo a crazy simple idea: Instant CLI delivery over HTTP Just curl or irm, nothing else TypeScript on the fly via Deno Live deployment</title>
      <dc:creator>Vsevolod</dc:creator>
      <pubDate>Sat, 26 Apr 2025 18:01:57 +0000</pubDate>
      <link>https://dev.to/vseplet/tired-of-bloated-installers-and-complex-devops-pipelines-i-built-httpspporttop-a-52cg</link>
      <guid>https://dev.to/vseplet/tired-of-bloated-installers-and-complex-devops-pipelines-i-built-httpspporttop-a-52cg</guid>
      <description></description>
      <category>typescript</category>
      <category>deno</category>
      <category>cli</category>
      <category>devops</category>
    </item>
    <item>
      <title>📝 I Built nowrite.fun – A Writing App That Deletes Your Text If You Stop Typing</title>
      <dc:creator>Vsevolod</dc:creator>
      <pubDate>Wed, 23 Apr 2025 19:41:05 +0000</pubDate>
      <link>https://dev.to/vseplet/i-built-nowritefun-a-writing-app-that-deletes-your-text-if-you-stop-typing-3ljd</link>
      <guid>https://dev.to/vseplet/i-built-nowritefun-a-writing-app-that-deletes-your-text-if-you-stop-typing-3ljd</guid>
      <description>&lt;p&gt;&lt;strong&gt;Hey devs!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So, I had a free afternoon, some chaotic energy, and an old idea rattling around in my head:&lt;br&gt;
What if you had to write without stopping... or lose everything you’ve typed?&lt;/p&gt;

&lt;p&gt;That’s how nowrite.fun was born — a simple, slightly unhinged writing app where:&lt;/p&gt;

&lt;p&gt;You set a timer (e.g. 5 minutes)&lt;/p&gt;

&lt;p&gt;You start typing&lt;/p&gt;

&lt;p&gt;If you pause, even briefly… 💥 your text disappears. No second chances.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why?&lt;/strong&gt;&lt;br&gt;
I’ve always been intrigued by tools like Write or Die or the old Svečki app (RIP). I wanted to see how fast I could build a modern twist on that idea using AI copilots — GPT-4o, Claude 3.7 — and a solid web stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;🦕 Deno + TypeScript&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🧠 XState for state logic&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🎉 anime.js &amp;amp; canvas-confetti for ✨ vibes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🚀 Hosted on Deno Deploy&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What’s it for?&lt;/strong&gt;&lt;br&gt;
Quick writing sprints: blog intros, tweets, journal entries, brainstorming.&lt;br&gt;
You type → you survive.&lt;br&gt;
You stop → it's all gone.&lt;br&gt;
Brutal, but weirdly satisfying.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wanna support?&lt;/strong&gt;&lt;br&gt;
I launched it on Product Hunt too — it’d mean a lot if you check it out or give it an &lt;a href="https://www.producthunt.com/posts/no-write" rel="noopener noreferrer"&gt;upvote&lt;/a&gt; ❤️&lt;/p&gt;

&lt;p&gt;💻 &lt;a href="https://nowrite.fun" rel="noopener noreferrer"&gt;Try it out!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’d love your thoughts, feature ideas, or "lol this is pure chaos" reactions.&lt;br&gt;
Happy to answer any questions about the tech stack or the (admittedly unhinged) development process.&lt;/p&gt;

&lt;p&gt;Cheers! 🙃&lt;/p&gt;

</description>
      <category>tooling</category>
      <category>vibecoding</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>🦆 Introducing DevExp – a unified CLI platform for indie developers</title>
      <dc:creator>Vsevolod</dc:creator>
      <pubDate>Mon, 14 Apr 2025 09:13:29 +0000</pubDate>
      <link>https://dev.to/vseplet/introducing-devexp-a-unified-cli-platform-for-indie-developers-22em</link>
      <guid>https://dev.to/vseplet/introducing-devexp-a-unified-cli-platform-for-indie-developers-22em</guid>
      <description>&lt;p&gt;Hi dev.to! 👋&lt;/p&gt;

&lt;p&gt;After years of juggling too many tools, managing subscriptions I barely used, and trying to remember endless CLI syntaxes, I finally snapped.&lt;/p&gt;

&lt;p&gt;I'm not good at remembering a million commands. I don't want to babysit 5 different dashboards.&lt;br&gt;
And I hate subscriptions. Especially when they start stacking like Tetris blocks in my brain.&lt;/p&gt;

&lt;p&gt;So I decided to build the platform I’ve always wanted:&lt;br&gt;
A single CLI-first tool that gives me everything I need to build, deploy, and operate — under one roof, with one subscription, and on my terms.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 Meet DevExp (or simply dx)
&lt;/h2&gt;

&lt;p&gt;DevExp is a programmable CLI platform for indie developers, solo hackers, and lean teams.&lt;/p&gt;

&lt;p&gt;Think of it as a developer experience layer that wraps tunnels, secrets, git, databases, LLMs, automations — into one cohesive CLI.&lt;/p&gt;

&lt;p&gt;You install dx, and boom — the toolbox is yours.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔧 What it does (so far)
&lt;/h2&gt;

&lt;p&gt;Here’s what’s working (or in active prototyping):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;dx tunnel — fast HTTP/WS tunnels (think ngrok, but native)&lt;/li&gt;
&lt;li&gt;dx vault — secure secrets &amp;amp; env management&lt;/li&gt;
&lt;li&gt;dx hyper — process manager for Deno-based microservices&lt;/li&gt;
&lt;li&gt;dx llm — use OpenAI, local models, or your own API&lt;/li&gt;
&lt;li&gt;dx git - git identity switching, git flow and more&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Coming soon:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Instant deployments&lt;/li&gt;
&lt;li&gt;Database access &amp;amp; migrations&lt;/li&gt;
&lt;li&gt;Remote script execution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All in one CLI. No tabs. No clutter.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧠 Why I’m building this
&lt;/h2&gt;

&lt;p&gt;I built dx for people like me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Indie devs and makers&lt;/li&gt;
&lt;li&gt;Solo founders and hackers&lt;/li&gt;
&lt;li&gt;Small startups that want fewer tools, not more&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I believe the best developer tools are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fast&lt;/li&gt;
&lt;li&gt;Composable&lt;/li&gt;
&lt;li&gt;Predictable&lt;/li&gt;
&lt;li&gt;And respect your flow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;DevExp is my shot at creating that.&lt;/p&gt;

&lt;h2&gt;
  
  
  🌱 What’s next?
&lt;/h2&gt;

&lt;p&gt;Over the next year, I hope to grow DevExp into a truly unified dev experience.&lt;/p&gt;

&lt;p&gt;One CLI. One install. Zero mental overhead.&lt;br&gt;
From secrets to deploys to AI helpers — everything just works together.&lt;/p&gt;

&lt;p&gt;Imagine never needing to “duct tape” your dev workflow again.&lt;/p&gt;

&lt;h2&gt;
  
  
  🙌 How you can help
&lt;/h2&gt;

&lt;p&gt;DevExp is still early, but growing fast.&lt;br&gt;
If this resonates with you — here's how to support:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⭐ Star the &lt;a href="https://github.com/devexp-pro/cli" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📬 Join the &lt;a href="https://devexp.pro" rel="noopener noreferrer"&gt;waitlist&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🐦 Follow and retweet &lt;a href="https://x.com/devexp_sh/status/1911685210334441937" rel="noopener noreferrer"&gt;@devexp&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💬 Tell your dev friends or drop me feedback below&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for reading! I’m building DevExp in the open — If you’ve ever wished for fewer tools, less cognitive load, and a developer stack that just works — you're not alone.&lt;/p&gt;

</description>
      <category>tooling</category>
      <category>startup</category>
      <category>ai</category>
    </item>
    <item>
      <title>I wonder, am I the only one who installs all sorts of tools like ripgrep, better man, and another hundred utilities whose names I can’t even remember the next day?)</title>
      <dc:creator>Vsevolod</dc:creator>
      <pubDate>Fri, 13 Dec 2024 17:57:42 +0000</pubDate>
      <link>https://dev.to/vseplet/i-wonder-am-i-the-only-one-who-installs-all-sorts-of-tools-like-ripgrep-better-man-and-another-17dh</link>
      <guid>https://dev.to/vseplet/i-wonder-am-i-the-only-one-who-installs-all-sorts-of-tools-like-ripgrep-better-man-and-another-17dh</guid>
      <description></description>
      <category>productivity</category>
      <category>discuss</category>
      <category>watercooler</category>
    </item>
    <item>
      <title>Ohhh, it feels like I've completely forgotten how to truly "code"</title>
      <dc:creator>Vsevolod</dc:creator>
      <pubDate>Fri, 13 Dec 2024 15:12:38 +0000</pubDate>
      <link>https://dev.to/vseplet/ohhh-it-feels-like-ive-completely-forgotten-how-to-truly-code-2ch9</link>
      <guid>https://dev.to/vseplet/ohhh-it-feels-like-ive-completely-forgotten-how-to-truly-code-2ch9</guid>
      <description>&lt;p&gt;Initially, programming for me was something akin to a hobby. It was interesting, fun, challenging enough, and there was always something to do: from creating a simple game to tackling a complex algorithm or rendering project. I still remember one summer evening in the countryside where I used to live: my friend and I were sitting late at night in a log cabin, coding our own version of Digger (remember that DOS game?) in Java Micro Edition on NetBeans 6.8, which somehow managed to take five minutes to load on an old Eee PC. Whole sleepless nights went by like this because I didn’t have internet, and solving even small problems took significant time. It was fascinating, thrilling, but not something that promised any money.&lt;/p&gt;

&lt;p&gt;It’s worth noting that before I officially started my career, I didn’t have to justify my ideas or the time spent on them with concepts like “usefulness” or “practical application.” I hadn’t yet been drawn into this “cult of productivity.” What mattered to me was my interest and the joy I got from the process. And there was a lot of joy.&lt;/p&gt;

&lt;p&gt;But years passed, and today the result is clear: I don’t create anything funny, fun, or interesting. Almost never. I always have to justify and prove the usefulness of my efforts—to myself, to colleagues, friends, or acquaintances. I can’t approach development casually anymore: I immediately start planning architecture, drawing flowcharts, setting up all kinds of configs and microservices. But why? Because that’s how it’s done, that’s the standard in the professional software development world, which has completely consumed me.&lt;/p&gt;

&lt;p&gt;I started with large websites using Nuxt on the front end and Django on the back end, complete with admin panels and various effects and animations on Canvas and WebGL. Later, I moved on to designing network software as a systems programmer to manage thousands of e-ink display price tags using Python and C. Yes, I’m still interested in many things. For example, I still want to write my own Forth system with blackjack or maybe a text-based roguelike (like NetHack). But most of these ideas never go beyond thoughts. Why? Because they probably won’t bring any tangible benefits—or even money. Any such initiative feels like a waste of time, so I immediately discard it.&lt;/p&gt;

&lt;p&gt;Back then, I wasn’t constrained by any frameworks or societal standards and rarely interacted with “industry peers.” This freedom allowed me not just to enter the IT field but also to grow and develop across various domains (from graphics and neural networks to reverse engineering), gaining a broad perspective. And I was happy! Then came serious work, deep thought behind every decision, and experience in designing and building large, complex systems. Along with it came the fear of potentially wasting time, money, and resources, which suddenly became endlessly important. This second phase completely replaced the first, allowing me to grow my income and see myself as a “seasoned pro” who’s worth something. But what’s next? Beyond this lies stagnation and endless attempts to break free from this vicious cycle. Sure, salary growth can continue for quite a while, but the ceiling is already palpable, and higher pay doesn’t make me happier or improve my quality of life—in fact, the opposite.&lt;/p&gt;

&lt;p&gt;And here’s where I started thinking: what if this relentless pursuit of rationalizing every minute and ounce of effort is actually harmful and ultimately leads to stagnation rather than meaningful growth? What if, in fact, coding all kinds of silly things isn’t just okay but absolutely essential? Because it’s one of the few proven ways to create something genuinely interesting or innovative. Let me say it again: every idea I have goes through a series of questions about its theoretical usefulness, feasibility, profitability, and so on. And even if I try to reject this kind of “global” OCD in my life, the people around me will pressure me with the same standards. After all, we’re all “adults” who don’t waste time, right? And while I ask myself these questions or strain to come up with something “useful,” time slips away—time I could have spent developing something personally interesting to me. My energy drains, and I feel exhausted to the core of my mind. Overcoming this inner compulsion to rationalize everything is incredibly difficult! Of course, this doesn’t cancel out the need for a main job, a salary, and so on, but it also prevents me from thinking outside the box.&lt;/p&gt;

&lt;p&gt;So what’s the result? I’m still that “seasoned pro” who knows how to design and build complex systems—but nothing more.&lt;/p&gt;

&lt;p&gt;What’s the solution, then? Should I throw myself into every random project that comes to mind, even if it’s a messenger for pink ponies on FreeDOS? Probably not. These are just thoughts, musings out loud, but I really want to return to those old, primal feelings of programming and achieving goals—even completely useless ones. I want to believe that going back to the beginning could help me grow meaningfully again, break free from the trap of middle-income comfort, and aim higher. Or maybe I’m fundamentally mistaken—who knows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://t.me/itnigilizm" rel="noopener noreferrer"&gt;And more on my channel =)&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>learning</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Like Vim, but Helix</title>
      <dc:creator>Vsevolod</dc:creator>
      <pubDate>Wed, 11 Dec 2024 08:17:45 +0000</pubDate>
      <link>https://dev.to/vseplet/like-vim-but-helix-1eg6</link>
      <guid>https://dev.to/vseplet/like-vim-but-helix-1eg6</guid>
      <description>&lt;p&gt;The creator of Helix calls it a "post-modern text editor." What does that even mean? Who knows. But here's what really matters: multiple cursor support, imports, Tree-sitter, LSP, tons of beautiful themes with great support for popular programming languages — all of it available right after installation. You just download it, open the language config, tweak a couple of LSP settings for your Deno or Node.js projects, and you're good to go. No complicated setup rituals, no digging through obscure manuals, no configuring dozens of plugins (half of which probably won't even work). Even VSCode doesn't offer this kind of flexibility out of the box — but Helix just works.&lt;/p&gt;

&lt;p&gt;When a vim-like editor immediately supports your favorite programming language, you don't have to get sidetracked by configuration — you can focus on what matters most: editing text. It sounds trivial, but with Vim, Neovim, or Emacs, I often didn't even make it to that point — I'd burn out during the setup phase. Helix is different. Sure, you won't memorize all the hotkeys for every mode right away, but the built-in hints and clear documentation help you ease into it quickly. It took me just a couple of days.&lt;/p&gt;

&lt;p&gt;At first, the process feels like a game: you're exploring the editor's capabilities, learning key combinations, diving into the docs. It's fun because there are so many modes and tools. After a week, things start to click automatically. I’ve even caught myself trying to select text in Telegram like I would in Helix — habits form quickly. Yes, the system is complex, but it's intuitive. Once you master it, it becomes incredibly efficient.&lt;/p&gt;

&lt;p&gt;So what’s the catch? Helix stands out because it has nothing extraneous, yet everything you need to just start coding. It doesn't waste your time with plugin setups or deciphering convoluted guides. It's simply an editor, with functionality focused purely on text editing.&lt;/p&gt;

&lt;p&gt;However, there are a few things to keep in mind. Helix adheres to the Unix-way philosophy:&lt;/p&gt;

&lt;p&gt;There are no plugins (yet), though they’re planned for the near future.&lt;br&gt;
There’s no traditional file tree or tabs. Instead, Helix offers an alternative approach to managing files, which can quickly feel intuitive if you give it a chance.&lt;br&gt;
It’s a fresh perspective on text editing and project structure. It's worth your attention. I’d strongly recommend Helix to anyone curious about vim-like editors but intimidated by the steep learning curve. Helix provides a smooth, intuitive introduction, making it easy to grasp the basics. Plus, it rekindles that spark of excitement you get when discovering something new. With Helix, editing text becomes genuinely enjoyable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I use Helix?&lt;/strong&gt;&lt;br&gt;
Right now, it lets me keep dozens of personal and work projects readily accessible. That’s something I couldn't achieve with any IDE or editors like VSCode.&lt;/p&gt;

&lt;p&gt;But remember: Helix is just a text editor. It's designed purely for editing text — nothing more. If you don't expect the impossible, it can be an outstanding tool for your daily workflow.&lt;/p&gt;

&lt;p&gt;If you're looking for a fast, easy-to-learn, and powerful text editor, give Helix a try. It might just be exactly what you need.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://x.com/vseplet" rel="noopener noreferrer"&gt;And more on my twitter&lt;/a&gt; =)&lt;/p&gt;

</description>
      <category>development</category>
      <category>vscode</category>
      <category>vim</category>
      <category>productivity</category>
    </item>
    <item>
      <title>💬 Daily meetings — sh*t?</title>
      <dc:creator>Vsevolod</dc:creator>
      <pubDate>Fri, 13 Oct 2023 17:32:06 +0000</pubDate>
      <link>https://dev.to/vseplet/daily-meetings-sht-2lne</link>
      <guid>https://dev.to/vseplet/daily-meetings-sht-2lne</guid>
      <description>&lt;p&gt;The thing is: I'm endlessly tired of "meetings" myself. I don't like conducting them, don't enjoy listening to all the nonsense unrelated to me, and struggle to recall their outcomes after some time. What's worse is that the timing of these meetings isn't always personally convenient for me. I don't want to step out of the "flow," switch gears, and so on. In general, daily/syncs/stand-ups in your Slack/Google Meet are a total mess (in most cases).&lt;/p&gt;

&lt;p&gt;And what can be done about it? About a year and a half ago, I tried dailies in a text format. The essence is as follows: at a designated time, someone in the group chat sends a "daily message," and colleagues share how things are going in the thread. You won't believe it, but it works and works great. Now I can respond at a convenient time, gather my thoughts, analyze and remember all the work done during the day. It's already productive. But I'm a programmer, and that means automating such a routine. No problem! I wrote a simple script that sends this system message at a designated time. Even better, right? And no magic, everything is straightforward. But something is missing... Searching the chat after a month or two becomes inconvenient, so everything needs to be logged in a unified history and categorized by dates. No problem, now the script sends all replies to a special page in Notion.&lt;/p&gt;

&lt;p&gt;It turned out to be an extremely useful automation: truly productive dailies at a convenient time + keeping a history of replies that you can refer to and analyze the work done, problems, and difficulties the team faced. And this amazing experience, &lt;a href="https://dailylog.denostep.dev/" rel="noopener noreferrer"&gt;I implemented it with my colleagues as a test bot DailyLog&lt;/a&gt; (currently in testing, only for Telegram). Give it a try, test it out, evaluate this format of daily meetings, and share your experience with me &lt;a href="https://t.me/itnigilist" rel="noopener noreferrer"&gt;@itnigilist&lt;/a&gt; or in the comments under this post.&lt;/p&gt;

&lt;p&gt;🔥🔥🔥 Important: tell your friends, colleagues about it. I'm sure many will be happy to reconsider the classic format and increase their productivity!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>💬 Дейлики — это фигня?</title>
      <dc:creator>Vsevolod</dc:creator>
      <pubDate>Fri, 13 Oct 2023 17:28:39 +0000</pubDate>
      <link>https://dev.to/vseplet/dieiliki-eto-fighnia-2g76</link>
      <guid>https://dev.to/vseplet/dieiliki-eto-fighnia-2g76</guid>
      <description>&lt;p&gt;Дело вот в чем: я сам бесконечно устаю от "созвонов". Не люблю их проводить, не люблю слушать всякую дичь, которая не имеет ко мне никакого отношения, с трудом вспоминаю их результаты через какое-то время. Еще хуже то, что время таких встреч не всегда удобно персонально мне, не хочется выходить из "потока", переключаться и так далее. В общем, дейлики/синки/стендапы в этих ваших Slack/Google Meet — тотальная хрень (в большинстве случаев). &lt;/p&gt;

&lt;p&gt;И что с этим можно сделать? Где-то полтора года назад я попробовал дейлики в текстовом формате. Суть в следующем: в назначенный час в групповой чат кто-то отправляет некий "daily message", а товарищи в тред рассказывают о том, как у них дела. Вы не поверите, но это работает и работает отлично. Теперь я могу ответить в удобное время, собраться с мыслями, проанализировать и вспомнить всю проделанную за день работу. Это уже продуктивно. Но я программист, а это значит, что подобную рутину надо автоматизировать. Без проблем! Написал простенький скрипт, который отправляет это системное сообщение в назначенный час. Еще лучше, не правда ли? И никакой магии, все предельно просто. Но чего-то не хватает... Поиск по чату через месяц-два перестает быть удобным, а значит надо все это логгировать в единую историю и разбивать по датам. Без проблем, теперь скрипт все реплаи отправляет на специальную страничку в Notion.&lt;/p&gt;

&lt;p&gt;Получилась крайне полезная автоматизация: действительно продуктивные дейлики в удобное время + ведение истории реплаев, по которой можно ориентироваться и проанализировать проделанную работу, проблемы и трудности с которыми сталкивалась команда. И этот потрясающий опыт я со своими товарищами &lt;a href="https://dailylog.denostep.dev/" rel="noopener noreferrer"&gt;реализовал в виде тестового бота DailyLog&lt;/a&gt; (пока в состоянии тестирования, только для телеги). Попробуйте, протестируйте, оцените данный формат ведения ежедневных встреч и расскажите о вашем опыте мне &lt;a href="https://t.me/itnigilist" rel="noopener noreferrer"&gt;@itnigilist&lt;/a&gt; или в комментах под этим постом. &lt;/p&gt;

&lt;p&gt;🔥🔥🔥 Важно: расскажите про это вашим друзьям, коллегам. Я уверен, что многие будут рады пересмотреть классический формат и повысить свою продуктивность!&lt;/p&gt;

</description>
      <category>management</category>
      <category>startup</category>
      <category>productivity</category>
    </item>
    <item>
      <title>💌 Safe message sending script in Telegram with just 49 lines of code? Really?</title>
      <dc:creator>Vsevolod</dc:creator>
      <pubDate>Sun, 01 Oct 2023 12:05:37 +0000</pubDate>
      <link>https://dev.to/vseplet/safe-message-sending-script-in-telegram-with-just-49-lines-of-code-really-18jf</link>
      <guid>https://dev.to/vseplet/safe-message-sending-script-in-telegram-with-just-49-lines-of-code-really-18jf</guid>
      <description>&lt;p&gt;Mass messaging in messengers is always a challenging task that's hard to solve effectively. I won't be discussing any hacks or workarounds right now; instead, I'll share my approach that fully complies with the platform's usage rules (in this case, Telegram). And yes, I'm a fan of TypeScript + Deno. The latter is actively evolving, gaining new features and functions. One of the recent innovations is queues based on Deno KV, which is why I chose Deno for this article. Intrigued? Then, let's dive in!&lt;/p&gt;




&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;p&gt;The legal way to send messages on Telegram is through bots. However, they come with indefinite restrictions that may change based on the bot's load, Telegram itself, the positions of stars, and magnetic storms. In general, it's approximately ~28 messages per second. What happens if you exceed this limit? Telegram will return a &lt;strong&gt;status 429&lt;/strong&gt; and indicate in the 'retry_after' header how many seconds you need to wait before continuing with the sending. &lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;Based on the aforementioned limitations, I propose implementing a mechanism for mass message distribution on Telegram with the following characteristics:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Strict adherence to the number of messages sent per second.&lt;/li&gt;
&lt;li&gt;Tracking instances of exceeding the limit with a subsequent delay for the specified number of seconds.&lt;/li&gt;
&lt;li&gt;Ability to resend messages in case of failure a certain number of times.&lt;/li&gt;
&lt;li&gt;Waiting for a response to sending is interrupted after a specified timeout.&lt;/li&gt;
&lt;li&gt;Option for delayed sending.&lt;/li&gt;
&lt;li&gt;Script failures and crashes should not result in data loss, and message sending should resume after the script is back on track.&lt;/li&gt;
&lt;li&gt;Whew, sounds complicated? Well, not really!"&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Instruments
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://deno.com/blog/queues" rel="noopener noreferrer"&gt;&amp;gt;&amp;gt; Deno KV&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
Let's start by choosing our tools. The first thing I want to draw your attention to is &lt;strong&gt;Deno KV&lt;/strong&gt;, the built-in key-value database in the &lt;strong&gt;Deno&lt;/strong&gt; runtime. As mentioned earlier, &lt;strong&gt;Deno KV&lt;/strong&gt; now includes a queue mechanism. It's quite straightforward to use and comes with a delay feature. Here's a brief example where a message will be taken from the queue after 1 minute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&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;Deno&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;openKv&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listenQueue&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="nx"&gt;msg&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="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&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;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enqueue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;chat_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;23423423423&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello, World!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;delay&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's crucial to understand that &lt;strong&gt;Deno KV&lt;/strong&gt; is stored as files on your hard drive. So, we have a mechanism for saving data and their deferred processing through a queue right 'out of the box'!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/sevapp/fetchify" rel="noopener noreferrer"&gt;&amp;gt;&amp;gt; Fetchify&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
Next up are the requests, and we need to somehow limit their quantity per unit of time. It would also be good to handle retries in case of errors and manage delays when receiving a &lt;strong&gt;429 status&lt;/strong&gt;. Yes, this could be implemented manually, but I'll opt for my &lt;strong&gt;fetchify&lt;/strong&gt; package, about which I've jotted down notes: &lt;a href="https://dev.to/sevapp/apis-fetch-and-deno-or-how-i-make-rate-limiter-1ch0"&gt;one&lt;/a&gt;, &lt;a href="https://dev.to/sevapp/gentle-promise-based-http-client-for-deno-and-nodejs-part-2-1ian"&gt;two&lt;/a&gt;. It more than meets the needs in this case. Here's a quick example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fetchify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;limiter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Number of requests per second&lt;/span&gt;
    &lt;span class="na"&gt;rps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// You can handle the occurrence of a 429 error&lt;/span&gt;
    &lt;span class="c1"&gt;// and return the time in ms that the request loop should delay&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;429&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://jsonplaceholder.typicode.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;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;hello&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;world&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="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;for &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;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`send &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;i&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="c1"&gt;// All basic methods supported: get post put delete head patch&lt;/span&gt;
  &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/posts/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;i&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;i&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="nx"&gt;data&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;i&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="nx"&gt;err&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;span class="k"&gt;finally&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="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find more details here: &lt;a href="https://github.com/sevapp/fetchify" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; &lt;a href="https://deno.land/x/fetchify" rel="noopener noreferrer"&gt;deno.land/x&lt;/a&gt; &lt;a href="https://www.npmjs.com/package/@sevapp/fetchify" rel="noopener noreferrer"&gt;npm&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;Now we have everything we need. Ready? Let's take a look at the next &lt;a href="https://gist.github.com/sevapp/876e76399c2f88129f5259e17afe9582" rel="noopener noreferrer"&gt;49 lines&lt;/a&gt; of code and rejoice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fetchify&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://deno.land/x/fetchify@0.2.8/mod.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TelegramMailer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;tgApi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Deno&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Kv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Deno&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Kv&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tgApi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fetchify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`https://api.telegram.org/bot&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;token&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="na"&gt;limiter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;rps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 28 requests per second&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;429&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;retry_after&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
            &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&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="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// call before sending!!!&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;queue&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;Deno&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;openKv&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;this&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="nf"&gt;listenQueue&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="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// @ts-expect-error&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;chat_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="k"&gt;try&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;res&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tgApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sendMessage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10000&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;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;chat_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;text&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="s2"&gt;`message delivered to &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;chat_id&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="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="s2"&gt; &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;statusText&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;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`the message was not delivered to &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;chat_id&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chat_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&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="k"&gt;this&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="nf"&gt;enqueue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;chat_id&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's all for them! Nothing more is needed. Now we can invoke the notify function, save messages to the queue, and send them to Telegram with delays and limits. Of course, this code can be broken with excessive load, but for most tasks, it'll do just fine :)&lt;/p&gt;




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

&lt;p&gt;In the end, I managed to implement a fairly complex message distribution mechanism for Telegram, considering limits, making it resilient to crashes, and network issues. And all of this in just 55 lines of TypeScript code for Deno!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;PS: I'd be happy if you subscribe to my channel &lt;a href="https://t.me/+RqAiKK3PqLVhNTFi" rel="noopener noreferrer"&gt;IT NIGILIZM&lt;/a&gt; and join the russian-speaking &lt;a href="https://t.me/+Gml5i487e04yOWUy" rel="noopener noreferrer"&gt;deno runtime community&lt;/a&gt; ❤️!&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>deno</category>
      <category>typescript</category>
      <category>tutorial</category>
      <category>javascript</category>
    </item>
    <item>
      <title>🦖 My attempt to make an HTTP client...</title>
      <dc:creator>Vsevolod</dc:creator>
      <pubDate>Fri, 29 Sep 2023 19:34:33 +0000</pubDate>
      <link>https://dev.to/vseplet/my-attempt-to-make-an-http-client-495m</link>
      <guid>https://dev.to/vseplet/my-attempt-to-make-an-http-client-495m</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Earlier, I explained in two parts why all of this is needed: &lt;a href="https://dev.to/sevapp/apis-fetch-and-deno-or-how-i-make-rate-limiter-1ch0"&gt;PART 1&lt;/a&gt; и &lt;a href="https://dev.to/sevapp/gentle-promise-based-http-client-for-deno-and-nodejs-part-2-1ian"&gt;PART 2&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;✅ &lt;a href="https://t.me/+Gml5i487e04yOWUy" rel="noopener noreferrer"&gt;DENO RU COMMUNITY&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  fetchify
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/sevapp/fetchify" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; &lt;a href="https://deno.land/x/fetchify" rel="noopener noreferrer"&gt;deno.land/x&lt;/a&gt; &lt;a href="https://www.npmjs.com/package/@sevapp/fetchify" rel="noopener noreferrer"&gt;npm&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This package is designed to make the process of interacting with various APIs&lt;br&gt;
that have strict limitations more convenient and careful. For example, this&lt;br&gt;
could be APIs like Notion or Telegram, which have stringent limits.&lt;/p&gt;
&lt;h2&gt;
  
  
  👋 👋 ATTENTION!
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;This package is under development and will be frequently updated. The author&lt;br&gt;
would appreciate any help, advice, and pull requests! Thank you for your&lt;br&gt;
understanding 😊&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Import
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Deno:
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;From &lt;a href="https://deno.land/x/fetchify" rel="noopener noreferrer"&gt;deno.land/x&lt;/a&gt;:&lt;/p&gt;


&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fetchify&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://deno.land/x/fetchify@0.2.7/mod.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Or &lt;a href="//esm.sh"&gt;esm.sh&lt;/a&gt;:&lt;/p&gt;


&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fetchify&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://esm.sh/gh/sevapp/fetchify@0.2.7/mod.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Node.JS:
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Install from &lt;a href="https://www.npmjs.com/package/@sevapp/fetchify" rel="noopener noreferrer"&gt;npm&lt;/a&gt;:&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;--save&lt;/span&gt; @sevapp/fetchify
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;And import:&lt;/p&gt;


&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fetchify&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fetchify&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Usage:
&lt;/h2&gt;

&lt;p&gt;The first thing available to you is the &lt;strong&gt;fetchify&lt;/strong&gt; function&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await &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;fetchify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://catfact.ninja/fact&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;json&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="nf"&gt;log&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function has a similar interface to the classic &lt;strong&gt;fetch&lt;/strong&gt; but extends it&lt;br&gt;
with additional options, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await &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;fetchify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://catfact.ninja/fact&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Now, the waiting for a response will be interrupted after 1000 ms.&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;json&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="nf"&gt;log&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But you can also create an instance with a set base URL and rate-limiting&lt;br&gt;
constraints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fetchify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;limiter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Number of requests per second&lt;/span&gt;
    &lt;span class="na"&gt;rps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// You can handle the occurrence of a 429 error&lt;/span&gt;
    &lt;span class="c1"&gt;// and return the time in ms that the request loop should delay&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;429&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://jsonplaceholder.typicode.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;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;hello&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;world&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="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;for &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;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`send &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;i&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="c1"&gt;// All basic methods supported: get post put delete head patch&lt;/span&gt;
  &lt;span class="nx"&gt;jph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/posts/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;i&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;i&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="nx"&gt;data&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;i&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="nx"&gt;err&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;span class="k"&gt;finally&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="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;Yes, all methods comply with the &lt;strong&gt;fetch&lt;/strong&gt; interface but also extend it with&lt;br&gt;
additional options, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;jph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/posts/10`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Number of attempts&lt;/span&gt;
  &lt;span class="na"&gt;attempts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
  &lt;span class="c1"&gt;// Time after which we stop waiting for a response&lt;/span&gt;
  &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you need to make a request to the configured &lt;strong&gt;baseURL&lt;/strong&gt; but not through the&lt;br&gt;
request queue, you can add the flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;jph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/posts/10`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;unlimited&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>node</category>
      <category>deno</category>
      <category>typescript</category>
      <category>javascript</category>
    </item>
    <item>
      <title>🦖 Gentle, promise-based HTTP client for Deno and Node.js (part 2)</title>
      <dc:creator>Vsevolod</dc:creator>
      <pubDate>Wed, 27 Sep 2023 17:27:39 +0000</pubDate>
      <link>https://dev.to/vseplet/gentle-promise-based-http-client-for-deno-and-nodejs-part-2-1ian</link>
      <guid>https://dev.to/vseplet/gentle-promise-based-http-client-for-deno-and-nodejs-part-2-1ian</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/sevapp/fetchify" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; &lt;a href="https://deno.land/x/fetchify" rel="noopener noreferrer"&gt;deno.land/x&lt;/a&gt; &lt;a href="https://www.npmjs.com/package/@sevapp/fetchify" rel="noopener noreferrer"&gt;npm&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;✅ &lt;a href="https://t.me/+Gml5i487e04yOWUy" rel="noopener noreferrer"&gt;DENO RU COMMUNITY&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/sevapp/apis-fetch-and-deno-or-how-i-make-rate-limiter-1ch0"&gt;PART 1&lt;/a&gt; (please review the first part before reading this article &amp;lt;3)&lt;/p&gt;

&lt;p&gt;Hello! It's Vsevolod again 😎 In the first part, I outlined the problem: when working with various APIs, we often encounter limits and restrictions that the standard &lt;strong&gt;fetch&lt;/strong&gt; function cannot handle. During my contemplation, I arrived at a solution (&lt;a href="https://github.com/sevapp/fetchify/blob/0.0.1/src/HTTPLimiter.ts" rel="noopener noreferrer"&gt;source code&lt;/a&gt;), which involved creating a queue through which all &lt;strong&gt;fetch&lt;/strong&gt; requests would pass at a certain interval. Yes, this resolves the problem described above, but it is highly inefficient...(&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;❤️ And please, support the project by starring it on GitHub or subscribing to my Telegram &lt;br&gt;
channel &lt;a href="https://t.me/itnigilizm" rel="noopener noreferrer"&gt;IT NIGILIZM&lt;/a&gt;!&lt;br&gt;
This motivates me to continue developing this topic, writing new articles, and improving this tool!&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🫠 Mistakes analysis:
&lt;/h2&gt;

&lt;p&gt;So, what's the plan? Let's tackle this! The solution from the first part is inefficient because we're making &lt;strong&gt;N&lt;/strong&gt; requests at equal time intervals, waiting for each request to complete. Let's assume we need to make 3 requests per second, no more, right? Take a look at the code from the previous article:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&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="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;interval&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;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&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="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;try&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&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="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&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="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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;What could go wrong? Let's calc:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1st request ~300ms +
2nd request ~200ms +
3rd request ~250ms +
interval 1000ms = 1750ms!!!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, it means that it will take us a long time to complete the first three requests, and the next three requests will only start after 1750ms. But we wanted to make 3 requests per second. What can be done about this? The most obvious solution is not to send requests sequentially, waiting for responses, but to do it "in parallel".&lt;br&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ✨ New Solution!
&lt;/h2&gt;

&lt;p&gt;Alright, let's start by adding a few new fields to HTTPLimiter (hereinafter referred to as "Limiter"):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;requestsPerIteration&lt;/strong&gt; - This field will serve as a counter, keeping track of the number of requests sent during one "iteration."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;iterationStartTime&lt;/strong&gt; - This is the start time of the batch request sending process in milliseconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;loopIsWorking&lt;/strong&gt; - A flag indicating whether the loop is active (I want to stop the request processing loop if the queue becomes empty).
In &lt;strong&gt;IHTTPLimiterOptions&lt;/strong&gt;, let's add &lt;strong&gt;rps: number&lt;/strong&gt; (requests per second) for configuring the Limiter's loop.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Additionally, we will make some changes to the &lt;strong&gt;fetch&lt;/strong&gt; function so that it triggers the request processing loop as needed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FetchInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;RequestInit&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Response&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;promise&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;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;reject&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="c1"&gt;// Now we don’t have to run the loop “manually”&lt;/span&gt;
    &lt;span class="c1"&gt;// and leave it running in the background forever&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;loopIsWorking&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;loopIsWorking&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;loopIsWorking&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;promise&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;Great, now let's move on to the breakdown of the new loop. First and foremost, we need to remember the time when we started the loop and began sending requests. This is important because now we will make an honest effort to accurately count the number of requests per second:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;iterationStartTime&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;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first thing to do when entering the loop is to check the number of requests we have already made. If it exceeds the allowed limit per second, we skip the iteration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;    &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;requestsPerIteration&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rps&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="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;continue&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;Unlike the previous implementation, now fetch is executed asynchronously compared to other fetches in the queue. We don't wait for a response to the previous request to send the next one; instead, we simply increment the counter and decrement it when a response arrives:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;requestsPerIteration&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&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;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&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="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;error&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;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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="k"&gt;finally&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;requestsPerIteration&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, I establish the termination rule for the loop, namely:&lt;br&gt;
if the queue is empty, we don't expect any more responses to the sent requests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;entity&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;requestsPerIteration&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;loopIsWorking&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;loopIsWorking&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&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;🤢 Here's a small workaround that prevents the request processing loop from blocking, which would otherwise hinder the handling of other promises:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;entity&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;iterationStartTime&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&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="nf"&gt;delay&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the last part of the loop, controlling the intervals. If we hit the limit on the number of requests, we calculate the time we need to delay before starting the next iteration. It's not a perfect solution, but it works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;requestsPerIteration&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rps&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;timeOffset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;iterationStartTime&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1000&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;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getTime&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;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timeOffset&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// !!! there are no guarantees that sent requests will be executed after this delay&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;iterationStartTime&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;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Certainly, this is not a perfect solution: it can lead to many problems (for example, if the requests themselves have a very long delay) and errors. But now the loop operates much more efficiently than the approach in the first part.&lt;/p&gt;




&lt;h2&gt;
  
  
  🫶 In conclusion and for the future...
&lt;/h2&gt;

&lt;p&gt;Thank you very much for reading the second part of the article! You can find the entire code on GitHub, and I've also created a port for nodejs, which is now available on NPM (all links below). I hope to create a good alternative to great modules like ky or axios, taking into account the limits and requirements of various APIs. I'll certainly appreciate any suggestions or pull requests.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/sevapp/fetchify" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; &lt;a href="https://deno.land/x/fetchify" rel="noopener noreferrer"&gt;deno.land/x&lt;/a&gt; &lt;a href="https://www.npmjs.com/package/@sevapp/fetchify" rel="noopener noreferrer"&gt;npm&lt;/a&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>deno</category>
      <category>javascript</category>
      <category>node</category>
    </item>
    <item>
      <title>🦖 APIs, fetch and Deno or how I make rate limiter (part 1)</title>
      <dc:creator>Vsevolod</dc:creator>
      <pubDate>Mon, 25 Sep 2023 18:20:43 +0000</pubDate>
      <link>https://dev.to/vseplet/apis-fetch-and-deno-or-how-i-make-rate-limiter-1ch0</link>
      <guid>https://dev.to/vseplet/apis-fetch-and-deno-or-how-i-make-rate-limiter-1ch0</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/sevapp/fetchify" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; &lt;a href="https://deno.land/x/fetchify" rel="noopener noreferrer"&gt;deno.land/x&lt;/a&gt; &lt;a href="https://www.npmjs.com/package/@sevapp/fetchify" rel="noopener noreferrer"&gt;npm&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;✅ &lt;a href="https://t.me/+Gml5i487e04yOWUy" rel="noopener noreferrer"&gt;DENO RU COMMUNITY&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/sevapp/gentle-promise-based-http-client-for-deno-and-nodejs-part-2-1ian"&gt;PART 2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hello, my name is Vsevolod! And you know what? I've grown very fond of Deno! And you know what else? I really love TypeScript! These two tools faithfully serve me on the challenging path of developing various integrations, automations, and all sorts of tools. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;❤️ And please, support the project by starring it on GitHub or subscribing to my Telegram channel &lt;a href="https://t.me/itnigilizm" rel="noopener noreferrer"&gt;IT NIGILIZM&lt;/a&gt;!&lt;br&gt;
This motivates me to continue developing this topic, writing new articles, and improving this tool!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Of course, during our work, we often have to interact with various APIs. For example, these could be the Telegram Bot API or the Notion API. Do you know what they have in common? Well, all these APIs come with certain rate limits. For instance, when using the Notion API, it's recommended not to exceed an average of 3 requests per second. But what do you do if your application generates far more requests than the limits of a specific API allow? What if the minimum time intervals between requests can vary dynamically?&lt;/p&gt;




&lt;h2&gt;
  
  
  💭 Let's explore:
&lt;/h2&gt;

&lt;p&gt;The first thing that came to my mind is to create a queue of requests. The idea is simple: instead of using fetch, we push all our requests into a queue (which could be a regular Array). Then, in a loop, we gradually process these requests, calling our beloved fetch for each one and storing the received data somewhere. Of course, the request processing loop should adhere to the intervals between them according to the limits of the specific API. Isn't it beautiful? Let's look:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;delay&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://deno.land/std@0.202.0/async/delay.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IMyRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&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;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&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="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IMyRequest&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;responses&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;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;Error&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&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;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&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="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;interval&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;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&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="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;try&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&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="nx"&gt;responses&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;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;responses&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;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myFetch&lt;/span&gt; &lt;span class="o"&gt;=&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&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;id&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="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;id&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;Excellent! Now we can create a single request or a series of requests to be executed sequentially with a 100ms interval. Yes, we'll have to wait for a while, and then retrieve the results from &lt;strong&gt;responses&lt;/strong&gt; by their &lt;strong&gt;id&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;myFetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://jsonplaceholder.typicode.com/todos/1`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, tracking errors/responses in this example would not be very convenient. We could add a callback to &lt;strong&gt;IMyRequest&lt;/strong&gt; that would be invoked after receiving the data. However, that would bring us back to callback hell))) But this code already partially solves this problem!&lt;br&gt;
&lt;br&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  💡 Idea:
&lt;/h2&gt;

&lt;p&gt;Yes, &lt;strong&gt;fetch&lt;/strong&gt; is much more convenient. Why? Well, &lt;strong&gt;fetch&lt;/strong&gt;  returns a &lt;strong&gt;promise&lt;/strong&gt;, and we can easily wait for it using either &lt;strong&gt;await&lt;/strong&gt; or &lt;strong&gt;then&lt;/strong&gt; (whichever you prefer). And I think it would be great to preserve this mechanism. In other words, we need a fetch that allows us to &lt;strong&gt;await&lt;/strong&gt; a &lt;strong&gt;response&lt;/strong&gt; but sends requests in accordance with the specified limits through a &lt;strong&gt;queue&lt;/strong&gt;. What if myFetch returns a &lt;strong&gt;Promise&lt;/strong&gt; that will resolve when the request reaches the queue and the data is returned or an error occurs? In that case, we can use &lt;strong&gt;async/await&lt;/strong&gt; to wait for the data, even though the requests go through a &lt;strong&gt;queue&lt;/strong&gt;. Let's give it a try:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;delay&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://deno.land/std@0.202.0/async/delay.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IMyRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&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;queue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IMyRequest&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myFetch&lt;/span&gt; &lt;span class="o"&gt;=&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="kr"&gt;string&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;promise&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;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&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;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&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="nx"&gt;resolve&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;promise&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;loop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&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;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&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="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;interval&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;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&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="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;try&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&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="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&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="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Amazing! Now we can create a large number of requests and await their responses using &lt;strong&gt;await&lt;/strong&gt; or &lt;strong&gt;then&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;for &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;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;myFetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://jsonplaceholder.typicode.com/todos/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;i&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="nf"&gt;then&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="nx"&gt;response&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nb"&gt;Error&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="nf"&gt;log&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="k"&gt;else&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="nf"&gt;log&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🏁 Final result:
&lt;/h2&gt;

&lt;p&gt;Not bad already) &lt;br&gt;
But that was just an example, not "production" code. Using it would be challenging because &lt;strong&gt;myFetch&lt;/strong&gt; is far from perfect; it needs to be made similar to &lt;strong&gt;fetch&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Well, I decided to create a basic version and publish it as a separate package on deno.land/x. Try it out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fetchify&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://deno.land/x/fetchify@0.0.2/mod.ts&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;limiter&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;fetchify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;HTTPLimiter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;for &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;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&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;limiter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`https://jsonplaceholder.typicode.com/todos/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;i&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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🥰 Conclusion!
&lt;/h2&gt;

&lt;p&gt;I hope that these thoughts will be helpful to someone. I understand that there may already be existing modules for Deno or Node.js that partially solve this problem, but I was interested in figuring it out on my own. And don't let my imperfect English deter you. Thank you for your attention!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/sevapp/fetchify" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; &lt;a href="https://deno.land/x/fetchify" rel="noopener noreferrer"&gt;deno.land/x&lt;/a&gt; &lt;a href="https://www.npmjs.com/package/@sevapp/fetchify" rel="noopener noreferrer"&gt;npm&lt;/a&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>deno</category>
      <category>api</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
