<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Alexander's Blog Blog</title>
        <link>https://www.alexanderlolis.com</link>
        <description>Alexander's Blog Blog</description>
        <lastBuildDate>Tue, 16 Dec 2025 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <copyright>Copyright © 2026 Alexander Lolis.</copyright>
        <item>
            <title><![CDATA[My 2025 reads]]></title>
            <link>https://www.alexanderlolis.com/my-2025-reads</link>
            <guid>https://www.alexanderlolis.com/my-2025-reads</guid>
            <pubDate>Tue, 16 Dec 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[How big things get done]]></description>
            <content:encoded><![CDATA[<p><a href="https://www.goodreads.com/book/show/73004305-how-big-things-get-done" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="How big things get done" src="https://www.alexanderlolis.com/assets/images/how_big_things_get_done_bent_flyvbjerg-57d13f6e1cca71d35ab751e73071109d.jpg" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/50364458-the-tyranny-of-merit" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="The Tyranny of Merit: What&amp;#39;s Become of the Common Good?" src="https://www.alexanderlolis.com/assets/images/the_tyranny_of_merit_michael_sandel-02f6705efb45f32de1e2e0b7ab10fc20.png" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/4099.The_Pragmatic_Programmer" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="The Pragmatic Programmer: From Journeyman to Mastery, 20th Anniversary Edition" src="https://www.alexanderlolis.com/assets/images/the_pragmatic_programmer_20th_anniversary_edition_andy_hunt-3167d9674e41ef6f0037d6f503ef5e2d.jpg" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/566213.The_Secrets_of_Consulting" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="The Secrets of Consulting: A Guide to Giving and Getting Advice Successfully" src="https://www.alexanderlolis.com/assets/images/the_secrets_of_consulting_gerald_weinberg-e0cd72f11b342d6d649220df3f3fad6a.jpg" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/6732019-rework" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="Rework" src="https://www.alexanderlolis.com/assets/images/rework_jason_fried-db590360e32ac7b898141bc35625f050.jpg" width="167" height="250" class="img_ev3q"></a></p>
<ul>
<li class=""><strong>[5/5/] How big things get done</strong>: This was a very, VERY, good read. This is how a book, filled with real experience and backed by data looks like. One of the very few management books that I have read that it didn't feel like "vague stories" but as real, hard earned insights from actual projects. I strongly recommend reading it. If I had to keep one thing from the whole book is this: Think slow, act fast.</li>
<li class=""><strong>[5/5/] The Tyranny of Merit: What's Become of the Common Good?</strong>: This book was a great eye-opener regarding the dangers of meritocracy. The core idea is that meritocracy breeds hubris among winners and humiliation among losers. I agree with the author’s argument that meritocracy ignores a crucial factor: luck. It convinces people that their success is entirely of their own making, while blinding them to the advantages that allowed them to succeed in the first place. The result is not just inequality, but a moral divide that erodes solidarity. A strong and unsettling read.</li>
<li class=""><strong>[5/5/] The Pragmatic Programmer: From Journeyman to Mastery, 20th Anniversary Edition</strong>: It has been some time since I read the original version of the book and the anniversary edition was sitting there in my bookshelf so I felt like re-reading it. I am glad I did. This is a book that every software engineer should read. Years of distilled experience are written in these pages and the important part is not the code examples, it's the philosophy of the book with which I agree 100%. YOU are responsible for the quality of your work. Not your tools, not your manager, not the process. YOU. Be adaptable and never stop improving yourself by treating software development as a continuous learnign and decision-making discipline.</li>
<li class=""><strong>[4/5/] The Secrets of Consulting: A Guide to Giving and Getting Advice Successfully</strong>: This is probably one of the few books I have read about consulting and genuinely liked. Its core idea is that consulting is fundamentally a human and political activity, not a technical one. Success depends less on having the "right" answers and more on managing expectations, relationships, and responsibility. I completely agree with this perspective. The book does a great job of describing common pitfalls consultants fall into and stays practical throughout. I highly recommend it, especially if you’re considering venturing into the consulting world.</li>
<li class=""><strong>[4/5/] Rework</strong>: I really like the way these guys (Jason Fried and David Heinemeier Hansson) work. They get things done and consistently cut through the BS to focus on what really matters: delivering value. The book challenges many assumptions people take for granted about work and growth. Its core message is that simplicity, focus, and restraint often beat scale, speed, and ambition. Some ideas felt quite opinionated and context-dependent, but overall it offers a refreshing perspective on running a business without the hustle and overcomplication that most organizations fall into these days. Definitely an interesting read.</li>
</ul>]]></content:encoded>
            <category>books</category>
        </item>
        <item>
            <title><![CDATA[My 2024 reads]]></title>
            <link>https://www.alexanderlolis.com/my-2024-reads</link>
            <guid>https://www.alexanderlolis.com/my-2024-reads</guid>
            <pubDate>Wed, 25 Dec 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[Peopleware: Productive Projects and Teams]]></description>
            <content:encoded><![CDATA[<p><a href="https://www.goodreads.com/book/show/67825.Peopleware" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="Peopleware: Productive Projects and Teams" src="https://www.alexanderlolis.com/assets/images/peopleware_tom_demarco_timothy_lister-f41d1fa51a157b82017bdeb4c29f76a0.png" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/17255186-the-phoenix-project" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="The Phoenix Project" src="https://www.alexanderlolis.com/assets/images/the_phoenix_project_gene_kim_kevin_behr_george_spafford-69b6b58bc9276700c033ba3cf4fd7d95.png" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/18713393-how-to-solve-it" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="How to Solve It: A New Aspect of Mathematical Method" src="https://www.alexanderlolis.com/assets/images/how_to_solve_it_george_polya-817ab7edf21106909307876fb826cded.png" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/23692271-sapiens" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="Sapiens: A Brief History of Humankind" src="https://www.alexanderlolis.com/assets/images/sapeniens_a_brief_history_of_humankind_yuval_noah_harari-c02a901ee419af5951ae32a084b69841.png" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/63096355-the-art-of-shralpinism" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="The Art of Shralpinism: Lessons from the Mountains" src="https://www.alexanderlolis.com/assets/images/the_art_of_shralpinism_jeremy_jones-e603b7af701d800819a7c3cb93ac8f30.png" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/44279111-why-are-we-yelling" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="Why Are We Yelling?: The Art of Productive Disagreement" src="https://www.alexanderlolis.com/assets/images/why_are_we_yelling_buster_benson-606487d6b79330c08dd2a42665ccb71c.png" width="167" height="250" class="img_ev3q"></a></p>
<ul>
<li class=""><strong>[5/5/] Peopleware: Productive Projects and Teams</strong>: If you are a manager in a software company then you need to read this NOW. The important lesson here is that people are the core of any development process, something that a lot of managers forget most of the time. The book was written in 1987 and still applies today.</li>
<li class=""><strong>[5/5/] The Phoenix Project</strong>: Loved it. I started reading it a couple of years ago and left it in the middle so decided to start from the beginning again. I am glad I did that because it was definitely worth my full attention. I am pretty sure that anyone long enough in IT will appreciate it and resonate with Bill's (the hero of the book) daily "adventures" in the crazy world of technology mixed with business. I also recommend reading <em>The Goal, by Eliyahu M. Goldratt</em> which I believe it's what basically inspired the writing of this book as well.</li>
<li class=""><strong>[4/5] How to Solve It: A New Aspect of Mathematical Method</strong>: A very insighful book which applies even today, even if it's written in 1945 and it's around mathematics and geometry. Although it gets a bit repetetive, it's definitely worth it since it's filled with examples in order to help the reader understand its underlying framework for problem solving. Polya manages to translate his approach from the mathematics field to other domains that apply to our daily lifes.</li>
<li class=""><strong>[4/5/] Sapiens: A Brief History of Humankind</strong>: A VERY complex and extensive topic that I think the author manages to summarize it pretty well in 500 pages and keeps the reader engaged. Lot's of interesting information and ideas, and even though I am sceptical about some, the fact that it does give an interesting perspective to human history is definitely intriguing and worth the read. The central idea around the book is that the humans did a huge mistake by turning from from being hunter-gatherers to agriculture which made them settle down, lose important knowledge and skills that all anchestors posesed and made us more miserable and harsh. Really enjoyed it, definitely recommend.</li>
<li class=""><strong>[3/5] The Art of Shralpinism: Lessons from the Mountains</strong>: When a master speaks, you listen. Jeremy Jones is definitely a master in snowboarding and mounteneering and I was anxious to read his book. I really, really wanted to love it but I am afraid, it failed short to my expectations. First of all, there are a lot of story sections which due to the chosen font and background, are either very hard to read or compltely unreadable. Secondly, I was expecting to gain some deep knowledge which someone can only gain by doing it, and even though Jeremy Jones has been out there for a long time and have gained years and years of experience, he has failed to transfer at least some of it. Although there are tips and technical notes in the book here and there, in general it felt a little bit vague. Sorry Jeremy, I still respect you deeply for doing what you are doing, but for the next book, get a writing consultant!</li>
<li class=""><strong>[1/5] Why Are We Yelling? The Art of Productive Disagreement</strong>: A very interesting question which the author has failed to answer. At first, the book looks promising but as you keep going the author starts rambling about gun violence, politics and other completely unrelated things with the topic. Do not waste your time.</li>
</ul>]]></content:encoded>
            <category>books</category>
        </item>
        <item>
            <title><![CDATA[My 2023 reads]]></title>
            <link>https://www.alexanderlolis.com/my-2023-reads</link>
            <guid>https://www.alexanderlolis.com/my-2023-reads</guid>
            <pubDate>Tue, 26 Dec 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[Code: The Hidden Language of Computer Hardware and Software]]></description>
            <content:encoded><![CDATA[<p><a href="https://www.goodreads.com/book/show/44882.Code" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="Code: The Hidden Language of Computer Hardware and Software" src="https://www.alexanderlolis.com/assets/images/code_charles_petzold-3591d3b764ef5a0fdeb16702d3f759c0.png" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/11721966-good-strategy-bad-strategy" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="Good Strategy Bad Strategy: The Difference and Why It Matters" src="https://www.alexanderlolis.com/assets/images/good_strategy_bad_strategy_richard_rumelt-9462318363bfc8268fc274f5225b6b49.png" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/43190966-refactoring-ui" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="Refactoring UI" src="https://www.alexanderlolis.com/assets/images/refactoring_ui_adam_wathan_steve_schoger-d6e457ef1033193a2ce9a06d5658ed29.png" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/113934.The_Goal" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="The Goal: A Process of Ongoing Improvement" src="https://www.alexanderlolis.com/assets/images/the_goal_eliyahu_n._goldratt_jeff_cox-4f449a92724476e8a0dbd33316963626.png" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/56034793-seven-and-a-half-lessons-about-the-brain" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="Seven and a Half Lessons About the Brain" src="https://www.alexanderlolis.com/assets/images/seven_and_a_half_lessons_about_the_brain_lisa_feldman_barrett-a83fdac0041b552146748456149fdc6a.png" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/42114632-phoenix-in-action" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="Phoenix in Action" src="https://www.alexanderlolis.com/assets/images/phoenix_in_action_geoffrey_lessel-8b0c302198a9545232f21b6571386467.png" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/30724353-rxjs-in-action" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="RxJS in Action" src="https://www.alexanderlolis.com/assets/images/rxjs_in_action_paul_daniels_luis_atencio-ab7e46e32f485f92499cd42dc7c6ca23.png" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/40582814-programming-ecto" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="Programming Ecto" src="https://www.alexanderlolis.com/assets/images/programming_ecto_darin_wilson_eric_meadows-2a8319158498b8e6a7b9341da9250c89.png" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/8677004-the-art-of-readable-code" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="The Art of Readable Code: Simple and Practical Techniques for Writing Better Code" src="https://www.alexanderlolis.com/assets/images/the_art_of_readable_code_dustin_boswell_trevor_foucher-58db17e562606db268d510c51bc50734.png" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/113847910-cto-excellence-in-100-days" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="CTO Excellence in 100 Days: Becoming the Leader Your Company Deserves" src="https://www.alexanderlolis.com/assets/images/cto_excellence_in_100_days-0cd622161e336969c6fc5584d23d20a6.png" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/30556551-nobody-wants-to-read-your-sh-t" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="Nobody Wants to Read Your Sh*t: Why That Is And What You Can Do About It" src="https://www.alexanderlolis.com/assets/images/nobody_wants_to_read_your_shit_steven_pressfield-eb1e90fbccbfb20bb8338a6ce1c1c710.png" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/69571.Rich_Dad_Poor_Dad" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="Rich Dad, Poor Dad" src="https://www.alexanderlolis.com/assets/images/rich_dad_poor_dad_robert_kiyosaki-37f42a4168d93bebf8474e99f2a58e6e.png" width="167" height="250" class="img_ev3q"></a></p>
<ul>
<li class=""><strong>[5/5/] Code</strong>: This book was in my bookshelf for some time now and I finally got around it. Although most of the concepts were known to me this is the first time that I felt everything coming together and got plenty of "Aha!" moments. I must admit, that a couple of chapters got too deep technically and I've only read them without trying to fully understand them, but they were definitely worth going through them. This book is a <strong>MUST</strong> for anyone into computer science</li>
<li class=""><strong>[4/5/] Good Strategy Bad Strategy</strong>: I really enjoyed this book and how it defines strategy and makes a clear distinction between a good and a bad one. Lots of good real world examples and actionable ideas. I felt that a few sections could have been more compact but it definitely wasn't tiresome. I strongly recommend reading it</li>
<li class=""><strong>[4/5] Refactoring UI</strong>: This book is not only for designers, but for developer as well. In fact, I would probably say that a developer needs to read this more than a designer. It definitely gives an interesting pespective on design and how to keep things clean and simple, to get things going instead of losing yourself into too much details. I strongly recommend reading it</li>
<li class=""><strong>[4/5] The Goal</strong>: A very well written process improvement novel. The story was very realistic  with practical lessons that can be applied to the real world. For me, an important lesson here was that open-minded teams which want to solve problems and improve, will find the way by communicating well and by being willing to go through an iterative process. The story will probably be more interesting to people that are in the manufacturing industry but in general I find it useful for anyone that deals with process flows. It was a breez reading it and if you liked it and you are in IT, I also strongly recommend reading <em>The Phoenix Project</em> by Gene Kim</li>
<li class=""><strong>[4/5] Seven and a Half Lessons About the Brain</strong>: A short and very interesting book on human brain, a subject that I find fascinating. Two things really hit me when I was reading this book. The first one is the way the book phrases the fact that our brain can create reality and that we live in a made up world. The second one, which it's the first time I read about it although it seems it is known in this circle for some time now, is that we do not have a triune brain in which we can blame our primitive behavior when e.g get angry, but we simple have a differently evolved brain from other species and our irational thoughts can simple be attributed to body budgeting reasons. A very interesting idea that if accepted world wide, it will affect society at it's core, especially in law cases. If you are interested about the human brain, then definitely read this</li>
<li class=""><strong>[4/5] Phoenix in Action</strong>: A nice and easy read about the Phoenix framework which gives you a good overview with detailed examples of what is possible with it. I feel that the Elixir sections could have been more compact in order to make you read an Elixir-specific book that covers the subject better and maybe write more about of Phoenix, but I definitely didn't mind. This book is a good entry point for learning about the framework but things are moving within the Phoenix community and some code/techniques might not apply at the time you read it, so I strongly recommend that you also have a look at the very well written, <a href="https://hexdocs.pm/phoenix/Phoenix.html" target="_blank" rel="noopener noreferrer" class="">official Phoenix documentation</a></li>
<li class=""><strong>[4/5] Programming Ecto</strong>: A well written and helpful resource about Ecto. Altough <em>Phoenix in Action</em> had a lot of information about it, I felt that I had some gaps which this book definitely filled. Lot's of practical examples. What I also liked about the book is that it specifically covered real scenarios which in my opinion are pretty common. I strongly recommend reading it after <em>Phoenix in Action</em></li>
<li class=""><strong>[4/5] RxJS in Action</strong>: Reactive programming is a very interesting and powerful programming technique, especially in UI where a lot of things are happening asynchronously at the same time,  and this book will definitely help you get into it by using RxJS. Lot's of real world examples and practical ideas. Some sections were a little bit more dense and more tiring than others but definitely worth the trouble reading them. I feel that some examples which showed a bad way to achieve something were missing their counterpart, the good way to actually do it and not just explaining it in theory. I definitely feel though that I now know much more about RxJS than before reading it</li>
<li class=""><strong>[3/5] The Art of Readable Code</strong>: I have mixed feelings about this book since I am not sure to whom is addressed. Lot's of legit advice and ideas, but an experienced developer should already know all of this. A junior developer however, will not, but some of the advice in here might not be understood until the developer actually experiences it in a real code base. I definitely recommend that any junior developer should read this, and perhaps a more senior should go through it and refresh what they already know. I mostly agree with everything described in here but I would really love to if the author showed some functional programming as well</li>
<li class=""><strong>[3/5] CTO Excellence in 100 Days</strong>: I am not even sure if the book is worth the 3/5 I gave it. It's probably somewhere between 2 and 3. It was very basic and it was mostly about people having lack of confidence into becoming CTOs than actually being CTOs. I believe there are better books out there that can enhance your understanding of a technical role better, especially the role of a CTO. For example, I found <em>The Startup CTO's handbook</em> by Zach Goldberg much much better. I wouldn't recommend this one</li>
<li class=""><strong>[2/5] Nobody Wants to Read Your Shit</strong>: A very mediocre book with just a catchy title. I didn't even manage to finish it. Stopped at around 2/3 of the book. Even if there was some solid advice here and there, it was VERY basic and kinda boring. Don't waste your time</li>
<li class=""><strong>[1/5] Rich Dad, Poor Dad</strong>: This book is just, sad. Among other things, the author basically says to pray on the weak in order to create wealth. Even if I completely put my feelings
aside and judge the book more objectively, it only contains repetetive information without any actionable advice. Just a generic "learn what is an asset and what is a liability" mantra that is thrown around in most of the pages. I really do not understand why this book is a best seller. Definitely learn about investing and finance, just not from this. He did manage to do something right though; to properly market the book and take our money</li>
</ul>]]></content:encoded>
            <category>books</category>
        </item>
        <item>
            <title><![CDATA[My 2022 reads]]></title>
            <link>https://www.alexanderlolis.com/my-2022-reads</link>
            <guid>https://www.alexanderlolis.com/my-2022-reads</guid>
            <pubDate>Sun, 25 Dec 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Four Thousand Weeks: Time Management for Mortals]]></description>
            <content:encoded><![CDATA[<p><a href="https://www.goodreads.com/book/show/54785515-four-thousand-weeks" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="Four Thousand Weeks: Time Management for Mortals" src="https://www.alexanderlolis.com/assets/images/four_thousand_weeks_oliver_burkeman-db6afd66f29d4043357c7c45d3f76609.jpg" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/27491.The_Evolution_Of_Desire" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="The Evolution Of Desire: Strategies of Human Mating" src="https://www.alexanderlolis.com/assets/images/the_evolution_of_desire_david_buss-dbc2a3fb842ee3f8407c0b93aaee5a9a.jpg" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/53601155-laws-of-ux" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="Laws of UX: Using Psychology to Design Better Products &amp;amp; Services" src="https://www.alexanderlolis.com/assets/images/laws_of_ux_jon_yablonski-7b635bad05b9b8718de61c4f6ad63cfb.jpg" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/1924.Staying_Alive_in_Avalanche_Terrain" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="Staying Alive in Avalanche Terrain" src="https://www.alexanderlolis.com/assets/images/staying_alive_in_avalanche_terrain_bruce_temper-cb17afd5acf23d917c92ad55009333d2.jpg" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/40121378-atomic-habits" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="Atomic Habits: An Easy &amp;amp; Proven Way to Build Good Habits &amp;amp; Break Bad Ones" src="https://www.alexanderlolis.com/assets/images/atomic_habits_james_clear-d6ac2f3a7d63208051425f3cc1642efa.jpg" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/30753738-the-choice" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="The Choice: Embrace the Possible" src="data:image/jpeg;base64,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" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/39979237-moneyland" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="Moneyland: Why Thieves and Crooks Now Rule the World and How To Take It Back" src="https://www.alexanderlolis.com/assets/images/moneyland_oliver_bullough-856cf4a67d1e961f91538009e1883e92.jpg" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/52911233-strong-advice" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="Strong Advice: Zuby&amp;#39;s Guide to Fitness for Everybody" src="https://www.alexanderlolis.com/assets/images/strong_advice_zuby_udezue-57c0282d72cf7b6428a3afb3a6f6d2c8.jpg" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/58680286-practical-doomsday" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="Practical Doomsday: A Sensible Field Guide to Surviving Disasters" src="https://www.alexanderlolis.com/assets/images/practical_doomsday_michal_zalewski-ad963dca682194af2265fd649db393f9.jpg" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/59616977-building-a-second-brain" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="Building a Second Brain: A Proven Method to Organize Your Digital Life and Unlock Your Creative Potential" src="data:image/jpeg;base64,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" width="167" height="250" class="img_ev3q"></a></p>]]></content:encoded>
            <category>books</category>
        </item>
        <item>
            <title><![CDATA[Authorization in a microservices world]]></title>
            <link>https://www.alexanderlolis.com/authorization-in-a-microservices-world</link>
            <guid>https://www.alexanderlolis.com/authorization-in-a-microservices-world</guid>
            <pubDate>Sun, 20 Mar 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Authorization? How hard can it be? I am pretty sure that others have already solved it. We are not the first ones doing microservices. It should be easy to integrate what's already out there.]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>Authorization? How hard can it be? I am pretty sure that others have already solved it. We are not the first ones doing microservices. It should be easy to integrate what's already out there.</p>
<p>- Everybody when they started designing their microservices, before they cried</p>
</blockquote>
<p><strong>Fine-grained authorization in microservices is hard.</strong> Definitely not impossible, but hard. You would expect that a more standardized, all-around, full-proof solution is out there, but I am afraid there isn't. It's a complex matter and depending on what you are building, implementation varies.</p>
<p>You will probably start with a boolean <code>admin</code> flag in your <code>User</code> model and then you will replace it with a <code>role</code> field, as we all did. However, as things progress and the business model becomes more and more complex, so do the solutions that we need to implement in order to deal with that complexity.</p>
<p>But how do you actually go <strong>from a simple flag</strong> to <strong>Role Based Access Control (RBAC)</strong> and then to <strong>Attribute Based Access Control (ABAC)</strong>, especially in a microservices environment? In the following post I hope to help you get there.</p>
<p><em>(UPDATE: 02/04/2022): This article made it to the HackerNews frontpage and some interesting comments can be found <a href="https://news.ycombinator.com/item?id=30878926" target="_blank" rel="noopener noreferrer" class="">here</a>. Feel free to participate!</em></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-monolith">The monolith<a href="https://www.alexanderlolis.com/authorization-in-a-microservices-world#the-monolith" class="hash-link" aria-label="Direct link to The monolith" title="Direct link to The monolith" translate="no">​</a></h2>
<p>The first time I had to deal with a more complex authorization system was in a <strong>monolithic CMS application</strong> written in <code>PHP 5.x</code>, on top of <strong>Zend Framework</strong> (now known as Laminas Project), with a <strong>MySQL</strong> backend, a billion years ago. The app was following the hot and trendy <strong>Model-View-Controller (MVC)</strong> pattern and the requirement in the specific project was, except hierarchical role-based access control (H-RBAC), to have more fine-grained permissions as well as to be able to answer two specific questions:</p>
<ol>
<li class=""><strong>Can the user do X on a Y resource if their role allows it or if the resource is owned by them?</strong></li>
<li class=""><strong>Can the user A, with role X, access the data of user B, that has the same or a higher level role?</strong></li>
</ol>
<p>(For the sake of simplicity and because it does not really offer any extra value, I will skip the implementation details of question 2 and I will just use question 1 for my example below).</p>
<p>After thinking about the situation, and the fact that we had a VERY specific problem to solve, that I KNEW it wouldn't change in the future, and ALL the data models were associated with a single user, this is what a very simplified version looked like (forgive my PHP, it's a bit rusty):</p>
<div class="language-php codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-php codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name-definition class-name">ItemsController</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">extends</span><span class="token plain"> </span><span class="token class-name">Zend_Controller_Action</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ...omitted code</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function-definition function" style="color:#d73a49">listAction</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token variable" style="color:#36acaa">$page</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token variable" style="color:#36acaa">$this</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">_getParam</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string single-quoted-string" style="color:#e3116c">'page'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token variable" style="color:#36acaa">$orderBy</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token variable" style="color:#36acaa">$this</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">_getParam</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string single-quoted-string" style="color:#e3116c">'order_by'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string single-quoted-string" style="color:#e3116c">'created_at'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token variable" style="color:#36acaa">$order</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token variable" style="color:#36acaa">$this</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">_getParam</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string single-quoted-string" style="color:#e3116c">'order'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string single-quoted-string" style="color:#e3116c">'desc'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token variable" style="color:#36acaa">$filters</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">array</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string single-quoted-string" style="color:#e3116c">'drafts'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token constant boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token variable" style="color:#36acaa">$user</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token class-name static-context">Session</span><span class="token operator" style="color:#393A34">::</span><span class="token function" style="color:#d73a49">getUser</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token variable" style="color:#36acaa">$user</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">isAuthorized</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string single-quoted-string" style="color:#e3116c">'can_list_drafts'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string single-quoted-string" style="color:#e3116c">'items'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token variable" style="color:#36acaa">$filters</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string single-quoted-string" style="color:#e3116c">'drafts'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token variable" style="color:#36acaa">$this</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">_getParam</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string single-quoted-string" style="color:#e3116c">'drafts'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token constant boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token variable" style="color:#36acaa">$result</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token class-name static-context">ItemModel</span><span class="token operator" style="color:#393A34">::</span><span class="token function" style="color:#d73a49">find</span><span class="token punctuation" style="color:#393A34">(</span><span class="token variable" style="color:#36acaa">$filters</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token variable" style="color:#36acaa">$page</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token variable" style="color:#36acaa">$orderBy</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token variable" style="color:#36acaa">$order</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token variable" style="color:#36acaa">$result</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function-definition function" style="color:#d73a49">destroyAction</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token variable" style="color:#36acaa">$itemId</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token variable" style="color:#36acaa">$this</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token property" style="color:#36acaa">_request</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">getParam</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string single-quoted-string" style="color:#e3116c">'itemId'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token variable" style="color:#36acaa">$item</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token class-name static-context">ItemModel</span><span class="token operator" style="color:#393A34">::</span><span class="token function" style="color:#d73a49">findById</span><span class="token punctuation" style="color:#393A34">(</span><span class="token variable" style="color:#36acaa">$itemId</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token variable" style="color:#36acaa">$item</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">throw</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">ItemNotFoundException</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token variable" style="color:#36acaa">$user</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token class-name static-context">Session</span><span class="token operator" style="color:#393A34">::</span><span class="token function" style="color:#d73a49">getUser</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token variable" style="color:#36acaa">$user</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">hasRoleOrSelfPermissionOn</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string single-quoted-string" style="color:#e3116c">'can_delete_items'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string single-quoted-string" style="color:#e3116c">'can_delete_own_items'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string single-quoted-string" style="color:#e3116c">'items'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token variable" style="color:#36acaa">$item</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">throw</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">AuthorizationException</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token variable" style="color:#36acaa">$result</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token variable" style="color:#36acaa">$item</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">destroy</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token variable" style="color:#36acaa">$result</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ... rest of code</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<div class="language-php codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-php codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name-definition class-name">User</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ...omitted code</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">isAuthorized</span><span class="token punctuation" style="color:#393A34">(</span><span class="token variable" style="color:#36acaa">$permission</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token variable" style="color:#36acaa">$resource</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// The ACL singleton class is a wrapper around Zend ACL which is a module that provides you</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// with hierarchical, role based, access control lists which you can query and check</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// if a role has the requested permission on a resource.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token class-name static-context">ACL</span><span class="token operator" style="color:#393A34">::</span><span class="token function" style="color:#d73a49">getInstance</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">isAuthorized</span><span class="token punctuation" style="color:#393A34">(</span><span class="token variable" style="color:#36acaa">$this</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token property" style="color:#36acaa">role</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token variable" style="color:#36acaa">$permission</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token variable" style="color:#36acaa">$resource</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token constant boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">      </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token constant boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">public</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">hasRoleOrSelfPermissionOn</span><span class="token punctuation" style="color:#393A34">(</span><span class="token variable" style="color:#36acaa">$rolePermission</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token variable" style="color:#36acaa">$selfRolePermission</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token variable" style="color:#36acaa">$resource</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token variable" style="color:#36acaa">$model</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token variable" style="color:#36acaa">$this</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">isAuthorized</span><span class="token punctuation" style="color:#393A34">(</span><span class="token variable" style="color:#36acaa">$rolePermission</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token variable" style="color:#36acaa">$resource</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token constant boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">elseif</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token variable" style="color:#36acaa">$model</span><span class="token operator" style="color:#393A34">.</span><span class="token plain">userId </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> </span><span class="token variable" style="color:#36acaa">$this</span><span class="token operator" style="color:#393A34">.</span><span class="token plain">id</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token variable" style="color:#36acaa">$this</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token function" style="color:#d73a49">isAuthorized</span><span class="token punctuation" style="color:#393A34">(</span><span class="token variable" style="color:#36acaa">$selfRolePermission</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token variable" style="color:#36acaa">$resource</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token constant boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token constant boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ...rest of code</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>A few things are going on above:</p>
<ol>
<li class=""><strong>The decision point and enforcement of authorization is happening in the controller, getting mixed with application code that does not care about all these things</strong>, plus, it makes it harder to test. We do not want that. We want our controllers to be lean (in terms of responsibilities) and just return appropriate responses which are related to what was requested in the first place.</li>
<li class="">In <code>listAction</code> we need to check individual filters in order to decide if the user is authorized to use them or not. If multiple filters exist that need to be checked, the controller will become too messy, too fast.</li>
<li class="">The application data that the authorization requires (in this case <code>$item</code>) are locally available and easily loaded with our <code>ItemModel</code> class. As you will understand later, loading necessary application data is the biggest pain point.</li>
<li class="">The <code>User</code> class is suddenly coupled with authorization. It shouldn't. It should only care about the user entity itself, like the class name implies.</li>
<li class=""><code>hasRoleOrSelfPermissionOn</code> makes the assumption that all models have a <code>userId</code> field which means that we should know beforehand that this will be true (forever) or else the function will not work as expected.</li>
</ol>
<p><strong>The authorization code is tedious, poluting the controllers, and error prone</strong>. However, we were also lucky because this was the ugliest it could get (based on project requirements) and therefore it was acceptable (or at least we persuaded ourselves that it was), instead of pushing to a more complex solution. If, for example, there was the requirement of checking multiple model attributes (or attributes from different models), depending on the controller action, then <code>hasRoleOrSelfPermissionOn</code> wouldn't be able to cover everything and the controllers would immediately become much messier. It did serve us well back then but I no longer like it because avoiding tangling code at some point became a priority.</p>
<p><strong>What I am describing above is not inherent to monoliths.</strong> The same implementation could have been done in a microservice (and I have seen it in multiple occasions, with everything done in the controllers) if the application data that the authorization mechanism required, lived under the same roof. But more on that later.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="so-what-could-we-have-done-to-improve-the-above-situation">So, what could we have done to improve the above situation?<a href="https://www.alexanderlolis.com/authorization-in-a-microservices-world#so-what-could-we-have-done-to-improve-the-above-situation" class="hash-link" aria-label="Direct link to So, what could we have done to improve the above situation?" title="Direct link to So, what could we have done to improve the above situation?" translate="no">​</a></h4>
<p>Push the authorization code higher up in the stack. That would mean that you could push it to somewhere like a <code>middleware</code>, as most web frameworks call it, which is basically code that is executed before your controllers. And this, is where the fun begins and a whole new set of implementation problems rises!</p>
<p>But before we dive into that, we need to think about our authorization flow in a more abstract level.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="authorization-flow-overview">Authorization flow overview<a href="https://www.alexanderlolis.com/authorization-in-a-microservices-world#authorization-flow-overview" class="hash-link" aria-label="Direct link to Authorization flow overview" title="Direct link to Authorization flow overview" translate="no">​</a></h2>
<p>So, what would this authorization mechanism look like in order to not force a single architecture but be more flexible instead? The following diagram will give you an idea:</p>
<div style="text-align:center"><p><img decoding="async" loading="lazy" alt="Authorization Overview" src="https://www.alexanderlolis.com/assets/images/authorization_architecture_overview-a7f2e4f08870034de08c831e81aae323.jpg" width="872" height="672" class="img_ev3q"></p></div>
<p><strong>The flow goes like this:</strong></p>
<ol>
<li class="">User requests to view record A.</li>
<li class="">The request is intercepted by the <strong>Policy Enforcement Point (PEP)</strong>. This is usually a <strong>middleware</strong>, or generally speaking, a layer in your stack, as high as possible, in order for the request to stop right there in case it's not authorized, instead of allowing it to travel deeper in the system.</li>
<li class=""><strong>PEP</strong> makes a request to the <strong>Policy Decision Point (PDP)</strong> in order to figure out whether or not the request is authorized to move forward. PDP will probably be a library that keeps track of roles, permissions and resources and expose an interface in order to query it and get a boolean answer whether or not someone can do something on a resource.</li>
<li class=""><strong>PDP</strong> <em>might</em> need extra information in order to decide if the request should be allowed or denied so it needs to ask the <strong>Policy Information Point (PIP)</strong> for that extra information. That extra information can be retrieved from a database, from a flat file, from an external service or from any other source you need. PIP could be just another library which PDP can use internally if it needs to.</li>
<li class=""><strong>PIP</strong> loads the extra information and returns it to the PDP.</li>
<li class=""><strong>PDP</strong>, in combination with the extra information it got from PIP, evaluates the defined policy (which can be stored anywhere you like, e.g. database or flat file), and decides whether or not the request has access to the underlying resource based on what the policy says.</li>
<li class="">The answer <strong>is returned to PEP</strong>, which either allows or denies the request to move forward.</li>
</ol>
<p>On the diagram above there is another piece, <strong>the Policy Administration Point (PAP)</strong>. This is basically an optional interface that helps you manage the policies. It can be anything, a web interface, a command line tool, a desktop GUI or you can just skip it entirely if you do not want to provide it and do everything manually instead.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="architectures">Architectures<a href="https://www.alexanderlolis.com/authorization-in-a-microservices-world#architectures" class="hash-link" aria-label="Direct link to Architectures" title="Direct link to Architectures" translate="no">​</a></h2>
<p>Now that we have defined our authorization flow, let's go through a few real world architectures together. All the following architectures include a diagram and within this diagram I make clear what plays which role based on what I showed you earlier.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="with-an-authorization-service">With an authorization service<a href="https://www.alexanderlolis.com/authorization-in-a-microservices-world#with-an-authorization-service" class="hash-link" aria-label="Direct link to With an authorization service" title="Direct link to With an authorization service" translate="no">​</a></h3>
<p>So, you are there thinking, I am doing microservices, right? So the logical thing to do is to implement an <strong>authorization service</strong> and everybody would be able to use that and keep your precious service boundaries, right? <strong>WRONG</strong>. Your hell, has just begun!</p>
<div style="text-align:center"><p><img decoding="async" loading="lazy" alt="Overview" src="https://www.alexanderlolis.com/assets/images/authorization_architecture_authorization_service-131cac666b0b1feac9d7f636e04929bf.jpg" width="582" height="453" class="img_ev3q"></p></div>
<p><strong>How will PIP fetch the application data that PDP needs?</strong></p>
<ol>
<li class=""><strong>Direct DB reads:</strong> I am pretty sure someone will suggest this, and then will say, <em>"I know we shouldn't be doing this in microservices but let's just make this exception"</em>, but that's how all big problems are created. With a small exception here, with another one there, and then things start to blow up, or even worst, become unmaintenable. Direct DB reads, outside the domain of each service, is a bad idea and it will be obvious to you why on the first schema change. Please don't be lazy and just say no to this.</li>
<li class=""><strong>Attach all the extra data to the authorization service call:.</strong> By doing this you will probably increase the network traffic and you might cause a bottleneck on the service if every request has a lot of data attached. However, these - manageable - disadvantages are not even my main concern with this approach. My main concern is the fact that the caller now needs to know the authorization logic that will be used internally by the authorization service in order to send the appropriate data. This will probably lead to callers sending more data than they should, or not removing data from the call whenever a policy changes, "just to be sure", and as a result the code maintenance will become harder. Even in a perfect world, were the data are ALWAYS exactly what is needed, the extra work of choosing the data is a burden that the caller should not have.</li>
<li class=""><strong>Keep a copy of the data the authorization service needs:</strong> This basically means syncing the data from all the other services, to the authorization service database, with a generic model that will be able to fit (in terms of a schema) everything. I can tell you right now that, this is difficult to achieve, especially if the syncing is experiencing network delays and/or conflicts. There are others who are doing this (check <a href="https://research.google/pubs/pub48190/" target="_blank" rel="noopener noreferrer" class="">Google Zanzibar</a>), but for most mortal companies I think dealing with the complexities of syncing on your first implementation, is an overkill. Besides, even Zanzibar is not a silver bullet and it's higly opinionated with its models, so you might need to build extra parts that communicate with it in order to achieve what you want.</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="with-an-authorization-service-and-a-data-service">With an authorization service and a data service<a href="https://www.alexanderlolis.com/authorization-in-a-microservices-world#with-an-authorization-service-and-a-data-service" class="hash-link" aria-label="Direct link to With an authorization service and a data service" title="Direct link to With an authorization service and a data service" translate="no">​</a></h3>
<div style="text-align:center"><p><img decoding="async" loading="lazy" alt="Overview" src="https://www.alexanderlolis.com/assets/images/authorization_architecture_authorization_and_data_service-69c2c84ecf53d7527e708c695c554f55.jpg" width="592" height="540" class="img_ev3q"></p></div>
<p>This is probably the most complex architecture but it definitely clears things up a bit. If the data state and the mutations are all handled by the same layer (a data service), and all other services use that layer, it's like they have direct access to the database and the PIP can now fetch whatever it needs.</p>
<p>Personally, I like this architecture because now there is a very clear seperation of who is doing what, even if it means that more code will be required in order to set it up. Building a data service is beyond the scope of this post so I will just keep it simple and say that you shouldn't start with this, unless you already have a data service implemented or if you have other needs for it that go beyond authorization.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="variations">Variations<a href="https://www.alexanderlolis.com/authorization-in-a-microservices-world#variations" class="hash-link" aria-label="Direct link to Variations" title="Direct link to Variations" translate="no">​</a></h4>
<p>There are a couple of variations with this architecture that I would like to mention since someone might find them interesting.</p>
<p><strong>Instead of doing the authorization within each internal service, you could do the authorization at the API Gateway.</strong> You can use an authorization middleware within the API Gateway in order to communicate with the authorization service, or, you can even get rid of it completely and communicate with the data service directly from the middleware.</p>
<div style="text-align:center"><p><img decoding="async" loading="lazy" alt="Overview" src="https://www.alexanderlolis.com/assets/images/authorization_architecture_api_gateway_data_service-0b3ce09676ba0e94debaec46c09cc681.jpg" width="592" height="493" class="img_ev3q"></p></div>
<p>This has the advantage of stopping the request as early as possible before it has the chance to reach the internal services. In general, I like that idea.</p>
<p>However, it also has a couple of disadvantages:</p>
<ol>
<li class="">When services make internal calls, they will be bypassing the authorization checks. It might be acceptable in your case but personally I do not like it. I want each service to allow only what is expected and nothing more. You could enforce in your implementation to make all the calls go through the API Gateway but then you increase the network latency and you lose all the advantages of actually having an internal network.</li>
<li class="">API Gateway will need to have all the application data loaders (for PIP) implemented there and things might get messy if you are implementing too much. Especially if you have a lot of services you might be changing API Gateway too often for reasons unrelated with the service itself.</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="with-an-authorization-middleware-and-library-per-service">With an authorization middleware and library per service<a href="https://www.alexanderlolis.com/authorization-in-a-microservices-world#with-an-authorization-middleware-and-library-per-service" class="hash-link" aria-label="Direct link to With an authorization middleware and library per service" title="Direct link to With an authorization middleware and library per service" translate="no">​</a></h3>
<div style="text-align:center"><p><img decoding="async" loading="lazy" alt="Overview" src="https://www.alexanderlolis.com/assets/images/authorization_architecture_service_library-7003f6c7223ce5634575390d72c385e6.jpg" width="702" height="475" class="img_ev3q"></p></div>
<p>In my opinion <strong>this is probably a good balance between complexity and a scalable solution</strong>. You basically keep the authorization logic in each service and each service is responsible for the authorization rules of its own domain.</p>
<p>One thing you will probably need is to have the common code implemented as a library (or libraries) since it will be shared among all services but it shouldn't be a problem. Just make sure you cover it with enough tests!</p>
<p>The real question with this approach is; <strong>how homogeneous is your system</strong>? If it's 100% then you are in luck. You will only need to implement (or find) everything you need once. if it's not, you will need to do it for every different language you are using in your system. Even though there are plenty of libraries out there, depending on your system details, they might not integrate very well or they might be missing a couple of features that you might need. Before you are too eager to implement your own, make sure if an existing solution covers your needs. If it does not, maybe you can get away with it by doing a few changes on your side, or by adding those missing features to the library you found. Whatever you do, take the time to decide carefully.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="implementation">Implementation<a href="https://www.alexanderlolis.com/authorization-in-a-microservices-world#implementation" class="hash-link" aria-label="Direct link to Implementation" title="Direct link to Implementation" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="rbac">RBAC<a href="https://www.alexanderlolis.com/authorization-in-a-microservices-world#rbac" class="hash-link" aria-label="Direct link to RBAC" title="Direct link to RBAC" translate="no">​</a></h3>
<p>If you are doing RBAC, then implementing this is straight forward and clean. You just need a middleware (PEP) in which you must check if the user role (usually attached to a JWT that comes with the request) has access to the specified route path. The policies can easily be stored in a database or a configuration file, load them upon service initialization and let the RBAC library (PDP) that you will use do its work from within your middleware. In this case, no PIP is required.</p>
<p>There are a lot of good solutions out there for RBAC, for any modern web framework from any language, so I will not go into details about this, but I just wanted to briefly mention it. <strong>The only tip I do wanna share though, in case you are implementing something custom</strong>, is that the <strong>resource</strong> in your policies will probably be the <strong>regex route path</strong> and the <strong>permission</strong> will be the <strong>HTTP verb</strong> (<code>GET</code>, <code>DELETE</code>, etc). By doing that, you can easily get the route path from within the middleware and use a regex route matcher library to check if the resource is a match.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="rbacabac">RBAC/ABAC<a href="https://www.alexanderlolis.com/authorization-in-a-microservices-world#rbacabac" class="hash-link" aria-label="Direct link to RBAC/ABAC" title="Direct link to RBAC/ABAC" translate="no">​</a></h3>
<p>So let's assume that you go with option 3, with an authorization middleware and library per service. How would such implementation look like?</p>
<p>The following implementation approach is neither the best, nor the only one. It is however applicable to the real world and that's what is important for me. <strong>What we are basically trying to answer with our implementation is:</strong></p>
<ol>
<li class=""><strong>How will my authorization mechanism load the necessary application data it needs?</strong></li>
<li class=""><strong>How will I express my policies, in a non confusing way, and be able to access dynamically loaded data from within those policies?</strong></li>
</ol>
<p>By answering the above questions we will also be able to provide a better answer back to our friend <em>the monolith</em> and its question, <em>how will we allow a user to do X on Y resource if their roles allows it, or if it's owned by them?</em></p>
<p>The example is written in <code>Node.js</code>, but I am confident you can do something similar in any web framework of your choice. Also, please do keep in mind that I have omitted a lot of code and kept a flat structure in order to try to reduce any confusion with unecessary implementation details. I understand that it can be frustrating but I will try to explain in detail what everything is supposed to do.</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports">express</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'express'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// express is a web framework for Node.js </span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports">logger</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'/your/logger'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token maybe-class-name">AccessControl</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token constant" style="color:#36acaa">ROLE</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token constant" style="color:#36acaa">PREDICATE</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'/your/access/control/lib'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// `express.js` router handler for listing items and assigned to route `GET /items`. </span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// The assignment is done in our `routesObject` further below.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">listItemsHandler</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">res</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> req</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> next</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> items </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> </span><span class="token maybe-class-name">ItemModel</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">find</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">req</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">query</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ... rest of handler; no authorization checks required </span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// `express.js` router handler for getting a specific item and assigned to route `GET /items/:id`. </span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// The assignment is done in our `routesObject` further below.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">getItemHandler</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">res</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> req</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> next</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">id</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> req</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">params</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> item </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> </span><span class="token maybe-class-name">ItemModel</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">findById</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">id</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token plain">item</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">throw</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">ItemNotFoundError</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ... rest of handler; no authorization checks required</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">/*</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">  This is a function that represents an `express.js` middleware. It's basically code that will run BEFORE </span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">  the `listItemsHandler` and the `getItemHandler` implemented above. The middleware acts as the PEP </span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">  and  internally uses our access control library in order to decide if it should allow a request or not.</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">  Check here for extra info on middlewares: https://expressjs.com/en/guide/using-middleware.html</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">*/</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">authorizationExpressMiddleware</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">request</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> response</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> </span><span class="token parameter function" style="color:#d73a49">next</span><span class="token parameter punctuation" style="color:#393A34">(</span><span class="token parameter punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// Get the user object from the JWT token that is attached to the request.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// Usually this happens within a middleware higher up in the middleware chain.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> user </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> request</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">locals</span><span class="token operator" style="color:#393A34">?.</span><span class="token plain">user</span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> </span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> userRole </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> request</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">locals</span><span class="token operator" style="color:#393A34">?.</span><span class="token plain">user</span><span class="token operator" style="color:#393A34">?.</span><span class="token plain">role</span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">ROLE</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">GUEST</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// This is just an object with data that we want to pass to our access control library that is related</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// with this request. Everything in this object will be accessible by specifying the appropriate </span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// object path in the `conditions` key of our policy definitions as we will see later.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> context </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    user</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    request</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> action </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> request</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">method</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// GET, DELETE, PUT, etc</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> resource </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> path</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">join</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">request</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">baseUrl</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> request</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">route</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">path</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// e.g. /items/:id</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// Check if the current request is authorized, by also including our `context` object. </span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> isAuthorized </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> accessControl</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">withContext</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">context</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">can</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">userRole</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> action</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> resource</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// If the request is authorized, then pass the control to the next middleware in the chain.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">isAuthorized</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">next</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// Request is UNAUTHORIZED. Set the HTTP response status and stop the rest of the middleware </span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// chain from executing by returning an error. The request will stop here.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  response</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">status</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">401</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// Unauthorized</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">next</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">AuthorizationError</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">/*</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">  Initialization function for our access control library. This is an imaginary custom PDP library</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">  that internally uses an imaginary custom PIP library. Using the PIP part of the access control </span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">  library should be optional.</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">*/</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">initAccessControl</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">endpointsObject</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// PIP loader function in order to load a specific item and provide to</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// the access control library the extra information it needs.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">pipItemLoader</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">itemId</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> item </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> </span><span class="token maybe-class-name">ItemModel</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">findById</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">itemId</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">item</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> item</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// Initialize the access control library with two roles. The library should be able </span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// to support hierarchical roles in order to inherit permissions from a parent role </span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// instead of repeating them, just like below.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> roles </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">ROLE</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">USER</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">ROLE</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">ADMIN</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">inherits</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token constant" style="color:#36acaa">ROLE</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">USER</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> accessControl </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">AccessControl</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">roles</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// Add our loader to the access control library in order to be able to use it internally when necessary.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  accessControl</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">addLoader</span><span class="token punctuation" style="color:#393A34">(</span><span class="token constant" style="color:#36acaa">PIP_LOADER_TYPE</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">ITEM</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> pipItemLoader</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// `buildPermissions` is a custom utility function that parses the `accessControl` key object value from </span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// our endpoints object and creates a list of permission objects, formatted the way our access</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// control library expects them.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> permissions </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">buildPermissions</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">endpointsObject</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  accessControl</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">addPermissions</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">permissions</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> accessControl</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// The following object contains everything we need to describe all the exposed routes. Within the object,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// we have also embedded our authorization policies via the `accessControl` key. Within this key, we specify</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// which role can do what and under which conditions.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> routesObject </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">middlewares</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">authorizationExpressMiddleware</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">endpoints</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     </span><span class="token string-property property" style="color:#36acaa">'/items'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token string-property property" style="color:#36acaa">'/'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token constant" style="color:#36acaa">GET</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token literal-property property" style="color:#36acaa">handler</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> listItemsHandler</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token literal-property property" style="color:#36acaa">accessControl</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token literal-property property" style="color:#36acaa">permissions</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token literal-property property" style="color:#36acaa">role</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">ROLE</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">USER</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token comment" style="color:#999988;font-style:italic">// The 'user' role can only list items which the `user_id` filter matches the user who request them.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token literal-property property" style="color:#36acaa">condition</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token literal-property property" style="color:#36acaa">predicate</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">PREDICATE</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">EQUAL</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">args</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'$.request.query.user_id'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'$.user.id'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token literal-property property" style="color:#36acaa">role</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">ROLE</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">ADMIN</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token comment" style="color:#999988;font-style:italic">// The admin role can list items with any filters they like.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token literal-property property" style="color:#36acaa">condition</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// Any filter is allowed</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string-property property" style="color:#36acaa">'/:id'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token constant" style="color:#36acaa">GET</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token literal-property property" style="color:#36acaa">handler</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> getItemHandler</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token literal-property property" style="color:#36acaa">accessControl</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token literal-property property" style="color:#36acaa">loader</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token literal-property property" style="color:#36acaa">fn</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">PIP_LOADER_TYPE</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">ITEM</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">args</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'$.request.params.id'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token literal-property property" style="color:#36acaa">permissions</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token literal-property property" style="color:#36acaa">role</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">ROLE</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">USER</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token comment" style="color:#999988;font-style:italic">// The 'user' role can only get an item that is owned by the user who requested it.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token literal-property property" style="color:#36acaa">condition</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token literal-property property" style="color:#36acaa">predicate</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">PREDICATE</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">EQUAL</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">args</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'$.resource.userId'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'$.user.id'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Initialize access control and our app.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> accessControl </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">initAccessControl</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">routesObject</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">endpoints</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> app </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">express</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// `buildRoutes` is a custom utility function that parses the routes object and adds all the specified routes </span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// to our express.js router by also associated them with the respective handler. The function will also apply</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// the `middlewares` array from the object to all routes which means that each request will need to go</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// through our authorization middleware.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">//</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Check the express.js router here: https://expressjs.com/en/guide/routing.html</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">buildRoutes</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">app</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> routesObject</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">app</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">listen</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">3000</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  logger</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">info</span><span class="token punctuation" style="color:#393A34">(</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">kitten app listening at http://localhost:3000</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre></div></div>
<p>I tried to keep the example code as succinct as possible in order for everyone to understand, even if they are not familiar with <code>Node.js</code>. The important take from the code above is that you <em>could</em> use an object to describe your policies, along with your routes, and pass values to those policies by expressing paths from dynamically loaded data that can be used during the evaluation step within our access control library.</p>
<p>You have probably noticed that in the <code>condition</code> key some weird stuff are happening in the <code>args</code> that are prefixed with <code>$</code>. What is happening there is that I am using <a href="https://restfulapi.net/json-jsonpath/" target="_blank" rel="noopener noreferrer" class="">JSON path syntax</a> in order to specify the location of the data (within the <code>context</code> object) that will be passed as arguments to the predicate; In this case, <code>PREDICATE.EQUAL</code>, which as the name implies, is a predicate for equality check.</p>
<p>If you re-read the code, you will see that the <code>authorizationMiddleware</code> passes a <code>context</code> object to the access control library that includes the <code>request</code> and <code>user</code> objects. That's how they are available for access with the JSON path syntax. As for the <code>$.resource</code> in the <code>args</code>, it's just a key name I choose to attach the data returned from the <code>pipItemLoader</code> and automatically attached to the <code>context</code> (this happens inside our imaginary custom PIP library). That means, that the <code>resource</code> represents an <code>item</code> object with a field named <code>userId</code> which we can now access with JSON path syntax.</p>
<p>Like I have already mentioned, the above is one way to do it, and it works well. You could go with something that is more general-purpose and advanced like a policy engine (e.g <a href="https://www.openpolicyagent.org/" target="_blank" rel="noopener noreferrer" class="">OPA</a>) but I assure you that they are definitely not a free meal either, nor without limitations. Once again, it comes down to the details of your own system and what you are trying to achieve.</p>
<p><em>(UPDATE 23/05/2022): <a href="https://github.com/cerbos/cerbos" target="_blank" rel="noopener noreferrer" class="">Cerbos</a> recently came to my attention which follows this "contextual" approach I am describing above. Maybe you can check it out as well!</em></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="potential-problems">Potential problems<a href="https://www.alexanderlolis.com/authorization-in-a-microservices-world#potential-problems" class="hash-link" aria-label="Direct link to Potential problems" title="Direct link to Potential problems" translate="no">​</a></h3>
<p>I would like to briefly mention a few situations that you might come up against as a heads up and what you can try to do in order to solve them.</p>
<h5 class="anchor anchorTargetStickyNavbar_Vzrq" id="bulk-enforcement">Bulk enforcement<a href="https://www.alexanderlolis.com/authorization-in-a-microservices-world#bulk-enforcement" class="hash-link" aria-label="Direct link to Bulk enforcement" title="Direct link to Bulk enforcement" translate="no">​</a></h5>
<p>This becomes apparent as soon as you have some kind of listing endpoint. How will you check if the user is authorized to access all the items in the result? Checking them one by one is probably the first thing that comes to mind, but that means you will have to load them first, plus, it might be slow if you need to keep loading 1000 items on every request even for users that are not allowed to access that data.</p>
<p>In our implementation above this is solved with our routes map and the <code>condition</code> key. When you are listing items, you are probably passing some kind of filters to the endpoint via query parameters, which means you can easily use those filters and decide what to do before you even load the data. For roles that are allowed to pass whatever filters they want (e.g. an admin role), you can just omit the <code>condition</code> key.</p>
<p>The important thing here is that the authorization mechanism is not getting coupled with internal details of how you actually implement your data filtering. It just stays on a very shallow layer of your service which is publicly available to use.</p>
<h5 class="anchor anchorTargetStickyNavbar_Vzrq" id="not-found-requests-in-pip-loaders">Not Found requests in PIP loaders<a href="https://www.alexanderlolis.com/authorization-in-a-microservices-world#not-found-requests-in-pip-loaders" class="hash-link" aria-label="Direct link to Not Found requests in PIP loaders" title="Direct link to Not Found requests in PIP loaders" translate="no">​</a></h5>
<p>This is something that I am still working on myself. I have tried various approaches but I am not 100% satisfied with any of them.</p>
<p>Let's asume that you have your <code>pipItemLoader</code> in which you load an item with id X. What should happen if the item does not exist? Should we return an authorization error? Should we return an <code>ItemNotFound</code> error? If we do the first one, isn't it misleading for the API caller? Especially if the user actually has access to load items. If we do the second one, before we check if the user is authorized for this action, wouldn't it be bad from a security perspective to provide such info? Maybe we should return a special value from the loader and if it's detected to let the request go through, and HOPEFULLY, our controller will be doing checks if the item exists or not.</p>
<p>You can try any of the above and something might fit your needs, or you can be creative and let me know if you find something interesting as well!</p>
<h5 class="anchor anchorTargetStickyNavbar_Vzrq" id="duplicate-database-round-trips">Duplicate database round trips<a href="https://www.alexanderlolis.com/authorization-in-a-microservices-world#duplicate-database-round-trips" class="hash-link" aria-label="Direct link to Duplicate database round trips" title="Direct link to Duplicate database round trips" translate="no">​</a></h5>
<p>As you might have noticed on our implementation above for the <code>GET</code> endpoint, is that we are loading the same item twice; once in the <code>pipItemLoader</code> and once in <code>getItemHandler</code>. Why hit the database twice when you can just avoid it?</p>
<p>One solution is to cache the data in order for the second call to return the cached data. But why hit the cache when you can avoid that as well? How? Just use a data loader and persist the data in RAM (and cache them as a side effect) for as long as the request lives and clear them upon finish with a middleware. For example, for <code>Node.js</code> you could use the <a href="https://www.npmjs.com/package/dataloader" target="_blank" rel="noopener noreferrer" class="">dataloader</a> package. I am pretty sure there are similar solutions in whatever you might be using.</p>
<p>Of course, none of this is necessary and might be too much. Once again, it depends on how everything is implemented on your side and more importantly, your incoming traffic load.</p>
<h5 class="anchor anchorTargetStickyNavbar_Vzrq" id="restricting-resource-fields">Restricting resource fields<a href="https://www.alexanderlolis.com/authorization-in-a-microservices-world#restricting-resource-fields" class="hash-link" aria-label="Direct link to Restricting resource fields" title="Direct link to Restricting resource fields" translate="no">​</a></h5>
<p>Depending on user access level, return resources with specific fields removed. Some authorization libraries that I have seen provide a way to do this by exposing a function/method, but in my opinion this does not really belong there. I feel like this is a job for an authorization middleware, or at least something other than the library itself which should be kept as tight as possible.</p>
<p>If you are using the routes map approach above, then you can easily extend it to support this by adding an extra key that describes the fields that are allowed and let the middleware remove the rest.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://www.alexanderlolis.com/authorization-in-a-microservices-world#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>As you saw, implementing authorization is hard, but it's also a very interesting challenge.</p>
<p>Although there is a popular belief that in a monolith is easier to implement authorization, in my opinion both the monolith and a microservices architecture have a lot in common when it comes to the authorization mechanism when that same mechanism has access to the application data it needs. The main difference is, as long as that mechanism no longer has a straight forward way to get that data, things start to get more complicated. Due to the independent nature of microservices, this can be easily observed, but the same complexity can be also observed within the same organization if multiple monoliths need to have a common authorization system.</p>
<p>I am definitely not done with looking for better solutions than the ones I have already suggested and I am looking forward to hearing what others are doing.</p>
<p>The good thing is, that more and more people are starting to take this more seriously and spend more time on it. Hopefully, at some point we will solve it more elegantly and have a more unified approach. Until then, good luck!</p>
<p><strong>PS:</strong> I know you are going to ask me, so I am telling you right now that I made all the diagrams with <a href="https://drawio-app.com/" target="_blank" rel="noopener noreferrer" class="">draw.io</a>.</p>]]></content:encoded>
            <category>microservices</category>
            <category>authorization</category>
            <category>architecture</category>
            <category>battlefield</category>
            <category>development</category>
        </item>
        <item>
            <title><![CDATA[My 2021 reads]]></title>
            <link>https://www.alexanderlolis.com/my-2021-reads</link>
            <guid>https://www.alexanderlolis.com/my-2021-reads</guid>
            <pubDate>Sat, 25 Dec 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[On A Life Well Spent: Cicero's De Senectute]]></description>
            <content:encoded><![CDATA[<p><a href="https://www.goodreads.com/book/show/51881206-on-a-life-well-spent" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="On A Life Well Spent: Cicero&amp;#39;s De Senectute" src="https://www.alexanderlolis.com/assets/images/on_a_life_well_spent_marcus_tullius_cicero-a6d51647be8017d472dbcfc7f89beb1e.jpg" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/34536488-principles" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="Principles: Life and Work" src="https://www.alexanderlolis.com/assets/images/principles_ray_dalio-c99c1e9759e639d83f90580b52634cde.jpg" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/25666050-algorithms-to-live-by" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="Algorithms to Live By: The Computer Science of Human Decisions" src="https://www.alexanderlolis.com/assets/images/algorithms_to_live_by_brian_christian-f19afd70eabb3b1ea16d3922ebc762c4.jpg" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/21343.The_Five_Dysfunctions_of_a_Team" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="The Five Dysfunctions of a Team: A Leadership Fable" src="https://www.alexanderlolis.com/assets/images/the_five_dysfunctions_of_a_team_patrick_leoncini-3697752fb9497601e55cf3aabcf95e33.jpg" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/801178.Chicken_Soup_for_the_Soul" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="Chicken Soup for the Soul" src="https://www.alexanderlolis.com/assets/images/chicken_soup_for_the_soul_jack_canfield-a9e1761d65b6dfb7f2e80a6a0af312f5.jpg" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/69242.Made_to_Stick" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="Made to Stick: Why Some Ideas Survive and Others Die" src="https://www.alexanderlolis.com/assets/images/made_to_stick_chip_heath-9c8967deb1e043ef1f581b84209e109e.jpg" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/49099937-no-rules-rules" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="No Rules Rules: Netflix and the Culture of Reinvention" src="https://www.alexanderlolis.com/assets/images/no_rules_rules_reed_hastings-74a0ce5425b6793035247a61ccd4a061.jpg" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/58165477-digital-zettelkasten" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="Digital ZettelKasten" src="https://www.alexanderlolis.com/assets/images/digital_zettelkasten_david_kadavy-bed3ac555980f6708a4d57c93931c597.jpg" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/34507927-how-to-take-smart-notes" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="How to take smart notes" src="https://www.alexanderlolis.com/assets/images/how_to_take_smart_notes_sonke_ahrens-ab80679045d7067379536b2155f5c2ca.jpg" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/28388921-beginning-neo4j" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="Beginning Neo4j" src="data:image/jpeg;base64,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" width="167" height="250" class="img_ev3q"></a></p>]]></content:encoded>
            <category>books</category>
        </item>
        <item>
            <title><![CDATA[Node.js fork is slow; Deal with it]]></title>
            <link>https://www.alexanderlolis.com/node-js-fork-is-slow-deal-with-it</link>
            <guid>https://www.alexanderlolis.com/node-js-fork-is-slow-deal-with-it</guid>
            <pubDate>Mon, 27 Sep 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[Dealing with Node.js fork slowness]]></description>
            <content:encoded><![CDATA[<p>Yes. I know. Forking a process in <code>Node.js</code> is slow. Instead of crying about it, let's see how we can handle it!</p>
<p>Let's assume that you have a service in which you:</p>
<ol>
<li class="">Accept a request</li>
<li class="">Fork a process with <a href="https://nodejs.org/api/child_process.html#child_process_child_process_fork_modulepath_args_options" target="_blank" rel="noopener noreferrer" class=""><code>child_process.fork</code></a></li>
<li class="">Execute some code within that process</li>
<li class="">Exit from the child process</li>
<li class="">Complete the request</li>
</ol>
<p>Probably the first thing you tried was to receive the request, spin up a process, do whatever you need in the processor, and exit. You timed the whole thing and your jaw dropped that it took a million years for the request to complete, even if you are just doing a <code>console.log('I love kittens')</code> inside your processor.</p>
<p>Don't bother. I will tell you right now that the bottleneck is the forking.</p>
<blockquote>
<p>"<strong>OUTRAGEOUS!</strong> I should have used <em>[INSERT_OTHER_TECH_HERE]</em> which is super awesomely fast and all the cool kids are using it! Some random dude on medium.com says it is true!</p>
<p><em>-- your loud voice</em></p>
</blockquote>
<p>Well, instead of re-writting the whole thing in a different language, I have an alternative for you; <strong>use a pool of forked child processes.</strong></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="approach">Approach<a href="https://www.alexanderlolis.com/node-js-fork-is-slow-deal-with-it#approach" class="hash-link" aria-label="Direct link to Approach" title="Direct link to Approach" translate="no">​</a></h2>
<p>The idea is straightforward; Upon service initialization, fork a bunch of processes, and whenever a request comes in, get a resource (a child process) from the pool and use <code>IPC</code> communication to send commands to it. After the processor completes, return it to the pool.</p>
<p>The first thing for all this of course, is a pool. The good news is that there is no need to implement your own, you can just use this lovely <code>npm</code> package, <a href="https://www.npmjs.com/package/generic-pool" target="_blank" rel="noopener noreferrer" class="">generic-pool</a>, or the native <a href="https://nodejs.org/api/cluster.html" target="_blank" rel="noopener noreferrer" class="">cluster</a> module depending on your use case.</p>
<p>For this post, I will use <code>generic-pool</code> so lets start with the processors pool which will look something like this:</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">pool.js</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports">fork</span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'child_process'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">GenericPool</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'generic-pool'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports">logger</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'/your/logger'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> commandProcessorsPool </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token maybe-class-name">GenericPool</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">createPool</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function-variable function" style="color:#d73a49">create</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> modulePath </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> path</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">join</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">__dirname</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'processor.js'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> commandProcessor </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">fork</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">modulePath</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    logger</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">debug</span><span class="token punctuation" style="color:#393A34">(</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">Forked command processor with pid </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">commandProcessor</span><span class="token template-string interpolation punctuation" style="color:#393A34">.</span><span class="token template-string interpolation property-access">pid</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> commandProcessor</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function-variable function" style="color:#d73a49">destroy</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">commandProcessor</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    logger</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">debug</span><span class="token punctuation" style="color:#393A34">(</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">Destroying command processor with pid </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">commandProcessor</span><span class="token template-string interpolation punctuation" style="color:#393A34">.</span><span class="token template-string interpolation property-access">pid</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    commandProcessor</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">removeAllListeners</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    commandProcessor</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">kill</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'SIGKILL'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function-variable function" style="color:#d73a49">validate</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token parameter">commandProcessor</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> commandProcessor</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">connected</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">!</span><span class="token plain">commandProcessor</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">killed</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">testOnBorrow</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">min</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// Depending on your load, set a MINIMUM number of processes that should always be available in the pool</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">max</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">5</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// Depending on your load, set a MAXIMUM number of processes that should always be available in the pool</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">commandProcessorsPool</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">on</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'factoryCreateError'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> logger</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">debug</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">commandProcessorsPool</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">on</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'factoryDestroyError'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> logger</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">debug</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>And of course, you will also need to implement the actual processor which looks like this:</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">processor.js</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports">serializeError</span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'serialize-error'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports">logger</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'/your/logger'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token constant" style="color:#36acaa">MESSAGE_STATUS</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'./pool.js'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">on</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'message'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">message</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">try</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Read and validate input data from `message` and do whatever you need to do...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">name</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> message</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> result </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">petKitten</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">name</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// All went well, send the result of your function to the parent process...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">send</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token literal-property property" style="color:#36acaa">status</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">MESSAGE_STATUS</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">OK</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">data</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> result</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">e</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    logger</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">debug</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">e</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">/* </span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">      In the real world, your processor will probably need to handle errors as well and pass those errors to</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">      the parent process. Unfortunately, sending `Error` instances via `IPC` is not possible, BUT, we can just</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">      serialize them and then deserialize them on the parent process!</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">    */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">send</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token literal-property property" style="color:#36acaa">status</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">MESSAGE_STATUS</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">ERROR</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">data</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">serializeError</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">e</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>Going back to our pool implementation, the only thing left now is the function that will be executing our commands:</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">pool.js</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// ... omitted previous code for succinctness</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports">deserializeError</span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'serialize-error'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">MESSAGE_STATUS</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token constant" style="color:#36acaa">OK</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'ok'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token constant" style="color:#36acaa">ERROR</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'error'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">executeCommand</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">params</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> commandProcessor </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> commandProcessorsPool</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">acquire</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">try</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">commandProcessorTask</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Promise</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">resolve</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> reject</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// https://nodejs.org/api/child_process.html#child_process_event_error</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        commandProcessor</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">on</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'error'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> reject</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        commandProcessor</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">on</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'message'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">message</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">status</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> data</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> message</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> handlersMap </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">[</span><span class="token constant" style="color:#36acaa">MESSAGE_STATUS</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">OK</span><span class="token punctuation" style="color:#393A34">]</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">resolve</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">data</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token comment" style="color:#999988;font-style:italic">// Don't forget to deserialize the error first!</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">[</span><span class="token constant" style="color:#36acaa">MESSAGE_STATUS</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">ERROR</span><span class="token punctuation" style="color:#393A34">]</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">reject</span><span class="token punctuation" style="color:#393A34">(</span><span class="token function" style="color:#d73a49">deserializeProcessorError</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">data</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">           </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> handler </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> handlersMap</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">status</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token plain">handler</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">reject</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Error</span><span class="token punctuation" style="color:#393A34">(</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">Unknown command processor message status '</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">status</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string string" style="color:#e3116c">'</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token function" style="color:#d73a49">handler</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        commandProcessor</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">send</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">params</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> result </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">commandProcessorTask</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> result</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">finally</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Make sure that the command processor is returned to the pool no matter what happened</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> commandProcessorsPool</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">release</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">commandProcessor</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token constant" style="color:#36acaa">MESSAGE_STATUS</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  executeCommand</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>For the shake of this example, I am going to assume that you are using <code>express.js</code>. The following is a very simple and short snippet on how to bring everything together, and execute a command when a request is received:</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_OeMC">express.js</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports">express</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'express'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports">executeCommand</span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'./pool.js'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// By importing the file, our pool will be initialized</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> logger </span><span class="token string" style="color:#e3116c">'/your/logger'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> app </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">express</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">app</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">post</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'/pet_kitten'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">req</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> res</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">name</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> req</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">body</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// If this is a long-running process, then we shouldn't really block the request by using await.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// Depending on your case here, you might want to handle the actual command execution differently.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">executeCommand</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">name</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token keyword control-flow" style="color:#00009f">catch</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">logger</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">error</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  res</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">status</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">202</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// HTTP Status Accepted</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  res</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">json</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token literal-property property" style="color:#36acaa">message</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">Petting </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">name</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string string" style="color:#e3116c"> is underway...</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">app</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">listen</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">3000</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  logger</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">info</span><span class="token punctuation" style="color:#393A34">(</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">kitten app listening at http://localhost:</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">port</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://www.alexanderlolis.com/node-js-fork-is-slow-deal-with-it#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>Although the above was a simple real world example on how to use a pool of processes, it can be the basis for more advanced usage. For example each processor can be modified to accept various commands instead of being limited to just one function. You could also add an <code>onMessage</code> event handler to support commands that send updates while they are being executed. I will leave those for another blog post!</p>
<p>Depending on your use case though, you can do a lot of different things by expanding the approach I have just described.</p>
<p><strong>The important thing here is to remember that most of the time, the problem is not the tool, it's your attitude towards the tool. Be creative and solve shit.</strong></p>]]></content:encoded>
            <category>battlefield</category>
            <category>nodejs</category>
        </item>
        <item>
            <title><![CDATA[Riding the bull; the npm package, that is]]></title>
            <link>https://www.alexanderlolis.com/riding-the-bull</link>
            <guid>https://www.alexanderlolis.com/riding-the-bull</guid>
            <pubDate>Mon, 16 Aug 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[This is a post about a specific Node.js library, named bull, which is used to execute background jobs.]]></description>
            <content:encoded><![CDATA[<p>This is a post about a specific <code>Node.js</code> library, named <a href="https://github.com/OptimalBits/bull" target="_blank" rel="noopener noreferrer" class="">bull</a>, which is used to execute background jobs.</p>
<p>The reason I am writing this is to address some cases which the library does not cover out of the box and share our experience on how we solved them in case others have the same needs.</p>
<p>I assume that you are already familiar with <code>bull</code> and how to use it so I will not waste time providing instructions. The official documentation can be found <a href="https://github.com/OptimalBits/bull#documentation" target="_blank" rel="noopener noreferrer" class="">here</a>, so I strongly recommend looking at it first before you read any further.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="background">Background<a href="https://www.alexanderlolis.com/riding-the-bull#background" class="hash-link" aria-label="Direct link to Background" title="Direct link to Background" translate="no">​</a></h2>
<p>In a system we were working on, we had the requirement for a jobs service. That jobs service had to be written in <code>Node.js</code> for reasons that are irrelevant to this article. The important part is that the users of the system would be able to submit jobs of different types (copies, moves, compressing/extracting files, etc) to the service via a UI and then track their progress and manage them.</p>
<p>Even though <code>bull</code> looked promising, with a nice set of features out of the box, we still had to solve a few problems on our own. <strong>After using this library for some time now with success</strong>, and seeing that others have similar requirements, I thought I should share our experiences in case someone finds them useful.</p>
<p>We were trying to avoid using an external database and keeping in sync the jobs with <code>bull</code> since we didn't need any advanced querying, plus, it is always a pain point to keep two different sources in sync. <strong>One source of truth is always easier...unless the code complexity increases a lot (without even providing a competitive advantage) and suddenly the problems that come with syncing do not sound that bad...</strong></p>
<p>Although we did have a few interesting iterations and used them in production for some time, we ended up going with a database after all.</p>
<div class="theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>caution</div><div class="admonitionContent_BuS1"><p>if you have been using <a href="https://github.com/alolis/bull/tree/rapiddot2" target="_blank" rel="noopener noreferrer" class="">my fork</a> then please stop doing so. All necessary changes are now in the official <code>bull</code> repository.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="fetching-jobs-by-user-id">Fetching jobs by user id<a href="https://www.alexanderlolis.com/riding-the-bull#fetching-jobs-by-user-id" class="hash-link" aria-label="Direct link to Fetching jobs by user id" title="Direct link to Fetching jobs by user id" translate="no">​</a></h2>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="approach-1---using-the-bull-api">Approach #1 - Using the <code>bull</code> API<a href="https://www.alexanderlolis.com/riding-the-bull#approach-1---using-the-bull-api" class="hash-link" aria-label="Direct link to approach-1---using-the-bull-api" title="Direct link to approach-1---using-the-bull-api" translate="no">​</a></h4>
<p>The first pain point in our quest for a database-less solution, was, that the <code>bull</code> API does not expose a method that you can fetch all jobs by filtering the job data (in which the <code>userId</code> is kept). Even if it did, it would probably be expensive to fetch the data by going through all the Redis <code>HSET</code> structures that the library uses.</p>
<p>Your only <strong>API-based option</strong> here is to fetch ALL the jobs with <code>Queue.getJobs</code> and do the filtering on the application level. This, of course, is not optimal at all, unless you know that your dataset will always be small. Not our case unfortunately but it might be OK for yours.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="approach-2---using-a-redis-secondary-index">Approach #2 - Using a Redis secondary index<a href="https://www.alexanderlolis.com/riding-the-bull#approach-2---using-a-redis-secondary-index" class="hash-link" aria-label="Direct link to Approach #2 - Using a Redis secondary index" title="Direct link to Approach #2 - Using a Redis secondary index" translate="no">​</a></h4>
<p>Another approach, since you will already have Redis deployed, is to <strong><a class="" href="https://www.alexanderlolis.com/how-to-tag-data-in-redis#using-sets-as-secondary-indexes">use a Redis secondary index</a></strong>. The general idea is to store a Redis <code>LIST</code> with all the job ids associated with a user id as the key. Then, you would load that list, loop it, and fetch each job with <code>Queue.getJob</code>.</p>
<p>Once again, nothing is for free. You would need to add logic to maintain those lists, cleanup scripts to ensure that nothing in those lists is dead, etc. Depending on your load, this might have a performance penalty since fetching multiple jobs with one call is not available in the <code>bull</code> API, but, if you are not overdoing it I think you will be fine.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="approach-3---one-queue-per-user">Approach #3 - One queue per user<a href="https://www.alexanderlolis.com/riding-the-bull#approach-3---one-queue-per-user" class="hash-link" aria-label="Direct link to Approach #3 - One queue per user" title="Direct link to Approach #3 - One queue per user" translate="no">​</a></h4>
<p>Another, more complex approach, if you do not want to use a database, is to <strong>use one queue per user</strong> in combination with <a href="https://github.com/OptimalBits/bull/blob/develop/PATTERNS.md#reusing-redis-connections" target="_blank" rel="noopener noreferrer" class="">connections re-use</a>. Depending on your use case and your user base, this might be just fine for you. You would still need to have all the queue objects loaded in memory at the same time, route each user request to the appropriate queue (e.g. by maintaining a <code>Map</code> with <code>userId-&gt;Queue</code> association), load all existing queues upon service initialization (and since there is no <code>Queue.list</code> method you need to <a href="https://github.com/OptimalBits/bull/issues/1024#issuecomment-414478540" target="_blank" rel="noopener noreferrer" class="">scan Redis manually to get the queue names</a>, and then instantiate the queues), cleanup, etc. If your load is relatively small, with a couple of thousand users, you will probably be ok with this.</p>
<p>If you need to have multiple queues per user, then I am telling you already that this will be far more complex than you think and you should probably avoid it. <strong>We have already done that, it worked, but, we weren't very happy with it, it does not scale well, and it will not work in a distributed environment.</strong></p>
<p>If you really want to give it a go, however, the following example code should give you the general idea.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-userqueue-class">The <code>UserQueue</code> class<a href="https://www.alexanderlolis.com/riding-the-bull#the-userqueue-class" class="hash-link" aria-label="Direct link to the-userqueue-class" title="Direct link to the-userqueue-class" translate="no">​</a></h4>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">Queue</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'bull'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports">_</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> 'lodash</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Keep track of all our user queues</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> userQueues </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Map</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// The `UserQueue` class can serve as a layer between `bull` and your application if you need</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// multiple queues per user and implement any method that you need here in order to manage</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// the underlying queues.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">UserQueue</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">constructor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">userId</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">queues</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Map</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">initQueues</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Initialize all your queues and for each queue add an entry to the this.queues</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// map with the queue type as a key and the Queue object as value. Each queue needs</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// to include the userId in it's name along with the queue type in order to be easily</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// identifiable e.g. somePrefix:45:copies</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Make sure that you re-use Redis connections here or else you will end up with A LOT</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// of open connections since each queue requires at least 3.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">getJobs</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// You basically need to loop all queues and get the jobs from each queue. No need from filtering</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// on the application level since all the jobs belong to this user. This is a very simple example</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// however, check Queue.getJobs documentation since you might need to pass extra parameters.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> promises </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> _</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">map</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">queues</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token parameter">queue</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> queue</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">getJobs</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> jobs </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> </span><span class="token known-class-name class-name">Promise</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">all</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">promises</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> jobs</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// Add any other methods you might need to interact with the underlying queues</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Check if the queue is already in our Map or else instantiate it and add it before returning it</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">loadUserQueue</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">userId</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">userQueues</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">has</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">userId</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> userQueues</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">userId</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">else</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> userQueue </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">UserQueue</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">userId</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    userQueues</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">userId</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> userQueue</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> userQueue</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">/* </span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">   Upon service initialization</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">*/</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Load all queue key names and extract the user id from the key matches</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> userIds </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">getUserIdsFromQueueKeys</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Instantiate all queues in order to start processing jobs</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">_</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">forEach</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">userIds</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> loadUserQueue</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">/* </span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">   During normal operation</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">*/</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// A request comes in to fetch the jobs for user 45</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> userQueue </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">loadUserQueue</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">45</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> jobs </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> userQueue</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">getJobs</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="approach-4---using-a-database">Approach #4 - Using a database<a href="https://www.alexanderlolis.com/riding-the-bull#approach-4---using-a-database" class="hash-link" aria-label="Direct link to Approach #4 - Using a database" title="Direct link to Approach #4 - Using a database" translate="no">​</a></h4>
<p>If you want to use a database, you need to built a layer in front of <code>bull</code> to bridge them together and control the flow. That layer, can be a service class, or separate functions, or whatever fits your codebase best.</p>
<p>Of course, there is the issue of data synchronization. If, for example, you have some kind of cleanup script that runs <code>Queue.clean</code> every now and then, you need to remove the jobs from your database as well. That's where the <a href="https://github.com/OptimalBits/bull/blob/develop/REFERENCE.md#events" target="_blank" rel="noopener noreferrer" class="">queue event handlers</a> come in handy!</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-jobservice-class">The <code>JobService</code> class<a href="https://www.alexanderlolis.com/riding-the-bull#the-jobservice-class" class="hash-link" aria-label="Direct link to the-jobservice-class" title="Direct link to the-jobservice-class" translate="no">​</a></h4>
<p>So, an approach with a service class would look like this:</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">Queue</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'bull'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">JobService</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">constuctor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">queues</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Map</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">initQueues</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token doc-comment comment" style="color:#999988;font-style:italic">/**</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic">    Initialize all service queues.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic">    @private</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic">  */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">initQueues</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Initialize all your queues and update `this.queues`</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// For each queue run `this.attachQueueHandlers(queue)`</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token doc-comment comment" style="color:#999988;font-style:italic">/**</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic">    Attach event handlers to each queue.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic">    @private</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic">  */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">attachQueueHandlers</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">queue</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     queue</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">on</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'removed'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">job</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       </span><span class="token comment" style="color:#999988;font-style:italic">// Remove from your database</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">add</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">jobData</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Add the job to your database</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Add the job to the appropriate queue but remember to use the id from database by passing</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// it to the `Queue.add` method!</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> job</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">find</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">filters </span><span class="token parameter operator" style="color:#393A34">=</span><span class="token parameter"> </span><span class="token parameter punctuation" style="color:#393A34">{</span><span class="token parameter punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Use your database to find the jobs you want</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// Add any other methods you might need to manage your jobs</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Global service object</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> jobService </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">JobService</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> job </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> jobService</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">add</span><span class="token punctuation" style="color:#393A34">(</span><span class="token spread operator" style="color:#393A34">...</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre></div></div>
<p>From what you have probably already figured out, if you want more than just fetching the jobs of a specific user, then the place to add more code is here!</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="removing-an-active-job-in-sandboxed-environments">Removing an active job in sandboxed environments<a href="https://www.alexanderlolis.com/riding-the-bull#removing-an-active-job-in-sandboxed-environments" class="hash-link" aria-label="Direct link to Removing an active job in sandboxed environments" title="Direct link to Removing an active job in sandboxed environments" translate="no">​</a></h2>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="overview">Overview<a href="https://www.alexanderlolis.com/riding-the-bull#overview" class="hash-link" aria-label="Direct link to Overview" title="Direct link to Overview" translate="no">​</a></h4>
<p>This was another pain point when we started using <code>bull</code>; the inability to remove - basically abort - a running job in a <a href="https://github.com/OptimalBits/bull#separate-processes" target="_blank" rel="noopener noreferrer" class="">sandboxed environment</a>. The <a href="https://github.com/OptimalBits/bull/issues/1098" target="_blank" rel="noopener noreferrer" class="">first approach we tried</a>, although it did work for us, it was a bad solution since our <a href="https://github.com/alolis/bull/tree/rapiddot" target="_blank" rel="noopener noreferrer" class="">fork</a> was straying way far out from the core boundaries of the library. That means, that after a while, maintenance starts becoming a nightmare.</p>
<p>And of course, the nightmare became true after bluebird (which supports <a href="http://bluebirdjs.com/docs/api/cancellation.html" target="_blank" rel="noopener noreferrer" class="">promise cancellation</a>) was <a href="https://github.com/OptimalBits/bull/commit/f05e67724cc2e3845ed929e72fcf7fb6a0f92626" target="_blank" rel="noopener noreferrer" class="">completely removed</a> from the library. That would mean that our fork would not be able to be in sync with the official repo from now on. To be fair here, I think that move was 100% correct from the <code>bull</code> side and it helped us push in a far better direction. Besides, I do love a challenge!</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="approach">Approach<a href="https://www.alexanderlolis.com/riding-the-bull#approach" class="hash-link" aria-label="Direct link to Approach" title="Direct link to Approach" translate="no">​</a></h4>
<p>So, the idea is simple:</p>
<ol>
<li class="">Send a <code>kill</code> signal to the sandboxed job processor</li>
<li class="">Handle the signal from within the processor</li>
<li class="">Discard the job (in order for <code>bull</code> to not retry it)</li>
<li class=""><code>process.exit</code> from within the processor and let <code>bull</code> do the rest</li>
</ol>
<p>As with all recipes, these are the ingredients to make it work:</p>
<ul>
<li class=""><code>Job.update</code> to be available in the sandboxed environment (<a href="https://github.com/OptimalBits/bull/releases/tag/v3.23.0" target="_blank" rel="noopener noreferrer" class="">added in v3.23.0</a>)</li>
<li class=""><code>Job.discard</code> to be available in the sandboxed environment (<a href="https://github.com/OptimalBits/bull/releases/tag/v3.27.0" target="_blank" rel="noopener noreferrer" class="">added in v3.24.0</a>)</li>
<li class="">A high order function to wrap all our processors with the common functionality</li>
</ul>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-wrapprocessor-function">The <code>wrapProcessor</code> function<a href="https://www.alexanderlolis.com/riding-the-bull#the-wrapprocessor-function" class="hash-link" aria-label="Direct link to the-wrapprocessor-function" title="Direct link to the-wrapprocessor-function" translate="no">​</a></h4>
<p>The following code snippet shows a simplified version of a higher order function that can be used to wrap all your processors in order to enhance them with your functionality and keeping all that code in one place. In our own wrapper version, we do other things as well, like, processor validation, setting the state of the jobs in our database, wrapping any errors thrown, etc. Any extra functionality that you need to run before and/or after executing the processor, can be done here.</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports">logger</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'/your/logger'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token doc-comment comment" style="color:#999988;font-style:italic">/**</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * Wraps a processor and adds common functionality to avoid code duplication.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * Make sure that ALL processors are exported by being wrapped first.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> *</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@param</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">Function</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment parameter" style="color:#999988;font-style:italic">processor</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"></span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@throws</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">Error</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"></span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@returns</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">Function</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"></span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">wrapProcessor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">processor</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">wrappedProcessor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">job</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">exitHandler</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">exitCode</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      logger</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">debug</span><span class="token punctuation" style="color:#393A34">(</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">Received SIGTERM for job id '</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">job</span><span class="token template-string interpolation punctuation" style="color:#393A34">.</span><span class="token template-string interpolation property-access">id</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string string" style="color:#e3116c">' with exit code '</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">exitCode</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string string" style="color:#e3116c">' and PID '</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">process</span><span class="token template-string interpolation punctuation" style="color:#393A34">.</span><span class="token template-string interpolation property-access">pid</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string string" style="color:#e3116c">'</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Discard the job first to ensure that it will not be retried after a process kill.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      job</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">discard</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">exit</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">exitCode</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">on</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'SIGTERM'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> exitHandler</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">try</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Store the process pid in order to be able to abort the process at any time by simply killing it.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> job</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">update</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token spread operator" style="color:#393A34">...</span><span class="token plain">job</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">data</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">pid</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">pid</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> result </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">processor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">job</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> result</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">finally</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// Bull internally uses a child pool of forked processors that are being re-used so we need to make sure</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// that we remove the listener before the processor returns to the pool or else we will cause memory leakage.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      process</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">removeListener</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'SIGTERM'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> exitHandler</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> wrappedProcessor</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="usage">Usage<a href="https://www.alexanderlolis.com/riding-the-bull#usage" class="hash-link" aria-label="Direct link to Usage" title="Direct link to Usage" translate="no">​</a></h4>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// myProcessor.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports">wrapProcessor</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'/some/path/wrap_processor.js'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">myProcessor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">job</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic">// Your processor code here</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> result</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Wrap your processor!</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">wrapProcessor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">myProcessor</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// main.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">Queue</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'bull'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports">kill</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'tree-kill'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">killJob</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">queue</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> jobId</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Promise</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">resolve</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> reject</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">try</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> job </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> queue</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">getJob</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">jobId</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token plain">job</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">resolve</span><span class="token punctuation" style="color:#393A34">(</span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// You can also add an extra check here if the PID does not exist</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// and return resolve(false) if it's true.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token function" style="color:#d73a49">kill</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">job</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">data</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">pid</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'SIGTERM'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">err</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">err</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token function" style="color:#d73a49">reject</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">err</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">else</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token function" style="color:#d73a49">resolve</span><span class="token punctuation" style="color:#393A34">(</span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> 	</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">e</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> 	  </span><span class="token function" style="color:#d73a49">reject</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">e</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> 	</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> queue </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Queue</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'downloader'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">queue</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">process</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'/some/path/processors/my_processor.js'</span><span class="token plain">'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> job </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> queue</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">addJob</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token literal-property property" style="color:#36acaa">url</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'https://example.com/file1.mov'</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// The following will send a SIGTERM to the underlying job process, and if it's still active, the</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// job processor will receive it, exit itself, and the job will be moved to the failed queue by bull.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">killJob</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">queue</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> job</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">id</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre></div></div>
<p>And of course, you can always update the <a class="" href="https://www.alexanderlolis.com/riding-the-bull#the-jobservice-class"><code>JobService</code></a> class and implement an <code>abort</code> method like so:</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">JobService</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ... omitted previous code for succinctness</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">load</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">id</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Load from database</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// If id does not exist, throw error</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> job</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">abort</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">id</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> job </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">load</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">id</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">kill</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">job</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">data</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">pid</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> job</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">kill</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">pid</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Kill code here</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="attaching-current-state-to-jobs">Attaching current state to jobs<a href="https://www.alexanderlolis.com/riding-the-bull#attaching-current-state-to-jobs" class="hash-link" aria-label="Direct link to Attaching current state to jobs" title="Direct link to Attaching current state to jobs" translate="no">​</a></h2>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="overview-1">Overview<a href="https://www.alexanderlolis.com/riding-the-bull#overview-1" class="hash-link" aria-label="Direct link to Overview" title="Direct link to Overview" translate="no">​</a></h4>
<p>The only way to get the job state with the <code>bull</code> API at the moment is <code>Job.getState</code>. That means, that first, you need to load the job(s) and then call the method. Not very convenient I am afraid, but again, for performance reasons from the <code>bull</code> side, this is how it is.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="approach-1---dynamically-attach-a-state-field-to-jobs">Approach #1 - Dynamically attach a <code>state</code> field to jobs<a href="https://www.alexanderlolis.com/riding-the-bull#approach-1---dynamically-attach-a-state-field-to-jobs" class="hash-link" aria-label="Direct link to approach-1---dynamically-attach-a-state-field-to-jobs" title="Direct link to approach-1---dynamically-attach-a-state-field-to-jobs" translate="no">​</a></h4>
<p>Although this is not very robust since it can break after a <code>bull</code> upgrade, it worked fine for us at the beginning (before we ended up using a database) and it might do the trick for you as well. The following function can be used to attach a <code>state</code> field on each <code>Job</code> object, which is way faster than calling <code>.getState</code> for every each one of them:</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports">_</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'lodash'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token doc-comment comment" style="color:#999988;font-style:italic">/**</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * Enum for possible job states.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> *</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@readonly</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"></span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@enum</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">String</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"></span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">JOB_STATE</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token constant" style="color:#36acaa">ACTIVE</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'active'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token constant" style="color:#36acaa">WAITING</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'waiting'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token constant" style="color:#36acaa">DELAYED</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'delayed'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token constant" style="color:#36acaa">COMPLETED</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'completed'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token constant" style="color:#36acaa">FAILED</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'failed'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token constant" style="color:#36acaa">PAUSED</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'paused'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token constant" style="color:#36acaa">STUCK</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'stuck'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token constant" style="color:#36acaa">UNKNOWN</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'unknown'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token doc-comment comment" style="color:#999988;font-style:italic">/**</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * The function will use some of the job fields in order to calculate the current state.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> *</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@param</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">Job</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment parameter" style="color:#999988;font-style:italic">job</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> - The job object.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@see</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">@link https</span><span class="token doc-comment comment class-name operator" style="color:#393A34;font-style:italic">:</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">//github</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">.</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">com/OptimalBits/bull/issues/</span><span class="token doc-comment comment class-name number" style="color:#36acaa;font-style:italic">1076</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"></span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token function" style="color:#d73a49">attachStateField</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">job</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     processedOn </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     delay </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     finishedOn </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     failedReason </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> job</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> statesPredicateMapping </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     </span><span class="token punctuation" style="color:#393A34">[</span><span class="token constant" style="color:#36acaa">JOB_STATE</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">COMPLETED</span><span class="token punctuation" style="color:#393A34">]</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> _</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">isInteger</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">finishedOn</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> _</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">isNull</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">failedReason</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     </span><span class="token punctuation" style="color:#393A34">[</span><span class="token constant" style="color:#36acaa">JOB_STATE</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">ACTIVE</span><span class="token punctuation" style="color:#393A34">]</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> _</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">isInteger</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">processedOn</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> _</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">isNull</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">finishedOn</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> _</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">isNull</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">failedReason</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     </span><span class="token punctuation" style="color:#393A34">[</span><span class="token constant" style="color:#36acaa">JOB_STATE</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">DELAYED</span><span class="token punctuation" style="color:#393A34">]</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> _</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">isNull</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">processedOn</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> delay </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     </span><span class="token punctuation" style="color:#393A34">[</span><span class="token constant" style="color:#36acaa">JOB_STATE</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">WAITING</span><span class="token punctuation" style="color:#393A34">]</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> _</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">isNull</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">processedOn</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> delay </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     </span><span class="token punctuation" style="color:#393A34">[</span><span class="token constant" style="color:#36acaa">JOB_STATE</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">FAILED</span><span class="token punctuation" style="color:#393A34">]</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> _</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">isString</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">failedReason</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   job</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">state</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> _</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">findKey</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">statesPredicateMapping</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token parameter">predicate</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">predicate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">JOB_STATE</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">UNKNOWN</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> job</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Usage</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> queue </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Queue</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'downloader'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> jobs </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> queue</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">getJobs</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">_</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">forEach</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">job</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> attachStateField</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>You can also check <a href="https://github.com/OptimalBits/bull/issues/1076" target="_blank" rel="noopener noreferrer" class="">this</a> discussion thread which includes the above plus a few more ideas in case you want to persue this any further.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="approach-2---using-a-database">Approach #2 - Using a database<a href="https://www.alexanderlolis.com/riding-the-bull#approach-2---using-a-database" class="hash-link" aria-label="Direct link to Approach #2 - Using a database" title="Direct link to Approach #2 - Using a database" translate="no">​</a></h4>
<p>If you are using a database, then this is pretty straightforward. You add the job in the database with a default value, like <code>JOB_STATE.PENDING</code>, and then with the use of the <a href="https://github.com/OptimalBits/bull/blob/develop/REFERENCE.md#events" target="_blank" rel="noopener noreferrer" class="">queue events</a>, we can update the job state.</p>
<p>The following is using the <a class="" href="https://www.alexanderlolis.com/riding-the-bull#the-jobservice-class"><code>JobService</code></a> from our previous section to show you an example of how to do it:</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">JobService</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ... omitted previous code for succinctness</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token doc-comment comment" style="color:#999988;font-style:italic">/**</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic">    Attach event handlers to each queue.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic">    @private</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic">  */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">attachQueueHandlers</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">queue</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Attach on queue all events that can change state like so:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     queue</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">on</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'active'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">job</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       </span><span class="token comment" style="color:#999988;font-style:italic">// Update `state` field in database</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Add all the necessary queue event handlers that change state</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="using-a-custom-error-object">Using a custom Error object<a href="https://www.alexanderlolis.com/riding-the-bull#using-a-custom-error-object" class="hash-link" aria-label="Direct link to Using a custom Error object" title="Direct link to Using a custom Error object" translate="no">​</a></h2>
<p>Another problem that we had to solve was the need to expose custom error objects from within the processors in order to be able to pass them higher in the call chain and handle them accordingly.</p>
<p>Unfortunately, <code>bull</code> does not have some kind of mechanism at the moment to do that and the only thing you have access to when an error occurs, is the <code>job.failedReason</code> which is a string. So, why not use that to our benefit?</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="approach-1">Approach<a href="https://www.alexanderlolis.com/riding-the-bull#approach-1" class="hash-link" aria-label="Direct link to Approach" title="Direct link to Approach" translate="no">​</a></h4>
<p>You basically need to create a small mechanism to <code>JSON.stringify</code> the error object you want to store and <code>JSON.parse</code> it before consumption.</p>
<p>One way to do this is by taking advantage of our previously mentioned higher order function, <a class="" href="https://www.alexanderlolis.com/riding-the-bull#the-wrapprocessor-function"><code>wrapProcessor</code></a> in combination with a custom <code>Error</code> object that will use the <code>message</code> property of the <code>Error</code> to store a JSON stringified value. The reason we will use the <code>message</code> property is because that's what <code>bull</code> <a href="https://github.com/OptimalBits/bull/blob/v3.28.1/lib/job.js#L276" target="_blank" rel="noopener noreferrer" class="">uses to automatically store the failed reason</a>.</p>
<p>After you wrap the error thrown by the processor, you can just re-throw it and parse it in your <code>failed</code> queue event handler or when you load a job with the <code>Queue</code> class. In the following code I will use our <a class="" href="https://www.alexanderlolis.com/riding-the-bull#the-jobservice-class"><code>JobService</code></a> class as an example for parsing and handling the error:</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// The custom error object</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token known-class-name class-name">ExtendableError</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'es6-error'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">JobError</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">extends</span><span class="token plain"> </span><span class="token class-name">ExterndableError</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">constructor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">code</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> message</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">errorObject</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      code</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      message</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">super</span><span class="token punctuation" style="color:#393A34">(</span><span class="token known-class-name class-name">JSON</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">stringify</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">errorObject</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">getErrorObject</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">errorObject</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// The updated wrapProcessor function</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports">logger</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'/your/logger'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">wrapProcessor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">processor</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">wrappedProcessor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">job</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// ... omitted previous code for succinctness</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">try</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     </span><span class="token comment" style="color:#999988;font-style:italic">// ... omitted previous code for succinctness</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> result </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">processor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">job</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> result</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">e</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      logger</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">error</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">e</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">e </span><span class="token keyword" style="color:#00009f">instanceof</span><span class="token plain"> </span><span class="token class-name">JobError</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword control-flow" style="color:#00009f">throw</span><span class="token plain"> e</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">else</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">/* </span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">          Wrap the error as `JobError` and throw it. You can also create a mapping of "known" errors and assign them</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">          a specific code and error message. For example if you are doing validation within your processor, you could</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">          handle the validation error here and then wrap it as a `JobError`.</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">        */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">finally</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// ... omitted previous code for succinctness</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> wrappedProcessor</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// myProcessor.js</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">myProcessor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">job</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic">// Your processor code here</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic">// Something bad happened, throw error!</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token keyword control-flow" style="color:#00009f">throw</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">JobError</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'myErrorCode'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Something very bad happened!'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> result</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Wrap your processor!</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">wrapProcessor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">myProcessor</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// The updated `JobService` </span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports">_</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'lodash'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">JobService</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ... omitted previous code for succinctness</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token doc-comment comment" style="color:#999988;font-style:italic">/**</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic">    Attach event handlers to each queue.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic">    @private</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic">  */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">attachQueueHandlers</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">queue</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Attach on queue all events that can change state like so:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     queue</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">on</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'failed'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">job</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> errorObject </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">parseFailedReason</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">job</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">failedReason</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       </span><span class="token comment" style="color:#999988;font-style:italic">// Do whatever you want with `errorObject`</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Add all the necessary queue event handlers that change state</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token doc-comment comment" style="color:#999988;font-style:italic">/**</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic">   * Parses failed reason which occurs when the job process gets killed or an error is thrown from within</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic">   * the processor. Bull uses the `message` property from any errors inherited from `Error`, so, if that's the case </span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic">   * we should be able to get a JSON object since we stringified our error object, or else we will just handle </span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic">   * the `failedReason` parameter as a plain string.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic">   *</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic">   * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@private</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"></span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic">   * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@see</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">@link https</span><span class="token doc-comment comment class-name operator" style="color:#393A34;font-style:italic">:</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">//github</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">.</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">com/OptimalBits/bull/blob/v3</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">.</span><span class="token doc-comment comment class-name number" style="color:#36acaa;font-style:italic">28.1</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">/lib/job</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">.</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">js#L276</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"></span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic">   * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@param</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">String</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment parameter" style="color:#999988;font-style:italic">failedReason</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"></span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic">   * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@returns</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">Object</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"></span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic">  */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">parseFailedReason</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">failedReason</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// https://github.com/OptimalBits/bull/blob/develop/lib/process/sandbox.js#L42</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> unexpectedExitString </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Unexpected exit code'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">try</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token known-class-name class-name">JSON</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">parse</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">failedReason</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">e</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// If we are here, then it means that something out of our control happened</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// and the processor never returned a json result string as we expected.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token literal-property property" style="color:#36acaa">code</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> _</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">startsWith</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">failedReason</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> unexpectedExitString</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token string-property property" style="color:#36acaa">'UNEXPECTED_EXIT_ERROR'</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token string" style="color:#e3116c">'INTERNAL_ERROR'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">         </span><span class="token literal-property property" style="color:#36acaa">message</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> failedReason</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="per-user-per-queue-concurrency-granularity">Per-user, per queue concurrency granularity<a href="https://www.alexanderlolis.com/riding-the-bull#per-user-per-queue-concurrency-granularity" class="hash-link" aria-label="Direct link to Per-user, per queue concurrency granularity" title="Direct link to Per-user, per queue concurrency granularity" translate="no">​</a></h2>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="overview-2">Overview<a href="https://www.alexanderlolis.com/riding-the-bull#overview-2" class="hash-link" aria-label="Direct link to Overview" title="Direct link to Overview" translate="no">​</a></h4>
<p>The final and toughest pain point was that the concurrency of the <a href="https://github.com/OptimalBits/bull/blob/develop/REFERENCE.md#queueprocess" target="_blank" rel="noopener noreferrer" class="">named processors</a> was not working as we thought it would.</p>
<p>For our case, we needed to have a concurrency limit <em>per-user, per queue</em>, and at first, the named processors in combination with the <code>concurrency</code> option seemed that it might do the trick. Unfortunately, <a href="https://github.com/OptimalBits/bull/issues/1113" target="_blank" rel="noopener noreferrer" class="">that was not the case</a>. The plan was to use one named processor per user to apply those limits. Not sure if that would have worked well  anyway, but it wasn't a viable option so we didn't pursue it any further.</p>
<p>The following from the <code>bull</code> documentation explains the named processors' concurrency limitation:</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic">/***</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * For each named processor, concurrency stacks up, so any of these three process functions</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * can run with a concurrency of 125. To avoid this behaviour you need to create an own queue</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * for each process function.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> loadBalancerQueue </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Queue</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'loadbalancer'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">loadBalancerQueue</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">process</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'requestProfile'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">100</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> requestProfile</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">loadBalancerQueue</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">process</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'sendEmail'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">25</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> sendEmail</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">loadBalancerQueue</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">process</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'sendInvitation'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> sendInvite</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="approach-1---one-queue-per-queue-type">Approach #1 - One queue per queue type<a href="https://www.alexanderlolis.com/riding-the-bull#approach-1---one-queue-per-queue-type" class="hash-link" aria-label="Direct link to Approach #1 - One queue per queue type" title="Direct link to Approach #1 - One queue per queue type" translate="no">​</a></h4>
<p>If you need a per queue concurrency, then the simplest way to do it is to use separate queues. if your environment has some kind of limitation then you can also <a href="https://github.com/OptimalBits/bull/blob/develop/PATTERNS.md#reusing-redis-connections" target="_blank" rel="noopener noreferrer" class="">re-use your connections</a> as I have already mentioned.</p>
<p>In combination with the <a class="" href="https://www.alexanderlolis.com/riding-the-bull#the-userqueue-class"><code>UserQueue</code></a> class, you could also achieve a database-less solution for a per-user, per queue concurrency granularity.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="approach-2---using-a-database-1">Approach #2 - Using a database<a href="https://www.alexanderlolis.com/riding-the-bull#approach-2---using-a-database-1" class="hash-link" aria-label="Direct link to Approach #2 - Using a database" title="Direct link to Approach #2 - Using a database" translate="no">​</a></h4>
<p>Let me start by saying that for our own needs, we have a more complex implementation so I will try to simplify this as much as I can to give you a general idea of what you can try on your own.</p>
<p>The idea is this:</p>
<ol>
<li class="">Keep track of all jobs in a database with a <code>state</code> and a <code>userId</code> field on each entry</li>
<li class="">Every time a job is created, completed or failed, a <code>JobService</code> method (let's call it <code>addNextPendingJobs</code>) will take a count of currently active jobs of the user and if they have empty slots, add some more to <code>bull</code></li>
</ol>
<p><strong>Sounds straightforward, right? Wrong.</strong> If your service accepts parallel requests, you will need to take into account any possible race conditions each time you count the active jobs of a user and deciding whether or not you should add it to the <code>bull</code> queue.</p>
<p>Example scenario:</p>
<ol>
<li class=""><em>Request A</em> comes in, you execute a database count</li>
<li class=""><em>Request B</em> comes in, you execute a database count</li>
<li class=""><em>Count B</em> returns, slots are available, code starts adding jobs to <code>bull</code></li>
<li class="">Before the jobs from <em>Count B</em> have been added to bull and the <code>state</code> has been updated, *Count A *returns. Slots are available (even though they are not) and code starts adding jobs to <code>bull</code></li>
<li class="">You now have more active jobs than your limit allows</li>
</ol>
<p>So, depending on the database you are using, the atomicity level it offers, whether or not it supports transactions and exclusive locking, you will need to implement your <code>addNextPendingJobs</code> accordingly. <strong>In other words, you need to ensure that the entries that are being read during the counting are locked like they are being updated and not allow other operations from modifying them until the transaction ends.</strong></p>
<p>For example, in the SQL world, this can be done with <code>SELECT...FOR UPDATE</code> but this is way out of the scope of this article. <strong>If you have NO CLUE what I am talking about then I recommend starting reading material on locking and the documentation of your database before you try anything further.</strong></p>
<p>If your database does not support any of the above, or if you want to use Redis to implement the whole thing, you could also use something like <a href="https://github.com/mike-marcacci/node-redlock" target="_blank" rel="noopener noreferrer" class="">redlock</a> instead.</p>
<p>And of course, <strong>you can always process everything in sequence and avoid any possible race conditions</strong>, but it will create a bottleneck if you have a big load. As always, depending on your case, it's up to you.</p>
<p>Finally, you will need to use the <a href="https://github.com/OptimalBits/bull/blob/develop/REFERENCE.md#events" target="_blank" rel="noopener noreferrer" class="">queue events</a> to handle the <code>completed</code> and <code>failed</code> events and add more jobs whenever necessary.</p>
<p>The updated code in our <a class="" href="https://www.alexanderlolis.com/riding-the-bull#the-jobservice-class"><code>JobService</code></a> will look like this:</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports">logger</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'/your/logger'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">JobService</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ... omitted previous code for succinctness</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token doc-comment comment" style="color:#999988;font-style:italic">/**</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic">    Attach event handlers to each queue.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic">    @private</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic">  */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">attachQueueHandlers</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">queue</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Attach on queue all events that can add next pending jobs like so:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     queue</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">on</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'completed'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">job</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       </span><span class="token comment" style="color:#999988;font-style:italic">// this.addNextPendingJobs</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    queue</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">on</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'failed'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">job</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// For the failed scenario, we need to check whether or not the failed job will automatically</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic">// re-run by `bull` before we try to add more jobs</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">job</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">isDiscarded</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> job</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">attemptsMade</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&gt;=</span><span class="token plain"> job</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">opts</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">attempts</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// this.addNextPendingJobs</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">create</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">jobData</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// ... code</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Add next pending jobs whenever possible without blocking</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">addNextPendingJobs</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">jobData</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">userId</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> jobData</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">type</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token keyword control-flow" style="color:#00009f">catch</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">logger</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">error</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">addNextPendingJobs</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">userId</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> jobType</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     </span><span class="token comment" style="color:#999988;font-style:italic">// Start a transaction/take lock</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     </span><span class="token comment" style="color:#999988;font-style:italic">// Count all ACTIVE entries of the specified `userId` and `jobType`</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     </span><span class="token comment" style="color:#999988;font-style:italic">// If the active are less than MAX ALLOWED then update those entries to WAITING</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     </span><span class="token comment" style="color:#999988;font-style:italic">// and start adding them to the appropriate `bull` queue</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     </span><span class="token comment" style="color:#999988;font-style:italic">// Commit transaction/release lock</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>The above is simplified but it should give you some idea on how to proceed. As a final remark, <strong>the whole approach is not bulletproof</strong>. You need to ensure that queue stagnation will never happen in case something goes wrong in <code>addNextPendingJobs</code> and more jobs are not added to the queue. A retry strategy can be used or maybe the code that adds more jobs can live in a <code>setInterval</code> if you are using a single service.</p>
<p>Like I said plenty of times before, depends on your use case and there is always room for improvement.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://www.alexanderlolis.com/riding-the-bull#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>This has been a long post but I wanted to show you that with a little creativity a lot can be accomplished. <strong>No magic tool, or a magic library exists out there that will cover all your requirements out of the box, and being eager to jump into a custom solution from the first sign of limitation is a bad tactic</strong>. Of course, you will need to spend some time figuring out how an existing solution works, but if its core fits your needs and you can build on top of it, then you will just need to solve a specific set of problems instead of the whole spectrum of what the existing solution is solving.</p>
<p>If you have any questions/feedback for any of the above, then do not hesitate to email me!</p>]]></content:encoded>
            <category>development</category>
            <category>battlefield</category>
            <category>nodejs</category>
            <category>javascript</category>
        </item>
        <item>
            <title><![CDATA[How to pass the first round of my interviews]]></title>
            <link>https://www.alexanderlolis.com/how-to-pass-the-first-round-of-my-interviews</link>
            <guid>https://www.alexanderlolis.com/how-to-pass-the-first-round-of-my-interviews</guid>
            <pubDate>Mon, 25 Jan 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[A cheat sheet for future software developer candidates]]></description>
            <content:encoded><![CDATA[<p>If you are reading this, then congratulations, you’ve just checked one of the things that I am looking for in a candidate; <strong>the ability to do some basic research</strong>. In this case, by simply reading information about the company you are applying to.</p>
<p>I am astounded by the number of candidates that come for an interview and have no clue what the company does exactly. Except of what the – clueless recruiter – might have told them, they haven’t even bothered to check the company site in detail. There is occasionally someone that has read the first paragraph of the home page, but that’s usually it. <strong>If you don’t give a damn or don’t even have the curiosity to check out the place you will be spending a large part of your day, then I do not want to work with you.</strong></p>
<p>If on the other hand you do give a damn, then keep reading.</p>
<p><strong>Don’t be late.</strong> I hate when people are being late. I do not care about any excuses, and unless there was a real emergency, you should be on time. It’s a sign of respect, professionalism and that you understand that your time is not more important than the other person’s time.</p>
<p>I will be polite upon your arrival. <strong>I will offer you something to drink</strong>, probably give you a few options but please choose the water. It’s not a cafeteria and I am not your barista. I am your interviewer so do not abuse my hospitality. That means you should not ask me for a latte like someone did once. Yes, that has really happened. He wanted to look "cool" as they advised him in the computer science school he went to during his "how to prepare for an interview" course. Nope, I did not find it cool.</p>
<p>We will be moving to the meeting room where the interview will take place. <strong>I am an easy person to talk to</strong> so try to relax. We will start with some <strong>casual conversation</strong> about your past experience, things that interest you, what happened to your last job, what you expect from your new job, how you try to improve yourself every day, what was the last book you read, what hobbies you have and basically anything else that might give me the slightest clue about the person you are.</p>
<p>I want to be absolutely clear about this. <strong>What type of person you are and how well you will fit in the team is the most important thing for me</strong>. Of course, your technical skills are important as well, but let me put it this way; if it comes down to two candidates, and the first one has an open personality and qualities that I am looking for, but the second one does not, although their technical skills are better, I will <strong>ALWAYS</strong> choose the first one. You can teach a skill but you cannot teach someone how to be a human being.</p>
<p><strong>What qualities am I looking for?</strong> Honesty, patience, someone who is observant, reliable, and respectful of those around them. I want to see a passionate, trustworthy person. A person that is not always looking for excuses or whines about everything but rises up to the occasion. And of course, to have some sense of humor, because, what is life without humor?</p>
<p><strong>Try to be as sincere as possible</strong> about yourself. Even if you pretend to be someone you are not during the interview, or if you manage to give the answers I want to hear, you won’t be able to hide your real self after a couple of months of working closely together. <strong>Under pressure, all characters are truly revealed. And you will be pressed. So please, if you plan to bamboozle me, do both of us a favor and walk away because I will be reluctant to work with you.</strong></p>
<p>At some point, after I feel I have heard enough about you – or too little – <strong>we will move to the technical part of the interview. Please do not suck. No seriously, please do not suck.</strong> By sucking I mean that when I ask you to write a for loop (for a simple “FizzBuzz” test) on the whiteboard just to check that you are actually able to write ANY code at all before we proceed, please do write it without hesitation. Unfortunately, it has happened more than once people tell me “I haven’t written code for the last couple of months and I do not remember how to write a loop at the moment” or “I did not expect to write any code. Nobody asked me in the other interviews”. SERIOUSLY? This is an interview for a software development position. I definitely want to see SOME code.</p>
<p>Yes, I know that phone screens exist, yes I know that you can give to the recruiter a cut-off test, yes I know all about the awesome ways to avoid wasting your time, but sometimes, these people just slip through and my answer to these people is, find a different work field. I know that someone told you that computer science “is the future” and that all the cool kids are doing it, but obviously, you do not like it. If you did, you would know how to write a simple for loop. Find something you like instead, be great at it, stop wasting your time, and even worst, others’ time!</p>
<p>Depending on the level of the position you are applying the questions will vary, but there will be something in common. <strong>Through the questions, I want to understand what kind of knowledge you were able to acquire from your experiences. I need details. It is not the amount of knowledge you have acquired that interests me, it is the ability of actively trying to recognize and acquire that knowledge through the challenges/opportunities that were presented to you.</strong></p>
<p>I want to hear about your personal preferences and opinions about software. Do you have a piece of code that you like to show me and take me through it? Why did you do it with X way instead of Y way? Do you have a favorite editor? Which tools do you use? Do you have a favorite programming language? Why do you like it? Do you have a least-favored programming language? Why do you hate it? Have you done a project that you are proud of? How was the code organized? What challenges did you face? What do you think you could have done better? Are you familiar with functional programming? If yes, do you prefer it over OOP and why?</p>
<p><strong>If your answer to most of the questions above – or similar ones that I will ask – is “I haven’t really thought of it” or some other passive answer, then I do not want to work with you.</strong></p>
<p>In most interviews, there are theoretical questions like, what is OOP? What is a class? What is polymorphism? What is a linked list? What is the difference between stack and heap? What is a binary tree? What is inheritance? What is encapsulation? What is a singleton? What is the difference between merge sort and quick sort? I will not linger a lot on this kind of questions but I will definitely ask a few. To be honest, I do not expect an exact definition from Wikipedia, but I do expect you to be able to give some kind of answer in your own words or through an example. These questions should be easier for a person that just got out of University and has all this theory fresher, but seniors, please, do brush it up a bit as well.</p>
<p><strong>The one thing that I will not be very kind with bad answers is recursion, especially from senior developers.</strong> In fact, I will ask you to implement recursion. The most common exercises with recursion are to find the factorial of a positive number n or calculating a Fibonacci sequence or walking a directory tree. Do not worry about the math in the first two, I will give exact instructions. In fact, you will not need any math knowledge at all. As long as you understand recursion, you will have no problem implementing any of the exercises. If you are a junior candidate and you are not very comfortable with it, please do try without whining that “I am not a mathematician” like someone told me once. There will be no math. I will give you all the definitions and clarifications you need. It’s just another programming problem you need to solve and you will have all the time in the world. JUST TRY.</p>
<p><strong>Although theoretical questions are some kind of indicator, at the same time it does not really show a person’s real experience.</strong> You could have just memorized all the theory, read all the “How to pass a coding interview” books and manage to succeed with a little bit of luck and by manipulating the interviewer on a weak day. I really don’t understand why a person would do that but it can happen. In fact, some people have the nerve to see how far they can go by trying to fool everybody even after they get hired. That can mostly work in companies with a lot of employees. In small teams the value of their work has a more direct effect and it’s harder to hide.</p>
<p>Therefore, <strong>the following part of the interview</strong> will be the most important one. <strong>Through real-world use cases, to show me your experience. To show me that you have tried to solve actual problems and you have bled while doing it.</strong> These can be from "What fields would a database table need for a user authentication system?",  “What is the difference between authentication and authorization?“, "Why would you use a finally clause in a try-catch-finally statement?", “What are some common logger levels?” to very specific ones like “How do you enter and exit the Node.js REPL?”, “How do you run the debugger with a keyboard shortcut in your favorite IDE?”.</p>
<p>Do you have some specific examples on your own? Even better, please share them with me! <strong>I always enjoy empathizing with the pain and joys that come with software development.</strong> I understand that you are worried that you might not have experience with any of the questions I might ask. I assure you that if you have actually worked on ANY real problem with ANY programming language, framework or technology, we will find some common ground.</p>
<p>I wish you good luck!</p>
<p>PS: I have humor.</p>]]></content:encoded>
            <category>business</category>
            <category>management</category>
            <category>experiments</category>
            <category>interviews</category>
        </item>
        <item>
            <title><![CDATA[My 2020 reads]]></title>
            <link>https://www.alexanderlolis.com/my-2020-reads</link>
            <guid>https://www.alexanderlolis.com/my-2020-reads</guid>
            <pubDate>Fri, 25 Dec 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[Flow]]></description>
            <content:encoded><![CDATA[<p><a href="https://www.goodreads.com/book/show/66354.Flow" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="Flow" src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QCMRXhpZgAATU0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgEoAAMAAAABAAIAAIdpAAQAAAABAAAAWgAAAAAAAABIAAAAAQAAAEgAAAABAAOgAQADAAAAAQABAACgAgAEAAAAAQAAAKegAwAEAAAAAQAAAPoAAAAA/8IAEQgA+gCnAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAMCBAEFAAYHCAkKC//EAMMQAAEDAwIEAwQGBAcGBAgGcwECAAMRBBIhBTETIhAGQVEyFGFxIweBIJFCFaFSM7EkYjAWwXLRQ5I0ggjhU0AlYxc18JNzolBEsoPxJlQ2ZJR0wmDShKMYcOInRTdls1V1pJXDhfLTRnaA40dWZrQJChkaKCkqODk6SElKV1hZWmdoaWp3eHl6hoeIiYqQlpeYmZqgpaanqKmqsLW2t7i5usDExcbHyMnK0NTV1tfY2drg5OXm5+jp6vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAQIAAwQFBgcICQoL/8QAwxEAAgIBAwMDAgMFAgUCBASHAQACEQMQEiEEIDFBEwUwIjJRFEAGMyNhQhVxUjSBUCSRoUOxFgdiNVPw0SVgwUThcvEXgmM2cCZFVJInotIICQoYGRooKSo3ODk6RkdISUpVVldYWVpkZWZnaGlqc3R1dnd4eXqAg4SFhoeIiYqQk5SVlpeYmZqgo6SlpqeoqaqwsrO0tba3uLm6wMLDxMXGx8jJytDT1NXW19jZ2uDi4+Tl5ufo6ery8/T19vf4+fr/2wBDAAkJCggKCAsLCQsKCwsLDhAMCgsNExcVEBQPFhISDhYSDxQPDxQSFBgTFhQZIBoeGRgrIRwkExwdMiIzKjclIjD/2wBDAQYLCgsNDgsMDA4ODA0QDh0UDQwiFBUXDh4IFwwQFhARFwsQExQLERkRHgkZDAgiGB0UDx0QDQwPFhALFBUjFhj/2gAMAwEAAhEDEQAAAevJi0LLZ05wNR821OcDUfBmi4Q6c4UUbBTTjEmmdbbVNXJRFrRLCjjEmjwNVEUE1Qha6CtMVKS6octSUfbU3qbapq5KItbbVttW21bbVttW21bbVttW21N6m2qauSiLW21bbVttW21bbVttW21bbVttTeptqmrkoi1ttW21bbVttW21bbVttW21bbU3qbapq5KItbbVsmKXo1ThqpWia2jVOTNTomttqb1NtU1clEWttqrA3Gqldv8AVVJt9VUm0mqybTVUgvdTV3E1ttTeptaqrmusKZkNLEmuL5DSrZeoe8g8n6GtqQg3ECf56ZbN5m7W2prCmDxoQh0wcjBdVdrVVm1mz5AVwJ9OlnYVTF++oVuXYEMp+kpm7clBmK9VdWfGNDn6K24+SOzPwhs+juqqitgLwVFJe7RUFyZ2xcFRquLsx1qFXy0y5mellhQLvM+VSSynTJol7irAzmCK5hcVNXJRlI22rbas0d6mS3AqAlwmgqezW21bbVttTeptqmrkoi1ttW21bZnTsCYpagRT2Wyad7attq22pvU21TVyURa22rbatkKoKYXUShVSYB6nbVttW21N6m2qauSiLW21bbVSMunx6uT3T4680TptZ8xun0WFhsvLttLttTeptqmrkoi1ttW21bbVonVttW21bbVttW21N6m2qauDUmFd6k1XepNV3qTVd6k1XepNV3qTVd6k1XepNV3qTVZ1Mjr/2gAIAQEAAQUCSlOOKHih4oeKHih4oeKHih4oeKHih4oeKHih4oeKHih4oeKHIlOCPYZ4ZroJFU5hZWt5qrmoErVTmFlZBCyTzFPM05hYWXmrvL7CPY7/AEmX07HNY5zPOqOZQcyg5of01fpafTP6aqMye8vsI9jsuNRkwnaopCOXNXCemE7IkrjI6KywW8ZWQsukjxkokLHeX2Eex/qOX2Eex/qOX2Eex/qOX2Eex/qOX2Eex/qOX2Eex/qOX2Eex/qOX2Eex9yo75JrUfdqPvS+wj2O8iF81MJDxnIiyqErwwmIpKHjK+WumErCJQyJUuMLH3JfYR7H8zVLqP5mX2Eewr2uyPb84uLJoConulZDlYND2iLl9lyfu0ewuWMKTdRg+8rLCrpZKbsmOO7fLvWv38PmXQabyAPKKRL4xMn6JoNFS+0j25PYEUy37vGFJjD5amhKgo8YuLl4j2iAXLaW1OXdxtN1LGE3Vsp1BRGOytVROX2EyGhX1RyCvMS+YllaaiVIfvDMyFI56H73Rqu1Fm6W/f5XJIJDihpVMkc+7D94uWLi5aJrlUiUpxMMb5UQZ5IZVCzyn9G/o2lkBT5Ci/dFP3MMWUT90t37rbvkwswQl8iF8uNyRRBNVuiy+XI+TK/d5X7sX7vGGEwvFP8AOS+wj2PvEGhBr1ct6vV6/fl9hHsffOgyDzeev8xL7CPY/mAlLwDKQf5mX2Eex941qKsg11r1tfFPH70vsI9j7+YeTzDyDyH8xL7CPY+6SB2wdHRNcHjr9+X2Eex927BKuZNhzZqc2d5TtK7kNK7jDmT8q2UtUX3ZfYR7H38E5Ypy/mpfYR7H+o5fYR7H+o5fYR7H+o5fYqXkp5KeSnkp5KeSnkp5KeSnkp5KeSnkp5KeSnkp5KeSnkpkl//aAAgBAxEBPwH/AHs0yA8mn3ofnac3+5SUZL/sl9z+hDY7v039LRhI8RdkvyLsl+Rfayf4pfYyf4r7Mx6UmEg0fUEMcW7xIf7H/eTLFIcIgf8AGfH9t3gf2n3sf9S+/wDkP9i/qZ+lBOfIfV3y/Mtn8y2fzb/0LHLQAMQafeh/iD/fvP8AT+v+wfe/3JH/AHgvvD/EHlJsk+OfH7V//9oACAECEQE/Af8AezQCfAt9uX5UjF/uYBOMf40X2j6GJ/zpiR5HcOvgbFkf5n9Xi9Zoz4j4mH3cf+MH3sf+MEZo+kn3Af6txaHoQUmm32MZ8iP+snpMB8gP6Ppx6H/XL+nxeg/2L+nh+VPsw/qjHAejtH5ND/Q9ttt/tf8A/9oACAEBAAY/AhoOD4B8A+AfAPgHwD4B8A+AfAPgHwD4B8A+AfAPgHwD4B8A0/Lto9Umvp6MdCqv2FPRBfsn/RfskjTg9Eq+T9hT9hRdMFD4vVCvhRjoVqaP2FM9CtBX/hn7J+H+j3LT8vueWP8AU+CWeHwY4F6Y/B60q/Ktf1PyL8nwTXyf5X+X/b831aU8vuFj5d8gqnD9Rr8tX+9H4P8Aea55fq4OvM/V5P8Ae+fp5P8AeD8HooUftupOno/aftPjR+1+p+1rV9Sq+nctPy/1IWn5f6kLHy/1IWPl/qQtPy/1IWn5f6kLT8v9SFp+X3ePelRX0/nC0/L7nMQNQnEfadfwYH5Qon+HV0IJ0oddCKNVTojpT8fP9XD7GE4aivX/AFp+J+LH5aHQnX9Vf637Kjpwr8PVj2v9Gvz4MVqfZJ18wXxVWg8/zV1/UyR7WXr5Uo86eyOiv2dPnxZy86fcLT8v5riP5otPyZ+fbzYZZ7VL/q768GCx3I+4PkzVXn5M6H/b+T6UvSjOqmdT+L9r9bHm+qGvydFhcX9of1usakq+Xb5fcH4dgy+pdB5AejNddfN9ID4B6ss9gw9QCyQnBXqjR/RShY/Zk/uuk9usD9pGof7yn9rRjEg6+TP4diyWWGdGe5ehP4Pifwetavgp+wov92fxf7pP4/6D/dx/i9Yoa+o4vgUn1BfRMofM/wB2r/eV/B+2r9X9x6SK/V/caQpSiDxFPgfgxp5duP637Yftv85f537Eh+1/u6fMvh+D0Cnr/C9aduBfsv2Ev2Q/ZD9lLqEir/P+t8Fn8X7Bfsv8oeq/wetS9HwH84WPl98/2n541fnX9dH0hX+36Pzp8v8AbLGivLT/AG/63rlX8v3y0/L+Z/B8DTyL/Cg8/wCZLT8v5kfyXxNPL+aLT8vvn2q16fk08da1ajr5Uf5sq6/J/m6tPlTz+1pPzoxTLh1V++Wn5fzHmxoX/V8eD+T/AFfb/MFp+X3tfPh24vU1dK68aP7auv2/zBafl96Glfb4jy0LAJk9tQyp5fHRxZLkTULyoNeOnk4s1rTVBJoNfh5MKNc+QTWnn+DOqlkxZio/N+DXRauA1x1B/D/hmdV6L408qfL+p1XlXX2vvFj5fzAVQVHAvKgyGgP82Wn5f6kLT8v9SFp+X+pC+JfEviXxL4l8S+JfEviXxL4l8S+JfEviXxL4l8S+JfEviX//xAAzEAEAAwACAgICAgMBAQAAAgsBEQAhMUFRYXGBkaGxwfDREOHxIDBAUGBwgJCgsMDQ4P/aAAgBAQABPyFrrMOr/wDLv/y7/wDLv/y7/wDLv/y7/wDLv/y7/wDLv/y7/wDLv/y7/wDLv/y7/wDLv/y7/wDLv/y7/wDLoEhccHu/oP8Akuiep4pBZDkH7efq6CZz88XhiN3/AO3HpnZ4j56s6iPgkjy+yug4QhsvM2JZSsw6GP2cVB5Nif7+KlIEhJ9b8H9WSgbrjP8AfVBF9By6xqSaJA67l9XF6uj/ADuq0H5Hr2sSy4xI9/8AX8L+b+g//ByeYjzU+T4J+dvd1rLzPD6oDKKdXmH/AFVJBE9up7+qpobER47bBJktnR60TOkZLk35px688fztmfwYTkdz/VnuceffinNA6ic5f4VOT1A4ff8A+D+F/N/Vf9lxMRiezy7Yc4ueyfjPxRYIGu6YnvDdcJyAYMZ9dN0CJ4IOnD793WciGO/z81KgKII/P5oWcJOtjxNOedQMoPHPmd3nJbCYVl5PP+urDIUER1M6vnLnhldfDoLNruSGRERE2Yg44Ij/AL/C/m/oP/0T+F/N/Qf/AKJ/C/m/ov8A9E/hfzf1X/6J/C/m/oP/ANE/hfzf0H/6J/C/m/oP/wBE/hfzf0H/AOCSY75ij8B+6oEuH/BGYPMt/FgYUnr/ALP/AAThH4skx34sjMdc/wD4P4X839B/+BoE7pjOR8IT4ivxoYhh3xbs7UxR5LA2XzznubPMLTz6JfdAsLjUmd2ZdOnmwM1JoGmyXv8AxFmT7XnpBLZfJ3M9U4/XleNlfMcc5X0r/vWN8fxQtkl+OvwoWVKSZHvdcx1NlmtmbZebVDN/NcmXQnzG/h//AAfwv5v6D/8AKi5/JReEf/yf4X839BZd/K77u/4bLt5rPZy2Wk8H/DbgLzeHQ/65Dny8Ub9FRXhL1NlVZdZv8x/VUA8t14naIZ4g/dUfCsWjrNWNkyPP9q89PaWEMf58XywPdn48Obi4VFRPl0/mmf5uH9Ldunsj7FZKBov/AI/f/T/nK8gf1QmY6JvzKlg8FM+jb/G/mlD9iPDXOPV5B28v5yvzBt8NJdCIi/tN/VP+cryVwnvaPAHsqfBBpT9XqK4H+lk73+wpWD4/5OUPDydDZd+K4+Rqle6d+gv8L+aEoOLznZpMni+pvy3kjy9WVgeahZyBCxlfK/VGYB9TH+7+pCEUfH2OsYAPk/1UDOdcUcx/kCL700J/oqxIebr+i+YfqkaPN4o4emQeTryKyccKlVHfdEzj5ochfdB2fgmvcP4iz4fkUXwn/HVgGRedr77/AJ8X+4aJw/P+hRCEfpf5aHlX4Aux1e/9UD/doB/RUJc/X/IX5b6qAARCfi+xc8ez4/5km/1/7ovBPlKJy/cv9Ub+h/umtwef/L/Ed0Pi2WSQ7eD/AKIkn/4/4X839F/+NBw7PPEm0iPIjXo8M82ID5vk/mKjrA2J+DtpYg8ZCajiOJ7XmtOCn9Jx7qOl4x/kD8//AI/4X839B/8AkKR1gm7JuflPj47sOWRL5I399U4UZf23P/5P8L+b+g//ACRMFZLz58/1coxCDxP+ZZZ2WN7ziP8A8n+F/N/Qf/j+CUDjh9fM0lTvk/5l7i6h/m3XKRHhy+ojiko4cv7P2fqjJMkPCYybP/2Fnnv4/wDx/wAL+b+g/wDyNWQYu+v3VyOZiE9LnV7N8eWseKPPJCU+P8/d2jddden/AOR/C/m/oP8A8SBIOEv6/wCR8uz3vl7aORRDJ+Eq/Z4xPMzHiwglc4P4+KE27xBz8/8A5H8L+b+g/wDxD8T0p8jiPzYWNwhw4/rg2yBsBriCR/iVgDZ2ZIwkmk93AZ8s6I+T4/VTIYjweBA/FQ6YYaZGxnnvKkwoCfLoEmE8r6UDyAkQxOdH8f8A4v4X839V/wDkKsY07AfFXLiPJjx/2P8A8j+F/N/Qf/on8L+b+g//AET+F/N/Uf8A6J/C/mhmdnd/+nf/AKd/+nf/AKd/+nf/AKd/+nf/AKd/+nf/AKd/+nf/AKd/+nf/AKd/+nf/AKd/+nf/AKd/+nVkKHzf/9oADAMBAAIRAxEAABBSxAhwAwAxjVQQRiRCwBwBVSwywwwxyyhVTzzzzzzzyhVTzzzzzzzyhVTjxSjiyjShVCAQxwwxxiB1aYNkRUYqFB0ZAEnBJj2579ozXhRRtrDW7MAwhCDhTyhVTzyDAABTyhVTzixQHzTyhVTyirxWJLyhVTywxwwzzyhW8888888889//xAAzEQEBAQADAAECBQUBAQABAQkBABEhMRBBUWEgcfCRgaGx0cHh8TBAUGBwgJCgsMDQ4P/aAAgBAxEBPxD/APY0vSH5/pb6H7EBiCZBuH2s7j/Ghun8TsF+pu/4vgT+L/yo/wCdbdfsW36f6njP8jxdkD/X+zdWB9cc/pJ/oIR5Dxu45/U+1wOv6/O57L+kcpb+vsbJfDfmh++7J+U/l/2/4j/e3d/0n+Nle/3pf5PtsB0i09q/y/8A5mfX9H6P/jgYrT67q99/P5cTscXb8dnGdPju704LLwofT7aTnvOcuRfhOcdAcdfORBAFJ8AXr+Nw/wDyv//aAAgBAhEBPxD/APY13EV9cfmc/paLv8qE5XyfpH1uyT7/AB+5x6+qDj7+Gn5Srgb99t7T/N/6PgA/R/yea/5W/wAp+dwap9N5vlH6/eN/onYqfnhty39ldoB+f/Sx7X81/m5dP6n91gvq/m6T+pvsoLoD75YfT/8ARjH6/X0u25+v0c30f1sfT/8AL//aAAgBAQABPxBzERWSscq8/d/+U/1f/lP9X/5S/wDyn+r/APKf6v8A8pf/AJS//Kf6v/yn+r/8pf8A5S//ACl/+Uv/AMp/q/8AyH+r/wDKX/5S/wDyn+r/APKf6raoYIDwPF/zHj/k66Pdr3GxQwnu9OXEsF8/wukoGCDSjXM0npNqckAS8TLAiRI0857qkiXAlakBEmMXyWJgJyxgyJY6ENEniu5CGkAUcuPJxxRrRSSYKCEB2mx/FCEZhPGwyyflEj13TiIGPRhyjk3ZhQTlMoCCPgNye0y5B7ypLEkBQcpOUZRMakEQFKAQkcuJ8tFTUKwLxIRJsMn1VzJuzwk8nRGIaEnhAwIcnceUPXun/P2P4L/kPH/d66o8R6rIIEzuszFSWJ+SA50ddIwMqIWocvCSMQIPHHdBIBKcNMEHIwaDTokhwhnwkuOWLh5A6H2HmODjOaH6Hm5kc4lDzyzRpHmDHCuBgv4gKQKzBToMgk1Wc8EIUlVZRl0bJ1uKSIoQnhmUTxzDPqO7LPKJCU8IYqwkXIj3XhKcrE5JdXMCInv/APB+x/Bf8J4/7hukSIpYyA7KHMxC4kGEYYDDBO3RYfFn8MxRCYAFpk8OQw3kI7MCsqiw8mszTjAE08GkjLUQPi4ZCYQQIR6x2iGe+qo6Z5pB27l+ClAEE2Wss3M8DyB93MZBNCVIVHYJD5W6caOJ5GXANiPAVmp8RBMlgWQMcOrmfdBWwgzgg8KtcoWKyiEBmPleaxIzkYcKPkWZ5hqkYwcQIZZVX/v7H8F/yHj/APRP2P4L/mPH/wCifsfwX/KeP/0T9j+C/wCE8f8A6J+x/Bf8x4//AET9j+C/5jx/+ifsfwX/ACHj/wDRP2P4L/kPH/4HnDHJsTExzE1aEpSALIomPSP4aoQDVcA9zxZKsHqIie5SVAAXBSX4ObJZqLJQZImJUn6rAkSFJbByx6k/NHBCwgOkkk/P/wCD9j+C/wCY8f8AWnIosJPlDwYteKak+JUiTKJWWD3VBhMlFxiAvETzsigtKTTCJGEo+XJnafRPp6QfYHwFkpEzRMGdJOAQQAgmaBSkgl4ZEvJ5+hQ4uMcMzxlGALICRLKyBEy5WbSMRyBBIRQCIa5IIkWQoI4jInaMUkbReAQx1hAzYSrz5zcBrmnk8ITZgpCyKSDDAPX/AOD9j+C/5jxZ/wDbJZKg0jzcsFguVkQnuR/u/gOEuXLnVgufiz/yavyfwX/MeKuR0Nf92fPPIK/3Zs9JlMzHDzN459ZfPzfqQmfL5f8AiUQH2+A+a+ye0QHz5fNgWAl7Al+4H90gWMTnpP7pByID5Xt7KwJdlwseTj7swXQ68TDO+KoKWBM/uvMBcnzPmpLL0ZftzXBUY88G3gFPBr3nn3QI2E3wCgtQOTrizeBJs++j82PqYEFKPgV9Jyz+bLCpMHip6liwhy8aVUfhB5fC0/kcv9UJkgxXaYNBNeBFYhfogNMBzHnfIE/NYkCEqnYxUTHMjfH3ZG6lP2o/isIx/nJZyXTfl1/daDtP4LIpxK++P3VejO/K/wCrDHiT+uP3F/c/gu7rnbo6xOJjFdyDRv8AofRWGeApwjjooPB+H/yxDEmGWZPV/wAl5b/iPf8Awp1FPsZ/uz0wB+nFaKHISfsrDWGUnfMHeRzKiz4NSPXI/P5stBJzY8oNDxt4FTEJ9YRPnaKiAhIiTh8tdsIMU+9Y/VJQf+of/LGTiI+IiywuIb9t/Y/gszkAHmBQo8As0Pu81cc+6dg/VE7H038eZeWsphjSeNqVIBTcyOGQ7GlDPHxAf2zYixmJDwpqqC58oD3yrZ49iVn0qiTIgEUccRpr+qj5JDad8qvF+iB9NIsq6D1z+sXlg5CbfCR/i/w1F+ND+aY+QAzvqxX1BRA+g4HdR0Mkx5Jo4SlYQbWwTSHg89t/ABFvDs96/RF0mFnAP3WCXPx/rq0erEP6XDKaYbr8tFJRxma/rX3eyZ8Q/al4tPUH5mf3YIPsP4Bfx0Afomhpp2GT8yJvBF+R/loAIwAz/qlg0RgM95cUi+j/AFQohiNPHHiu13WEllG+FPuzMBpAKEYRwwv9iC/mnBX2g/neOH0v4m/4QDwD+bxUHZ/an+Kdl0r4fAocAw8oEzEOBPq8HPcT+2gDAPUVNiEnkY1j4/6CSR4af9j/AJF/a/gp/wATqx/yP/wcdMHB6h6ywshWAS7RJNccLNdIoEMGJIIvg5n7sIGcpEyUhR/tZS5sZfk7U8V8NmGO6E1ABIYLK7kcBKZTSdEzc15Z0auEj4px/wDi/Y/gv+Y8f/jikiAyDmDWLnhxQxksA53h6SVMgSwnCQAyIF8rChhWcpEomAAl8cc02x/+P9j+C/5jx/8AkMPJNGEoJkzuPMJIPEF5FEGkcGMSwKS4qjRGJcM1LpJ3zRiBdeJ//I/Y/gv+Y8f/AI1s8tJ/Bo57j8VinAnYiFJOIMRBVEgObhQJSOXSOZSIQ8kAcJJIiEjZ+6nloCiJL5CBBpIlzQnV8Ga9tcBIniwcU9JTmI//AB/sfwX/ADHj/wDISAkCjkUMAvwk2aYgovRCBgy03cpyAEGEbeR7H6ni62RFHGJMYUkc8KOYxgPwT2n1xzlNPP8A+P8AY/gv+Y8f/iGV1AgV5iTr8f8ADIY4KAEamCQQhqjsggD0TzKr3WMWjFYezWTD1VTnSEAJgxCJQZzgzYQiLeiWpImF6/d6/wDx/sfwX/IeP/xTLR8li5oR1w2onFxkygQ0HlcbWytrgnTCJJi8DNgUw3qLgMYCOKQB7hw0Uycp+NABumC+ByeTL4okBYqwAhYTAXaOqKjeXSMo3vkiWVwEiAUoQEhHChSH/wDF+x/Bf8J4/wDxJYPUVtxHPIC6Hv4qiKUGHqHwvNi/j/iCIz4oR/8Ai/Y/gv8AkPH/AOOLFix/+V+x/Bf8x4//AET9j+Cr/A6//RP2P4KFBAABYT1t/wDqP93/AOo/3f8A6j/d/wDqP93/AOo/3f8A6j/d/wDqP93/AOo/3f8A6j/d/wDqP93/AOo/3f8A6j/d/wDqP93/AOo/3f8A6j/d/wDqP93/AOo/3f8A6j/d/wDqP92Sscik/C3/2Q==" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/26156469-never-split-the-difference" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="Never split the difference" src="data:image/jpeg;base64,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" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/12975375-the-advantage" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="The Advantage" src="data:image/jpeg;base64,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" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/324750.High_Output_Management" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="High Output Management" src="https://www.alexanderlolis.com/assets/images/high_output_management_andrew_grove-ef2a29c3a7b537ad2134475c7a25aba6.jpg" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/1169674.Conscious_Business" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="Conscious Business" src="https://www.alexanderlolis.com/assets/images/conscious_business_fred_kofman-23386ae76cccd3b3b1c53409935ff111.jpg" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/944652.Poor_Charlie_s_Almanack" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="Poor Charlie&amp;#39;s Almanack" src="https://www.alexanderlolis.com/assets/images/poor_charlies_almanack_charles_munger-9d3de8ff166b6817755cbc5ec51c70bb.jpg" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/36204378-the-book-of-why" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="The Book of Why" src="data:image/jpeg;base64,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" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/39996759-a-philosophy-of-software-design" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="A philosophy of Software Design" src="https://www.alexanderlolis.com/assets/images/a_philosophy_of_software_design_john_ousterhout-61297ba407a2497e1a1b24f3fec931ae.jpg" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/36343268-a-theory-of-human-motivation" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="A theory of human motivation" src="https://www.alexanderlolis.com/assets/images/a_theory_of_human_motivation_abraham_maslow-8f6e908a2bbbd830741be4437fe22eb8.jpg" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/8553359-making-software" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="Making Software: What Really Works, and Why We Believe It" src="https://www.alexanderlolis.com/assets/images/making_software_greg_wilson-ca1f3da1841678d17e63941652cb9c4b.jpg" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/38732242-elixir-in-action" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="Elixir In Action" src="https://www.alexanderlolis.com/assets/images/elixir_in_action_sasa_juric-5659adc04bc3feadcccc3f304f500527.jpg" width="167" height="250" class="img_ev3q"></a>
<a href="https://www.goodreads.com/book/show/26030703-disrupted" target="_blank" rel="noopener noreferrer" class=""><img decoding="async" loading="lazy" alt="Disrupted: My Misadventure in the Start-Up Bubble" src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QCMRXhpZgAATU0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgEoAAMAAAABAAIAAIdpAAQAAAABAAAAWgAAAAAAAABIAAAAAQAAAEgAAAABAAOgAQADAAAAAQABAACgAgAEAAAAAQAAAKegAwAEAAAAAQAAAPoAAAAA/8AAEQgA+gCnAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAFhYYFBgUHBoWHBgcGhwiJx4YHCAuODQnMCU2LCwiNiwlMCUjMiwwOi42MD5OQEk+OmdQRFguREd6UnxmhlpSdv/bAEMBDhoYGiAiGh4eIiIeICciRTAgHlIyNDgiSRQ4Hic2Jyk4HCcuMhwpPClJFj4eFFQ6RzIjRScgHiM2JxowNFY2Ov/dAAQAC//aAAwDAQACEQMRAD8A2gTj8/5mjJpB0/E/zNLXxdSc+afvPf8AU9KKVlogyaMmiisuef8AMyuVdkGTRk0UlHtJ/wAz+8LLyFyaMmoXmRJBGQ5dgWUAZ+UYBPHpmnxyJICUOdp2uDwQ3oR1BrR+1sn71u+uwvd8h+TRk0uDSVHPPvIdl2QZNGTUcciSM6rnKNtbPr1xUlNyqd5fiL3eyDJoyaKKXPPvL8R6eQZNGTRRRz1P5mFl5Bk0ZNFFL2k/5n94cq7IMmjJooo55/zP7wsuyClpKWvZoTlybvfz8jCSV9kf/9DZHT8/5mlpuVUZYhRnGWOB1PrUZnthnM0XH+0P8a+MnTnKU3GLfvPp5npKSSV30JqKRWVxuRlZfUHP51WS7tJHCRyqzngKOuay9lUd/ck7b6dB80e6LVJmqk97awPsdi8g4ZUGSO/OOlNgv7WdwmWjYn5N+AD9DnrXR9Vr8vP7OVu/l+ZHtIbX1JJA4vIpAjsixSIzLj7zFcDr7U3bKftEpj+aUxhIgcNhepYggZPpnt1qKfUY4ZXi8mRmQgE8AcjPc1ANXhyMwSc9cFT+m6vQhQxVotUlbl77xvfa/wDwTFyhd6/8OWVik8qJHjkIW4YsM4AjOe277uDgDPHWprRGjjZGV1w8mzcc/KWJGCCeMVBcXypbxz24WVXfYdxIxwWweCe1S2Vw91AZHVVIYrhfb6isakK3spTlFRjzWffmv23X5AnG6V2JE0sb3DGF23y7ox2I2qvr0GKEiIuZFzmNT5seTnEjArtODyAeR9afcXcNsUEu/L5KlVLdMcH86rC+sIiAqyASjzcheoPQn/PanGNaS5402+ZWT6aW/K34lNxu9bEkMcqvAWVgyBxdSE8SEjHHPOTyOOOlNtopI2tz5cqsFlWUs2eOqgnceT2PbmkXUrEn77j3ZSB+oq09zbpD53mAxZADLzyeMHApyWIjpKk1fbTrr/mL3P5hlqjojbgwB24Z/vn2kwSCfUjrVqqX2+wP/LVR9f8A9VOF7YswVZlLHoO/8q5qlGvJuTpTXyZqpRWl0W6KqfbtP/57xfnQL2xPSeP865/q9b/n3P7mVzx7ot0VB9ptQSDNECpwcnvT0lhkOI5I2PoGH6VLo1Vq4SS9Og+aPdElLSDqRTsV6uG+D5/5GU9z/9GfVRmxIPIEgzn/AHqzbOxjuldt3llTtwqjHTvmtbUhnT5f9nB/Js1V0Y8Tj/aVv0ArghUnDDVpRdpKpv8AMtxTnFPa34lGAyWN9tyAAwSUL91lPAOPXOKS0BTUUxniaTp67ZOlOvxt1Fz6mNv60ISupggcCYnHswP+NesmnBztZyo3l2uo66GGzt2Y2xjSe9CTk/OXYjPJYHO3P0/lV250x2kH2UoseOdx5Vs5+Ud6rX1s9rN5sZYRO5aJx1Vu4b0BrTsr4XGI5tqT9F/uv/u+9ebWnV5Y1qEuaHJaUOifXQ1io3akrO+jMjUdyXbluWCIzkd+oJ/Sti8trc2Tskao6oHRl9Rzz7GszVkzfEHjdEv82B/nSTajcSwGEoqLgBmUnJHTGCMfXmujkqVIYScGlb4umnoT7qc00VVYm2kUfdLRycf3iQv8q2NHObaUekv81B/rVL7O6aZJM+MyNHsA7IHXk1b0f7lwPSQH/wAdFRi5RnQr26T/APJh0/jjft+AzV8b4PUK5/VaypyRHF7QKPx5rT1jHmwjuI2J/MD+lZ04yVH/AEzUc104RfuaNt7Oy8zOp8cvU25dNtjEzRB0lC7gc8ZwOtZlignka2csI5Rvfb/eXof8+lXJtVjaArGhDMuwljjHGM1HpMLGQzHOxVKqSMZJwePUVw3rU6NeVZ2d/wB3fe99LGjUXKPKvUrX0EdrIqRlipTed3Jz0rQi0+BYluC0m4IJNvGM7Sf61U1gg3OPSLn8Sa284sPpB/7LU1a9VUcK1J3n8fmrIainKfkc5ZQrcz+UzMqkFsjr+taY0iPI/fynBB6L25rO0+aK3uDJMSF2bQQM8/hXQW11b3EhWFiSo3MCO3T+dXjKmKhP93dU7K7srbhTUGulznBGs96Y88SSMu7AzxxnnjtUt7Y/ZNjK5ZScK3AcN7FabZ4Oop/10f8Ama09Z/1EXvKf/Qc10zqzVfDUr3hKHvK3kZqK5Zy6p6Fuxmee0R3wX5Rz6kd6uYPpWbpP/HmP99q1K8lwSlVS0XOzsWqjddD/0r18M6fcD2Y1Q0Zh5kw/2Af1rTuButJx6o1YWnXEVq7NNu2PGFBUZ569q82lGU6GKgld8+iNZNKcG+w7VRi9J/6ZqfyqvMSt6e2JYT+axn+tPvJheXAMasu5RHGD1PP3sf56UXgCX7hv4TET+Cop/lXsUU4whTl8Xs3deVjle7a11/U6d/LPyuUYPxsJ6/hXN31obZwYmPlM37sjqjdQM025SWyvRKPUSQyH7vIwVJ7f/Xpbu/8AtYSNECgHIVcElse3avOw9CdOScJc9Fr3+yZtKXMnde90G3kjTi3lbG9oikhHdlIGfxzV+6QSaRBMFG6NEY47qeD0qjdRPBDaq4JcpIzhQT1KkZIHatqzUSabHE4PzRmNgR7Y6GrrVYwhQnBq0amuv2denzFGN+a/b8TNhlD6VcRMeYcMP90nd+nT8Kl0b71wvsh/DmqlnFNHOY3ilMUgaCU44K9mznsf505bW/tLhTGGIBx5idCmcncOo/pVVFTlGvSU43qe9H/hxq65JW239CTWf+PiP/riT+tUpx++UdsRj8M0+aSW/nwi5bHlqq9FXJGWP6/hRebU1BowR8piT8eK66SVOEKba5lB31MpaybXVnQ/ZLMEHyUzgHP4fWpxgYAAAHQCl7/gP5UdTXws6k5OV5Nq/fTc9WKSS06HN6uf9MJ9IU/m1bkny2Te0H/stYOrFftx/wCucY/Vq3bk7bGQnp5J/wDQa+gra0sCl5fkjjj8VQwtPt47mVo5d20JvypxznFbttaQ2rs8Zky4CtuPbk8Vg2NytrI7srOGQKNuOOpyc/WtVNTt2cLslBc7QcdzxzXTi4YqU2oJ+yaV+2hnTcUtdzKsADqCf78hH4E1oaznyoBn/loT/wCOkVl2kiwXQlkztUyZxz1J7Cp9QvIbjZ5YfYmSGYYyT2AJzXTKnN4jDzSfLGGr6bEJrkmureiNXSuLFD1yzH9a0sj0NUrKJ4bOJG+9jcR6Z5xVnmvJ5ryqtbe0Z1raPof/09cgMjKejbgfzNUV02wXgCQ9AMsa0B0H1P8AM1VuNwnsxk8yvu9/3bn9K+UjOqpzjCTjq27d0n0+R3OMbJtJkkNrbwEtBGoY8bjyfzND21u775IlZzyT/Liq8+7ddYJ/1kGMfVc4pFAW4R1OHe7eNvdNhyuM8AY/StFGp8XtJXa/S7IutrIvlVK7HVSv904IH50ixxof3caBvYDP4VnqqyGGOU5jMc75yc7g6rnI6kA02YbpE25dTHAq3DNgpkth+MZ3dPTnkU1Tna3PJLe3S1/+B/w47rsjUB9sn9aOSf5YrOlJEskoJMsdzDEp7hDsUqP97JP40RHBhyzYM9wCfTBfBPsPyrP6u2l72m9vlf8AUfNvpY0if/r4o5Hrjofp71QslWOR4yAsmxHYpyki7jiXB6OejZ9KrS71a6jUnM77l9F2gZ/MAfnUqh7zipPRKz9R8+mxrBVUfIqr6lQB/KmmONjykZbrnHP51TkUu86qV3b4WWNz8r/uwfLPpu/oKgnkQ28IjFwNheUIvJDIdvluRxtyf0rVUZOz53d7vytdfmS5Lsa3vR/MVRlC+bJMCQ4lh8pgecELlfcEE8Uo3faXhzxCJZMezD5f1J79qy+r/wB62l3p6f8AyRXP5Fh7e2kbdJEjNjG4jn2qQorrtKgqflKn09Ky2jzpybVELymH50OWJJADE/0ppxLazu6/Otyi89QcxxsBzwCB/wCPVr7KTt77spWXk9PPzJ5l2Lf2GwzkwRn60q2FkrblhVWHPHUe4pk21BOAQoVoFAHYZAx+NMQH+0Txt3TuRJu5YBF/dhe3r+BNar2zT/ezS5b/AC08/Ml8v8o86dY8/uyc88N+NSR2VnCwaOIbh0LHNQQoGtZWIjjLiRROTkkbj94cbQOh5qa12jzVC+UysBJEDlFbaP8AV4AwpHb1NTUnVUZfvZO2lvL+n/ww0o3XuotjJ680tIKWnh37nz/yHLc//9TZHT8/5moJ/JJiSWPfvZtnsQjOeR6gEfjU46fn/M1DLGZGiIIHlliQe+UZP5mvj00qsm3bV/fZno20XoQQPb3JicRDf5e8EnJGDtAb3GOPSlDJHLPIYFV4gWeQHOd2B8o7ZxzS29sYZd7EEGGON/8AfXqR9akeJnWcZXEyhR+Gev51u5U1KSUnytd331MrMjk8hl2eUjhJAijIC5K7/ve/cVLLsWB5DCrHZiRAQPkXLbc9OM8VE1ttVljiiKmVZVjPC4C4OcA855qeRDJA8ZwrOhUdwDjHXipk4Ll5ZaX116aeY7MiHzzRN5CmbYHJLD5QflBHYkAn8KbG8PmvKkHaQLMOQWU/OMfw7sdhzinmJxNHIEjkMaKgLHlSOpTg4yKRIZkDRqy+STIQf4mLksAeOApJ/Sr5oW36W3e13p/kTZ9gt/I3lIIwmY0kLDphicL7bcUK6y79sGQC4BJxlgdrA55Ge3rRb26wyEqqKjRojqoxl1zknHBzn9KfFHKLhnYR8gqXXOXH8O8dMr69aXuOUknfRW169R62sQF4XSLzocfaXA25BxgAByc8YIAyPUU9ZI1k8pYwoYmGNs53HG8qw6gHB5pGtndEVmKmOIIoU8eYGDZb1HAqRYSLj7QFTezHzD3KEADnHUEVbdJJpt9dL6Xvp+QtSO2WKbE3khGViiZO4gqTHn0yCDg+lK88UeZDGSWjlZ8dSsfUc9etOhg8uNVd2ykjuNpIB3OXGcdcZ5qOSCZw6koo2zLER3MnOWHtUJ03J3eiemr+G/3lWfYfBHCQUEQj2bWMedwH8SlcE4+naohPEY5C0OAwNwQDkNghcsf4WyowKuxqkaBEVUA+9tHGe/61QjtHVZFAiTehT5P423bg0mVHK/1NOMqbdS7drq2v9dhNPsiSVoTNzCHlSRYkLHAyV8zLEnHHvzmke4jUqyxF9ytOzZAIwRGSB/EeeMdQKSS3lcMzJC7PMJHib7hAXZySOfXp2pj2bnyhiF9kbRjd/ASwYGMbf4BwO9ar2Vopy9denL/n/TJsyfbbJ9oxEuEz5g7MGG48c9fpUluqiLKxrGGw2Fbdngck9SQPWmNHNmdQVKTDiQn5t2zbyMeo61JArJEEZI49oAATp0xnkDrXLNrllaV7tdeljRdCYUtIKWurD25Pn/kKe5//1dkdPz/maXikHQfj/M0HOOMZ7Z9a+IqfHL/F+p6i2+QtFUzPMu7MZbBIXaMZHGPx55+lI1zLjAhkVjwrdQD8vXj3/Sq9jPTZ/NbE8yLlLVP7VJgZhfftJx2yCFweO+c/hR9pfH/HvL6AHrnGeePfij2M/L70HMi3j2GKWqRuZGX5IXRs4DN0zkcMMcZz19qtROZEDFdmSRjOehxkEdjUypyiru33jTTHYpenI69qKBWSdrNdCxzgZDDow5pmBUi8gr+IqPp1rrqx+GotpL/ybqv1Movddn+AuKPyoo5rks3e2tvyNAxVae5hgHznLHoi/eP+FSTSCGF5G/hHA9T2H41zjMzsXflj1/w/Cu6hQU9ZfD+p0Qp8177IuSX90x+RY4x+ZxSLfXan5mRx6EY/UGqyI7nCYz3HT/GhkeMgNjcece1ex7KFrcq/rz3O32cNrL+vPc2Le9imOxx5cnoeh+hq9j865Yjjr05Hsfat2ynM0RV/9ZHgN9PWvMr0FFc0duqOSpT5dVsXBS0gpa2wy9z5/wCR50lqf//W2R0/E/zNLSDp+f8AM0tfD1Pjn/if5nqLZehXllljlwqs6FMgjs2TUa3E2QnlMz8jdjC5BYfyH61admWNmUbiqkhfU46VW+0z8/uH4BPJ6kDoPl6nPFdUHdaQi/O9jNqwC5l2AmCU+vrnnoB69j6U8zOI9xifcXMZA5wMbt307fWm+dcA7ih2g4ZV9228FgCcA5wKja8liUtLCwC8Fhkc7sDPHSr5L29yL125upJIs8rMgMbplgJMjI2dOPQ8gmrWAOAAAOOKbGXKBmKktz8vpjpTq5Jyvoklbf1NUgoopyru4qIQlKSjFXbG2krsFOG96nCIevXvQEAwMfjVWG9tp7h7ePdvTP6fia+so4ZRhyzSlrdeT/pHnynq2tCyYk96ik2jAHQdasHoajZNyZA5FTWpRVOfs4xT9Ps9Rxldq7djF1Q4iiXsz8/gDiqP2abdtO0Me2e3HP6/pWjqSsbcOBzGwY/yNZ0e+VmDysAFyp/L2rlw1vZrvfX1Pept8qt31CKJ/vI6gklPXBB2/wA6fLE25N8u8MSuRjI46k9DzTTHGhO5nHJHBGfXOOODQY4BxkYAx1zzznv9K7TS+q1f3DhBAE3byzYztyOvXBxU1jtS9Kp91oufqCP8aquLcIQgBk+UqffjNWdNXdK8n91Qo+p5Nc1drkn2t+JMvhldv0NgUtApa48O/c/7e/yPGnuf/9fZHT8/5mlpB0/P+Zpa+HqfHP8AxP8AM9SOy9AqJvO3HZyNuF/3upzUtFRGVr6XHYhYTE5G1gADgnjdzkigG4OBIkRHGeffmpqKv2n91E8oUUhwBk/Wqsl3bxTRxOGLSYK46D0zW9PDzq6x2vq/MTlYuKpbgVZVQvTr3oU8dvoKdnFfS0cLCnZ7y7nDKbba6C4HcfjUaRQoxZERWb7zAcmpM5or0DEKb0bHrTqo3F9ZW7gSSjcPvKnzEf7wXOPxote6tcL2HOqksrDKsDurnLi3e3fkExn7j9vofTHvXSvtbbIjblcAr9D0qMgMMMAQeoNfJObo1Kitpfb5nsUqjVmtU915HL/Sitx7G2Y5XcnsvT8qaun245Jkb2ziutYqnvr6eZ6Hto22dzIjjlmYJECTnn0X3NdDBEsEQRee5PqfU06NEjXbGoUeg/zzT686tXdTRK0bnJOo5eS7AKWkFLXVh/g+f+RxT3P/0NkdPz/maWkHT8/5mlr4ep8c/wDE/wAz1I7L0Ciiis+Vu7XTcoKKKKkCGcsIjtxk8c1GqDaGbYzKNqHuBVnAPUAikYhUJx07CvZw+J9nHkjFNt9zCUb6jI5grmNiQwwSD/nvV3dnr3qhG+TwpZmwWLcAfnyamAOeSWJ6n+gFe1UxUaaV2pStstrnPyX2+ZYaREGWP0x6/wAqA8jcbMZ6ZP8AMVVkdI4yz528AD1YkKB+JIqv9sm/s95HUC4DNCu3oWDbC3r8v9KrD1nVTdra2T/r11MpxUSvqF5IWNvCxwP9e69Sf7orFxtPT+v55qYARjbnLdWPqTzkmm5NfQRiktjgb1LmlMiXEys+PNVfLye6liV56deK3a5e3Ft9rjedSVyAMHADdQTXUnrzjNfF5jBKqpJNXWvqerQelhKKKK+fO4KKKKAAUtIKWvbw3wfP/Iwnuf/R2R0/P+ZpaQdPxP8AM0tfD1Pjn/i/U9SOy9Aooorai1zOL+0rfPp+QnsFFFH8651CTfKld32KuFFJn2NLVunUi/hYXQUn4gY5JP8AWlrI1KadMRsNkTfxf3jjOPwrsoYSpVkk/dXV+RjOagrpDb65DyxxRk7EljLkdGO9f5VDM20zruZWeZmB9F7gduTVQBm2tuUBCp+uCGGPyqWaTzXLeuSf58V9xTowgoxirKPXzPHlJu7f9Ihyvr9KU+p6dzSHeOd3bgGoHdePMz67PU+hrsk0jNRJGKBNzchvuj1+lb2mzGW22PzJHwf93qPyrngCTvfG8jgD+Eegp6ySo+UkMecfd6t9a8vE0PbU2tmtUzppy5Wdb3pa55NQu4mRWIkDMFw3UAnb1roq+IrUJUrXs77HrRnzCUUUVyGgClpBS17eG+D5/wCRhPc//9LW3oMjnIJ/maPMX3qIgbm+YA5OQSM9frScf3l/Mf415bwlJtt318+p0c8tCbzF96TzB6VCdvdl/Ok3Q95I/wA6awlJNNJ3Tvux+0ZN5yAsOfl4Pr+VVo7+3JcOQCh6L1/GmeZGjP8A6XCilsgFQSP+BE0hubZTzdp9QEH9DXRGjCLclHV9Sed7F5b2zYcMc/7p/oKkSeJ+gI+oxx+IrOF7Z976QfQrj9FprX9jtKm8nbP+1/gua0cV/XcOb0NJZrd2IV13D7wzzVPVdgtVyoY7127u3Y4/CsNnsnPzXJ4OVKgj/vogVdhMF8Ps6Tybl+dW6gY4wdw7+tCag1OV7Lf0CT5k1pcongcDCjtTWIXDGr/9nXhIG+Ej+Jxx+hzz+NOuLC3tLOaWV3mbAWPsAx4HA9+9djxtC6jGXM3supxeyluzFeXDbhyRwF96WOOQkyOAxPPNVwjnByfc981OpI6ySIf9sYX+VdS11fyB26ExI9s+lRueMAFmIOBjvTy0nRkB9GHelyygkgL2HrWpmh1mBLqUKqrKi/wNyeB1P41156n61zeiRk3M8jZJRAMn1JP9K6OvhcdL94l2R61La4UUUV5B1AKWkFLXt4b4Pn/kYT3P/9NtxpLXE8kvnqock7dpOPY4YCoRofPNwv4If6ua6Ecj8T/Olr5ipi6ylJJpJPTRbHdGnGxgf2FH3nP/AHyKeNDtu80n4Kv9RW5RXP8AW6/834Ir2cTE/sS0H/LWb8l/+Jp40Wx7vM31wP5AVr0d6n61X/nf4f5D9nEzBpFgOMS5/wB4j+VI2n6PHxKSPrIf6GqWsXs6Sm2jOxAAzkfeJPOM/hXOHLHkljXt0qVeSjKVWSv0/wCCcknHZIuXHlx3LrEQ0KsNp5Py8d634bnRLdt8IZZNu1iqk9Rz+Fcp2HtViNHYYGAPWu+VJTSTbt18/Uy5mtjqjqthjrKfQbDWfqV7Z3cACPIro2VRgQG//VWaLdTyzsfXHH8qeLeEDpn6msYYWnCSlG6a21G6jejIo3iwMSFWPUFc81KWc8ech9AVxUgjgHG1FPanFUAGRkdwK9dTOdoiCyD7vlY9QT/LGKGJUAttLdqWSKJRuUtg9l7D144qtIIwAQckjjPpW3OibHRaKp+zSyE8ySH8gMfzFa46VR0xdmnRf7QLn8Tn+tXq/PsTK9Wp/iPZgtEFFFFcZqApaQUte3hvg+f+RhPc/9TZHT8T/M0tIOn5/wAzS18PU+Of+J/mepHZBRRRWBYUetFBpiZy2uoReB+0kYx9Qf8A69ZESb5AOmASa6bXEzbxOBkq+CfYg/1xXORYWT6ivs8NLmpQflY8uejYyVdvT1qxEdqKfWopug+v9CKdbxzTMI4l3PgsFz1A9M12mZa8zHpTS9KllfucLBJ9WwAPzNWf7K1D0hH/AAI/0FYurTjvKP3hyyeyKobPbNKZBjpzT5rK6gwZSnPQDJz2qwukXx+bMa+xpurTSTco2e2ocruUtzEcHb6+mKqTPucHOSB/LmtSTSNQzkbGHs2P0x/WiHR7tnHnARx/xtnJx7DHeodelb4l9/QtQfY6KzXZZQKeyAVZpAAAFH3VAC0tfGTd5Sfd3PSjsgooorMsBS0gpa9vDfB8/wDIwnuf/9XZHT8T/M0tIOg/H+Zpa+HqfHP/ABP8z1I7IKKKKwLCiiimBR1RN+ny44K4f/vk7v1rjhncp98V3cyh4JUxncjDH4Vwig4HscfjX0+BleEo9n/X5HnVVqPl6H2PFT2bFZrdwcFZB/PH61FIAVzS2/34wOplUf8Ajwr15bS9DnO5OckZ/wA9aTpSnrRXwb3f9dT1+iMTWmRRbZYD58kDrtGDn860LS6W5T7joygZDDqPUHpVe+QNLyAcREDjtnNaKEmKP/cX+VevU5fYUbq7ez+bOZazY6iiivHOoKKKKQwooopiAUtIKWvbw3wfP/I55bn/1tkZx+J/maWgdPz/AJmivk50VzT16/qeipaIKKKKy9iu5XMFFFFHsV3FzAPT14NcROuy6lRG+VXY8/nXdJ95frUAtrVnYtBAxPUlFJPA65Fetgo8rlruc1TWxxOeSOKktP8Aj8hHbzl/xrsfslng/wCj2/8A3wv+FH2W0DZEEAIIIIRc5/Kvals/Q5f8yY/eP1pKXAHT0/woHWvjvZK+/wDVz009ilcjLt7Rnn8TVmIYhj/3BSkAsMgH5e/1NOAAAxxxXpVKd6NJX2bOeL96QoFNBB5Ugj1HT/IpJFRk+ZVb6jP86Io449yxoiKMYCgAfkBXL9WXLzcz+43UmOooorD2K7j5gooopOiu4+YKWkpa9ahTtDfqc0pan//Z" width="167" height="250" class="img_ev3q"></a></p>]]></content:encoded>
            <category>books</category>
        </item>
        <item>
            <title><![CDATA[JavaScript Objects Cachification]]></title>
            <link>https://www.alexanderlolis.com/javascript-objects-cachification</link>
            <guid>https://www.alexanderlolis.com/javascript-objects-cachification</guid>
            <pubDate>Sat, 19 Dec 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[Wrapping your JavaScript Objects with caching capabilities]]></description>
            <content:encoded><![CDATA[<p>In one of our backend services, we have a class, that is basically the business model of the service and is used in many locations within the code. At some point, we decided that we need to do some caching since a few methods were doing very expensive calls to other remote services and they didn’t need to occur that often.</p>
<p>How do you add caching to a <code>class</code> that is used very often within the rest of the codebase in the least intrusive way? You wrap the <code>prototype</code> of the <code>class</code> of course! And how do you wrap the prototype of the class? With another function that does the wrapping of course!</p>
<p>Our main business model (which is just a fancy name for a class that has business logic in it) requires some initialization every time it is instantiated. Therefore, we have a function that initializes and configures an instance of the <code>BusinessModel</code> which is used throughout the service. Let’s say that it looks like this:</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// Our dummy 'BusinessModel' ES6 class</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">BusinessModel</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">findItem</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// code</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">findItems</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// code</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token function" style="color:#d73a49">getBasicInfo</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// code</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// The utility function that instantiates and initializes a 'BusinessModel'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">getBusinessModel</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> businessModel </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">BusinessModel</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// Do some initialization here and other magic things...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> businessModel</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>So, the above function is basically being called any time we need an instance of the <code>BusinessModel</code> instead of directly constructing it. If for some reason we do not want to wrap the <code>prototype</code> and go with another approach instead, this function is the perfect candidate to add caching capabilities to the freshly instantiated <code>businessModel</code>. But first things first and more about this later.</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</div><div class="admonitionContent_BuS1"><p><code>class</code> is not a real boy in JavaScript. It’s just a keyword, not a type. If you are not already aware of this, I strongly recommend reading <a href="https://github.com/getify/You-Dont-Know-JS/blob/1st-ed/this%20&amp;%20object%20prototypes/README.md#you-dont-know-js-this--object-prototypes" target="_blank" rel="noopener noreferrer" class="">You Don’t Know JS: this &amp; Object Prototypes</a>.</p></div></div>
<p>We will need the code that actually adds caching capabilities to our class so I will just dump it here and try to explain a few things afterward:</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c">'use strict'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports">_</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'lodash'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports">objectHash</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'object-hash'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> debug </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">require</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'debug'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'cachify'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">KEY_PREFIX_DELIMITER</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">':'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token doc-comment comment" style="color:#999988;font-style:italic">/**</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@callback</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> preCache</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@param</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">CacheManager</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment parameter" style="color:#999988;font-style:italic">cacheManager</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> - The cache manager instance.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@param</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">String</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment parameter" style="color:#999988;font-style:italic">cacheKey</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> - The cache key used, including the prefix.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@param</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">Sting</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment parameter" style="color:#999988;font-style:italic">keyPrefix</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> - The cache key prefix.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@param</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">String</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment parameter" style="color:#999988;font-style:italic">fnName</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> - The name of the function that was wrapped.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@param</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">Array</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> [fnArgs=[]] - The arguments of the function that was wrapped.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token doc-comment comment" style="color:#999988;font-style:italic">/**</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@callback</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> postTransformer</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@param</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name operator" style="color:#393A34;font-style:italic">*</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment parameter" style="color:#999988;font-style:italic">data</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> - The cached data.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token doc-comment comment" style="color:#999988;font-style:italic">/**</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * Cachify configuration object.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> *</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@typedef</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">Object</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">CachifyOptions</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"></span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@property</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">preCache</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment optional-parameter punctuation" style="color:#393A34;font-style:italic">[</span><span class="token doc-comment comment optional-parameter parameter" style="color:#999988;font-style:italic">preCache</span><span class="token doc-comment comment optional-parameter punctuation" style="color:#393A34;font-style:italic">=</span><span class="token doc-comment comment optional-parameter code language-javascript keyword null nil" style="color:#00009f;font-style:italic">null</span><span class="token doc-comment comment optional-parameter punctuation" style="color:#393A34;font-style:italic">]</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> - The pre cache hook to call after the original function has been called and</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> *   before the data have been actually cached.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@property</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">Function</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment optional-parameter punctuation" style="color:#393A34;font-style:italic">[</span><span class="token doc-comment comment optional-parameter parameter" style="color:#999988;font-style:italic">postTransformer</span><span class="token doc-comment comment optional-parameter punctuation" style="color:#393A34;font-style:italic">=</span><span class="token doc-comment comment optional-parameter code language-javascript keyword null nil" style="color:#00009f;font-style:italic">null</span><span class="token doc-comment comment optional-parameter punctuation" style="color:#393A34;font-style:italic">]</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> - The post data load transformer hook. Called after the data have</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> *   been loaded from cache.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@property</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">String</span><span class="token doc-comment comment class-name operator" style="color:#393A34;font-style:italic">|</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">Array</span><span class="token doc-comment comment class-name operator" style="color:#393A34;font-style:italic">|</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">Function</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment optional-parameter punctuation" style="color:#393A34;font-style:italic">[</span><span class="token doc-comment comment optional-parameter parameter" style="color:#999988;font-style:italic">keyPrefix</span><span class="token doc-comment comment optional-parameter punctuation" style="color:#393A34;font-style:italic">=</span><span class="token doc-comment comment optional-parameter code language-javascript" style="color:#999988;font-style:italic">cachify</span><span class="token doc-comment comment optional-parameter punctuation" style="color:#393A34;font-style:italic">]</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> - The default prefix for cache keys. If it is a function</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> *   the first argument of the function will be the context object in which the function was called.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token doc-comment comment" style="color:#999988;font-style:italic">/**</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * Returns a cache key prefix string.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> *</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@param</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">String</span><span class="token doc-comment comment class-name operator" style="color:#393A34;font-style:italic">|</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">Array</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">&lt;</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">String</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">&gt;</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment parameter" style="color:#999988;font-style:italic">keyPrefix</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> - The key prefix to use for the cache key.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@returns</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">String</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"></span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">getCacheKeyPrefixString</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">keyPrefix</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> keyPrefixString </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token known-class-name class-name">Array</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">isArray</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">keyPrefix</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> keyPrefix</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">join</span><span class="token punctuation" style="color:#393A34">(</span><span class="token constant" style="color:#36acaa">KEY_PREFIX_DELIMITER</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> keyPrefix</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> keyPrefixString</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token doc-comment comment" style="color:#999988;font-style:italic">/**</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * Returns a cache key which is computed by hashing the target function name and</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * the target arguments list.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> *</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@param</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">String</span><span class="token doc-comment comment class-name operator" style="color:#393A34;font-style:italic">|</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">Array</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">&lt;</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">String</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">&gt;</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment parameter" style="color:#999988;font-style:italic">keyPrefix</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> - The key prefix to use for the cache key.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@param</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">String</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment parameter" style="color:#999988;font-style:italic">fnName</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> - The function name.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@param</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">Array</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment parameter" style="color:#999988;font-style:italic">fnArgs</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> - The function arguments list.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@returns</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">String</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"></span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">getCachifyCacheKey</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">keyPrefix</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> name</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> args</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> keyPrefixString </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">getCacheKeyPrefixString</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">keyPrefix</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> hash </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">objectHash</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> args</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">replacer</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> $</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">keyPrefixString</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain">$</span><span class="token punctuation" style="color:#393A34">{</span><span class="token constant" style="color:#36acaa">KEY_PREFIX_DELIMITER</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain">$</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">hash</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token doc-comment comment" style="color:#999988;font-style:italic">/**</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * Wraps target function with caching capabilities. The wrapped function will always return a promise</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * since it needs to check the cache first asynchronously and decide whether or not it will call</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * the original function.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> *</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@param</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">CacheManager</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment parameter" style="color:#999988;font-style:italic">cacheManager</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> - The cache manager instance.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@param</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">Function</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment parameter" style="color:#999988;font-style:italic">targetFn</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> - The function we will be wrapping with a caching layer.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@param</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">CachifyOptions</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment parameter" style="color:#999988;font-style:italic">options</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> - The cachify configuration object.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@returns</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">Function</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> - The asynchronous wrapped function.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">cachify</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">cacheManager</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> targetFn</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> options </span><span class="token parameter operator" style="color:#393A34">=</span><span class="token parameter"> </span><span class="token parameter punctuation" style="color:#393A34">{</span><span class="token parameter punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    keyPrefix </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'cachify'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    postTransformer </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    preCache </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ttl </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> options</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">wrappedFn</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter spread operator" style="color:#393A34">...</span><span class="token parameter">args</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> keyPrefixString </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> _</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">isFunction</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">keyPrefix</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token function" style="color:#d73a49">getCacheKeyPrefixString</span><span class="token punctuation" style="color:#393A34">(</span><span class="token function" style="color:#d73a49">keyPrefix</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token function" style="color:#d73a49">getCacheKeyPrefixString</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">keyPrefix</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> targetFnName </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> targetFn</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">name</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> cacheKey </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">getCachifyCacheKey</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">keyPrefixString</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> targetFnName</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> args</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> result </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> cacheManager</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">wrap</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cacheKey</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token function" style="color:#d73a49">debug</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">cachify</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token maybe-class-name">Calling</span><span class="token plain"> original </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'${targetFnName}'</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">with</span><span class="token plain"> cache key </span><span class="token string" style="color:#e3116c">'${cacheKey}'</span><span class="token spread operator" style="color:#393A34">...</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> originalResult </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> targetFn</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">apply</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> args</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">preCache</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">preCache</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">originalResult</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          cacheManager</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          cacheKey</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          args</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token literal-property property" style="color:#36acaa">keyPrefix</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> keyPrefixString</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token literal-property property" style="color:#36acaa">fnName</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> targetFnName</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> originalResult</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">ttl</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> postTransformer </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">postTransformer</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">result</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> result</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> wrappedFn</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token doc-comment comment" style="color:#999988;font-style:italic">/**</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * Wraps specified methods of the targetObject with caching capabilities.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> *</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * **NOTE:** Function mutates `targetObject`.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> *</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@param</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">CacheManager</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment parameter" style="color:#999988;font-style:italic">cacheManager</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> - The cache manager instance.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@param</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">Object</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment parameter" style="color:#999988;font-style:italic">targetObject</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> - The object we will be wrapping.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@param</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">Object</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment parameter" style="color:#999988;font-style:italic">cacheableMap</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> - The object describing which methods of `targetObject` should be wrapped.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@param</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">CachifyOptions</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment optional-parameter punctuation" style="color:#393A34;font-style:italic">[</span><span class="token doc-comment comment optional-parameter parameter" style="color:#999988;font-style:italic">options</span><span class="token doc-comment comment optional-parameter punctuation" style="color:#393A34;font-style:italic">=</span><span class="token doc-comment comment optional-parameter code language-javascript punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment optional-parameter code language-javascript punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment optional-parameter punctuation" style="color:#393A34;font-style:italic">]</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> - The cachify options object.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> * </span><span class="token doc-comment comment keyword" style="color:#00009f;font-style:italic">@returns</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> </span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">{</span><span class="token doc-comment comment class-name" style="color:#999988;font-style:italic">Object</span><span class="token doc-comment comment class-name punctuation" style="color:#393A34;font-style:italic">}</span><span class="token doc-comment comment" style="color:#999988;font-style:italic"> - The cachified object.</span><br></span><span class="token-line" style="color:#393A34"><span class="token doc-comment comment" style="color:#999988;font-style:italic"> */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">cachifyObject</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">cacheManager</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> targetObject</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> cacheableMap</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> options </span><span class="token parameter operator" style="color:#393A34">=</span><span class="token parameter"> </span><span class="token parameter punctuation" style="color:#393A34">{</span><span class="token parameter punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  _</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">forEach</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cacheableMap</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">functionConfig</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> functionName</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token plain">functionConfig</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token function" style="color:#d73a49">debug</span><span class="token punctuation" style="color:#393A34">(</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">{cachifyObject} Cachification for function '</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">functionName</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string string" style="color:#e3116c">' is disabled...skipping...</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> originalFn </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> targetObject</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">functionName</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token plain">originalFn</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token function" style="color:#d73a49">debug</span><span class="token punctuation" style="color:#393A34">(</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">{cachifyObject} Couldn't find function '</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">functionName</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string string" style="color:#e3116c">' on target object...skipping...</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> functionOptions </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token spread operator" style="color:#393A34">...</span><span class="token plain">options</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token spread operator" style="color:#393A34">...</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">_</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">isPlainObject</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">functionConfig</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> functionConfig </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> wrappedFn </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">cachify</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cacheManager</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> originalFn</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> functionOptions</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    targetObject</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">functionName</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> wrappedFn</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> targetObject</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>A few things are going on in the code block above. You do not need to use everything but I have added them anyway because we will need them in a later post for a caching approach I will describe. Besides, they might help you with your own use case!</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="using-hashes-for-cache-keys">Using hashes for cache keys<a href="https://www.alexanderlolis.com/javascript-objects-cachification#using-hashes-for-cache-keys" class="hash-link" aria-label="Direct link to Using hashes for cache keys" title="Direct link to Using hashes for cache keys" translate="no">​</a></h3>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">getCachifyCacheKey</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">keyPrefix</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> fnName</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> fnArgs</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> keyPrefixString </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">getCacheKeyPrefixString</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">keyPrefix</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> hash </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">objectHash</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token literal-property property" style="color:#36acaa">name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> fnName</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">args</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> fnArgs</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">replacer</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">keyPrefixString</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation constant" style="color:#36acaa">KEY_PREFIX_DELIMITER</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">hash</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>The most important thing from the whole code is the way we generate our cache keys within the <code>getCachifyCacheKey</code> function. If you look more closely above, we are creating a hash from the name and the arguments of the function we want to cachify and the <a href="https://www.npmjs.com/package/object-hash" target="_blank" rel="noopener noreferrer" class="">object-hash</a> npm module helps us do just that.</p>
<p>The reason we do this is the fact that <strong>we want to be able to cache results from the same function, but with different arguments, to separate cache keys</strong>. if something simpler was used as a cache key (e.g. the function name), then we would be returning the same cached value all the time, no matter the arguments we would be passing to the function, which is not something we want.</p>
<p>For example, the following two <strong>same</strong> method calls will produce two different cache keys due to the fact that they have different arguments:</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// Cache key: b1e2f1cbcf8a99de6bbeea579d980cc0b0f3261a</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">businessModel</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">findItems</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token literal-property property" style="color:#36acaa">isHidden</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Cache key: 493b0e3045532ab6effe912e71feb7ee26c29199</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">businessModel</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">findItems</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token literal-property property" style="color:#36acaa">isHidden</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-main-functions-cachify-and-cachifyobject">The main functions; <code>cachify</code> and <code>cachifyObject</code><a href="https://www.alexanderlolis.com/javascript-objects-cachification#the-main-functions-cachify-and-cachifyobject" class="hash-link" aria-label="Direct link to the-main-functions-cachify-and-cachifyobject" title="Direct link to the-main-functions-cachify-and-cachifyobject" translate="no">​</a></h3>
<p><code>cachify</code> is the function that does the main work. It will wrap a target function with caching capabilities by internally using the <a href="https://www.npmjs.com/package/cache-manager" target="_blank" rel="noopener noreferrer" class="">cache-manager</a> npm module. You do not really need this module for the wrapping, you could also use a lower-level library like <a href="https://www.npmjs.com/package/ioredis" target="_blank" rel="noopener noreferrer" class="">ioredis</a> and do the wrapping yourself. However, in our use case, we needed <code>cache-manager</code> so I used that instead. Besides, it comes with a handy method called <strong>wrap</strong> which is what we need.</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// Version with 'cache-manager'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">cachify</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">cacheManager</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> targetFn</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> options </span><span class="token parameter operator" style="color:#393A34">=</span><span class="token parameter"> </span><span class="token parameter punctuation" style="color:#393A34">{</span><span class="token parameter punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// [removed code for simplicity]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">wrappedFn</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter spread operator" style="color:#393A34">...</span><span class="token parameter">args</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// [removed code for simplicity] </span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> result </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> cacheManager</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">wrap</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cacheKey</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> originalResult </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> targetFn</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">apply</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> args</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">      </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> originalResult</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">ttl</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// [removed code for simplicity]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// The 'wrappedFn' which we will return is going to be asynchronous, no matter what</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> wrappedFn</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">/*</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">  Alternative version without 'cache-manager' but with 'ioredis' instead. </span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">  It could be done better, but just giving you an idea what might look like. </span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">  It will basically check if the cache key exists by loading it, and if not, </span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">  it will call the original function and cache the result before it actually </span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">  returns it. Easy to implement yourself as well in case you do not go </span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">  with 'cache-manager'.</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">*/</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">cachify</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">redis</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> targetFn</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> options </span><span class="token parameter operator" style="color:#393A34">=</span><span class="token parameter"> </span><span class="token parameter punctuation" style="color:#393A34">{</span><span class="token parameter punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// [removed code for simplicity]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">wrappedFn</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter spread operator" style="color:#393A34">...</span><span class="token parameter">args</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// [removed code for simplicity] </span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> cachedResult </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> redis</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cacheKey</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cachedResult</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token known-class-name class-name">JSON</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">parse</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cachedResult</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">else</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> originalResult </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> targetFn</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">apply</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> args</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> originalResultString </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token known-class-name class-name">JSON</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">stringify</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">originalResult</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      ttl </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> redis</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">setex</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">originalResultString</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ttl</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> redis</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">originalResultString</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> originalResult</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// [removed code for simplicity]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// The 'wrappedFn' which we will return is going to be asynchronous, no matter what</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> wrappedFn</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>The partial code above from the <code>cachify</code> function shows the current cache wrapping approach and a possible alternative.</p>
<p>One thing that we must note here is the use of <code>await</code> in order to communicate with the cache. That means, that even if the original function – the one we are wrapping – was not asynchronous, then it must be converted to one (by using <code>async</code> as shown above) in order for the mechanism to work properly.</p>
<p>I am pretty sure that in the full code at the beginning you have noticed other things already, like <code>preCache</code> and <code>postTransformer</code> hooks. Although they are not really necessary for this post, as you have already guessed they definitely have their use if you want to make more actions before and after caching the data. <strong>Hint:</strong> managing a <a class="" href="https://www.alexanderlolis.com/how-to-tag-data-in-redis#using-sets-as-secondary-indexes">redis secondary index</a>. If your own use case needs it, you could introduce more hooks into the code.</p>
<p><code>cachifyObject</code> is more like a helper function in order to easily cachify an <code>Object</code>. It will loop the object and based on a map describing which methods of the object we want to <code>cachify</code>, it will do it for us by using cachify internally.</p>
<p>The original methods will be overwritten and the mutated target <code>Object</code> will be returned to the caller</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="bringing-everything-together">Bringing everything together<a href="https://www.alexanderlolis.com/javascript-objects-cachification#bringing-everything-together" class="hash-link" aria-label="Direct link to Bringing everything together" title="Direct link to Bringing everything together" translate="no">​</a></h3>
<p>The hard part is done, we have our wrapping functions ready and all we need now is to apply it to the <code>prototype</code> of the <code>class</code> we want to cachify; in our case, the <code>BusinessModel</code>.</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c">'use strict'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports">getRedisSingletonInstance</span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'/some/magic/utils/we/have'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports maybe-class-name">BusinessModel</span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'/some/magic/models/we/have'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">CacheManager</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'cache-manager'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports">redisStore</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'cache-manager-ioredis'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">cachifyBusinesModel</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">businessModel</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> </span><span class="token parameter punctuation" style="color:#393A34">{</span><span class="token parameter">ttl </span><span class="token parameter operator" style="color:#393A34">=</span><span class="token parameter"> </span><span class="token parameter number" style="color:#36acaa">1200</span><span class="token parameter punctuation" style="color:#393A34">}</span><span class="token parameter"> </span><span class="token parameter operator" style="color:#393A34">=</span><span class="token parameter"> </span><span class="token parameter punctuation" style="color:#393A34">{</span><span class="token parameter punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> options </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ttl</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">keyPrefix</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'businessModel'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// The methods in this map will be targeted for cachification. </span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// Any methods that are not in this map will be left intact.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// We also make use of postTransformer in order to convert our cached </span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// results to BusinessModel instances before returning.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> cacheableMethodsMap </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">findItems</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token function-variable function" style="color:#d73a49">postTransformer</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token parameter">result</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> _</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">map</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">result</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token parameter">item</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token maybe-class-name">BusinessModel</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">fromJson</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">item</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">findItem</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token function-variable function" style="color:#d73a49">postTransformer</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token parameter">result</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token maybe-class-name">BusinessModel</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">fromJson</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">result</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">ttl</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">120</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// Override default ttl</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">getBasicInfo</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> cacheManager </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token maybe-class-name">CacheManager</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">caching</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">store</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> redisStore</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">redisInstance</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">getRedisSingletonInstance</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function-variable function" style="color:#d73a49">isCacheableValue</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token parameter">value</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> value </span><span class="token operator" style="color:#393A34">!==</span><span class="token plain"> </span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> value </span><span class="token operator" style="color:#393A34">!==</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> value </span><span class="token operator" style="color:#393A34">!==</span><span class="token plain"> </span><span class="token keyword nil" style="color:#00009f">undefined</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">cachifyObject</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cacheManager</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> businessModel</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cacheableMethodsMap</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> options</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Keep the cachification call near our 'getBusinessModel' utility function</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// and maybe with some explanatory comment block in order for someone new</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// to easily figure out what is going on.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">cachifyBusinesModel</span><span class="token punctuation" style="color:#393A34">(</span><span class="token class-name">BusinessModel</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">prototype</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Make sure that you mention in the comment block here that the result </span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// is cachified and point out to the reader to the correct direction in order </span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// to find more details about it. </span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">getBusinessModel</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> businessModel </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">BusinessModel</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// Do some initialization here...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> businessModel</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// Our businessModel instance is now cachified!</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>A few things I would like to note here for the code above:</p>
<ol>
<li class="">We have implemented a <code>cachifyBusinesModel</code> function in which we will be cachifying the <code>businessModel</code> Object we pass as an argument. A <code>map</code> named <code>cacheableMethodsMap</code> contains all the methods that we want to cachify and it will be passed along with some other options (like the cache key) to our <code>cachifyObject</code> function.</li>
<li class="">We initializate a <code>CacheManager</code> instance which we will pass to our cachifyObject function as well. The options of the <code>CacheManager</code> are not important. What is important is how we get the redis instance. As you have noticed we use a <code>getRedisSingletonInstance</code> function which basically returns a global instance of redis that will be instantiated the first time the function is called. For every other call, the existing instance will be returned. This is important in order to avoid any code bottleneck by instantiating redis again and again everytime <code>cachifyBusinesModel</code> function is called. If we wrap the prototype, as we do in the example above, then it shouldn’t be a problem since it will only be called once. But if you go with the alternative approach (described in the next section) which wraps the instantiated object instead, then it will definitely be an issue.</li>
<li class="">Make sure that you comment on everything in your code in order for the caller of the <code>getBusinessModel</code> to know exactly where he is getting into.</li>
</ol>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</div><div class="admonitionContent_BuS1"><p>Instead of implementing <code>getRedisSingletonInstance</code> to handle the singleton, you could use a <a href="https://www.npmjs.com/package/typedi" target="_blank" rel="noopener noreferrer" class="">container</a> approach.</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="to-prototype-or-not-to-prototype">To <code>prototype</code> or not to <code>prototype</code><a href="https://www.alexanderlolis.com/javascript-objects-cachification#to-prototype-or-not-to-prototype" class="hash-link" aria-label="Direct link to to-prototype-or-not-to-prototype" title="Direct link to to-prototype-or-not-to-prototype" translate="no">​</a></h3>
<p>I have mentioned earlier the use of a utility function, <code>getBusinessModel</code> in which the <code>BussinessModel</code> is instantiated and initialized there. The reason that I did mention this function is because I want to point out that wrapping the <code>prototype</code> has a trade-off; code obscurity.</p>
<p>A new guy might miss the fact that <em>some</em> of the object methods he uses return a cached version of the result. One might say that we can add a suffix to those methods (e.g. <code>findItemsWithCache</code>) instead of overwriting the originals. Except that I find this ugly, you will also not be able to easily disable the cache in case you want to do some debugging without it. By keeping the original names, you can just turn off cachification in case you want to run the code without the cache and just let it do its thing without the hassle of changing method names as well.</p>
<p>Another small problem with the wrapping of the <code>prototype</code> is when we need to dynamically build our cache key prefix. If we want to use variables that are not within the object we are wrapping (which we can access them via this if we use a function as a <code>keyPrefix</code> getter) and we can not know their values beforehand, then we simply can’t do it. To be fair here, we are doing some complex caching ourselves and we haven’t bumped in such a case but it’s definitely a possibility.</p>
<p>A possible alternative, as you can see in the code example below, is to do the wrapping every time the <code>BusinessModel</code> is instantiated:</p>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// Alternative version with cachifying the instantiated object. </span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// No obscurity with this version; just 'useCache' a flag that the caller </span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// decides if it should be true or false. The new guy will be happier.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">getBusinessModel</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter punctuation" style="color:#393A34">{</span><span class="token parameter">useCache </span><span class="token parameter operator" style="color:#393A34">=</span><span class="token parameter"> </span><span class="token parameter boolean" style="color:#36acaa">true</span><span class="token parameter punctuation" style="color:#393A34">}</span><span class="token parameter"> </span><span class="token parameter operator" style="color:#393A34">=</span><span class="token parameter"> </span><span class="token parameter punctuation" style="color:#393A34">{</span><span class="token parameter punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> businessModel </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">BusinessModel</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// Do some initialization here...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">useCache</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">cachifyBusinessModel</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">businessModel</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">else</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> businessModel</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>However, if you have a large amount of objects that you are instantiating, you will definitely benefit from wrapping the <code>prototype</code> <em>once</em> instead of wrapping each instantiated object as it might become a bottleneck for your code.</p>
<p>If you have a very small amount of objects that you are instantiating, it might be better to use the alternative version of <code>getBusinessModel</code> from above which will also be doing the cachification. The advantage of going with this approach, even if there is a slight – almost insignificant probably – performance penalty, the code will be much less obscured. The new guy will just need to read the documentation of the function and immediately will figure out what is going on without getting crazy first!</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://www.alexanderlolis.com/javascript-objects-cachification#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h3>
<p>We saw that with the use of a couple of functions, we can easily add caching to expensive <code>Object</code> methods that we use often with minimum effort. Depending on our use case, and if our code is modular enough, we can achieve the same result in more than one way. I hope that I gave you some idea of how to approach this and maybe you can find improvements while you are at it. If you do, I would love to hear about them!</p>]]></content:encoded>
            <category>development</category>
            <category>javascript</category>
            <category>nodejs</category>
            <category>caching</category>
            <category>battlefield</category>
        </item>
        <item>
            <title><![CDATA[How to tag data in Redis]]></title>
            <link>https://www.alexanderlolis.com/how-to-tag-data-in-redis</link>
            <guid>https://www.alexanderlolis.com/how-to-tag-data-in-redis</guid>
            <pubDate>Tue, 01 Dec 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[What are Sets and how to use them as secondary indexes]]></description>
            <content:encoded><![CDATA[<p><a href="https://redis.io/" target="_blank" rel="noopener noreferrer" class="">Redis</a> is a wonderful piece of technology. An in-memory, super-fast key-value database, which if you do not already know that it exists, you have been living under a rock.</p>
<p>The cool thing about Redis, is that it supports more data types than simply storing string key-value pairs, and that’s when the fun begins. One of those data types, which will help us with our tagging exercise, is Sets.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="redis-sets">Redis Sets<a href="https://www.alexanderlolis.com/how-to-tag-data-in-redis#redis-sets" class="hash-link" aria-label="Direct link to Redis Sets" title="Direct link to Redis Sets" translate="no">​</a></h3>
<p>Let’s assume that you have an app and you want to show a list of book titles and their associated tags. The first step is to add those articles in Redis, along with their tags. Let’s add a few via <code>redis-cli</code>:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token number" style="color:#36acaa">127.0</span><span class="token plain">.0.1:6379</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> SADD </span><span class="token string" style="color:#e3116c">"The Advantage: Why Organizational Health Trumps Everything Else in Business"</span><span class="token plain"> business leadership nonfiction patrick_lencioni</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">integer</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">4</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">127.0</span><span class="token plain">.0.1:637</span><span class="token operator file-descriptor important" style="color:#393A34">9</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> SADD </span><span class="token string" style="color:#e3116c">"Conscious Business: How to Build Value Through Values"</span><span class="token plain"> business nonfiction fred_kofman</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">integer</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">127.0</span><span class="token plain">.0.1:637</span><span class="token operator file-descriptor important" style="color:#393A34">9</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> SADD </span><span class="token string" style="color:#e3116c">"Code Complete"</span><span class="token plain"> programming computer_science reference steve_mcconnell</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">integer</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">4</span><br></span></code></pre></div></div>
<p>The above commands will create three different <code>Sets</code>. Each of them will use the title of the book as a key and the rest of the values after the title will be added as members within each Set.</p>
<p>You can check the members of the <code>Set</code> by issuing an <code>SMEMBERS</code> command, like so:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token number" style="color:#36acaa">127.0</span><span class="token plain">.0.1:6379</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> SMEMBERS </span><span class="token string" style="color:#e3116c">"The Advantage: Why Organizational Health Trumps Everything Else in Business"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"business"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"leadership"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"nonfiction"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"patrick_lencioni"</span><br></span></code></pre></div></div>
<p>The special thing about <code>Sets</code> is that if you try to add an element that already exists, the element will simply be ignored. This ensures that every member within that Set, is unique.</p>
<p>For example, let’s say you want to add the tag <code>must_read</code> to a <code>Set</code>, and then you try to re-add it:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token number" style="color:#36acaa">127.0</span><span class="token plain">.0.1:6379</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> SADD </span><span class="token string" style="color:#e3116c">"The Advantage: Why Organizational Health Trumps Everything Else in Business"</span><span class="token plain"> must_read</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">integer</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">127.0</span><span class="token plain">.0.1:6379</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> SADD </span><span class="token string" style="color:#e3116c">"The Advantage: Why Organizational Health Trumps Everything Else in Business"</span><span class="token plain"> must_read</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">integer</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">127.0</span><span class="token plain">.0.1:6379</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> SMEMBERS </span><span class="token string" style="color:#e3116c">"The Advantage: Why Organizational Health Trumps Everything Else in Business"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"business"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"must_read"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"leadership"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"nonfiction"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">5</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"patrick_lencioni"</span><br></span></code></pre></div></div>
<p>As you can see in the example above, even though I tried to add it twice, there is only one copy of the <code>must_read</code> tag within the <code>Set</code>, which definitely makes our next step easier.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="using-sets-as-secondary-indexes">Using Sets as secondary indexes<a href="https://www.alexanderlolis.com/how-to-tag-data-in-redis#using-sets-as-secondary-indexes" class="hash-link" aria-label="Direct link to Using Sets as secondary indexes" title="Direct link to Using Sets as secondary indexes" translate="no">​</a></h3>
<p>Storing plain <code>Sets</code> is not that useful by itself. Most of the time, we will need to access the data in different ways in order to show a useful representation of said data. By denormalizing and/or duplicating the data we are able to achieve just that. This common pattern, called "secondary index", in our case is basically a Set with the original data, but inverted.</p>
<p>For example, let’s say that you want to be able to find all the book titles with the tag <em>business</em>. What you need to do here is to create a <code>Set</code> whose key will be <code>tag:business</code> and its members are going to be the titles of the books which are associated with this tag:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token number" style="color:#36acaa">127.0</span><span class="token plain">.0.1:637</span><span class="token operator file-descriptor important" style="color:#393A34">9</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> SADD tag:business </span><span class="token string" style="color:#e3116c">"The Advantage: Why Organizational Health Trumps Everything Else in Business"</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Conscious Business: How to Build Value Through Values"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">integer</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">127.0</span><span class="token plain">.0.1:637</span><span class="token operator file-descriptor important" style="color:#393A34">9</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> SADD tag:programming </span><span class="token string" style="color:#e3116c">"Code Complete"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">integer</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><br></span></code></pre></div></div>
<p>Now every time you need to find all books with the <em>business</em> tag, all you have to do is to use the <code>SMEMBERS</code> command on the <code>tag:business</code> key, and then use those members (which are basically keys that represent other <code>Sets</code>, book titles in our case) to get the complete list of their tags:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token number" style="color:#36acaa">127.0</span><span class="token plain">.0.1:637</span><span class="token operator file-descriptor important" style="color:#393A34">9</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> SMEMBERS tag:business</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Conscious Business: How to Build Value Through Values"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"The Advantage: Why Organizational Health Trumps Everything Else in Business"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">127.0</span><span class="token plain">.0.1:637</span><span class="token operator file-descriptor important" style="color:#393A34">9</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> SMEMBERS </span><span class="token string" style="color:#e3116c">"Conscious Business: How to Build Value Through Values"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"business"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"fred_kofman"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"nonfiction"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">127.0</span><span class="token plain">.0.1:637</span><span class="token operator file-descriptor important" style="color:#393A34">9</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> SMEMBERS </span><span class="token string" style="color:#e3116c">"The Advantage: Why Organizational Health Trumps Everything Else in Business"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"business"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"patrick lencioni"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"nonfiction"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"leadership"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">5</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"must_read"</span><br></span></code></pre></div></div>
<p>Of course, the above is just a basic idea of what you can do with a secondary index. Properly creating and maintaining those secondary indexes is definitely something you need to carefully consider in your application logic since Redis will not handle it for you.</p>
<p>Another thing that you need to consider is atomicity. If multiple processes want to update your secondary indexes but also use any intermediate values from Redis in order to calculate the new data for the update, then the only way to achieve this is with <a href="https://redis.io/commands/eval" target="_blank" rel="noopener noreferrer" class="">Lua scripting</a> or else you will probably end up with inconsistencies. More on that in a different post!</p>
<p>Finally, keep in mind here that if you have A LOT of members in the <code>Set</code>, that might slow things down and you will need to use <code>SCAN</code> (or another closely related command) instead.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="other-cool-things-with-sets">Other cool things with Sets<a href="https://www.alexanderlolis.com/how-to-tag-data-in-redis#other-cool-things-with-sets" class="hash-link" aria-label="Direct link to Other cool things with Sets" title="Direct link to Other cool things with Sets" translate="no">​</a></h3>
<p>Finding common tags between book titles:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token number" style="color:#36acaa">127.0</span><span class="token plain">.0.1:637</span><span class="token operator file-descriptor important" style="color:#393A34">9</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> SINTER </span><span class="token string" style="color:#e3116c">"The Advantage: Why Organizational Health Trumps Everything Else in Business"</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Conscious Business: How to Build Value Through Values"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"business"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"nonfiction"</span><br></span></code></pre></div></div>
<p>Finding ALL unique tags between book titles:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token number" style="color:#36acaa">127.0</span><span class="token plain">.0.1:637</span><span class="token operator file-descriptor important" style="color:#393A34">9</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> SUNION </span><span class="token string" style="color:#e3116c">"The Advantage: Why Organizational Health Trumps Everything Else in Business"</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Conscious Business: How to Build Value Through Values"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"leadership"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"business"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"fred_kofman"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"must_read"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">5</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"nonfiction"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">6</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"patrick lencioni"</span><br></span></code></pre></div></div>
<p>Finding a random book title with a specific tag:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token number" style="color:#36acaa">127.0</span><span class="token plain">.0.1:637</span><span class="token operator file-descriptor important" style="color:#393A34">9</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> SRANDMEMBER tag:business</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token string" style="color:#e3116c">"Conscious Business: How to Build Value Through Values"</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://www.alexanderlolis.com/how-to-tag-data-in-redis#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h3>
<p>Sets are definitely a powerful and helpful data structure in Redis which can be the building block for many more things. With the use of secondary indexes, we can build and execute <a href="https://redis.io/topics/indexes" target="_blank" rel="noopener noreferrer" class="">even more complex</a> queries on our data and then load them in a very very fast manner. As we will be digging deeper, and by elevating the capabilities of Redis, we can build a very fast and efficient caching mechanism, even for the most demanding apps.</p>]]></content:encoded>
            <category>playground</category>
            <category>howto</category>
            <category>redis</category>
        </item>
        <item>
            <title><![CDATA[Fight or flight? Breathe instead]]></title>
            <link>https://www.alexanderlolis.com/fight-or-flight-breathe-instead</link>
            <guid>https://www.alexanderlolis.com/fight-or-flight-breathe-instead</guid>
            <pubDate>Wed, 04 Nov 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[How to achieve the best attitude at work with integrity and self-awareness]]></description>
            <content:encoded><![CDATA[<p>We’ve all been there. Feeling the frustration, the unfairness, and the negativity consuming us while everything is getting out of our control.</p>
<p>Emotions take over your brain and you are suddenly overwhelmed by your own feelings, leaving no room for rational thinking. That’s when your flight or fight response kicks in.</p>
<p>Thanks to amygdala hijacking, rational thinking becomes very difficult, and smashing a chair on someone’s face or storming out of a meeting room becomes very tempting. But please, do not do it, and instead, take a <strong>DEEP breath</strong> in order to make the first step into regaining consciousness.</p>
<p>In his excellent book, <a href="https://www.goodreads.com/book/show/1169674.Conscious_Business" target="_blank" rel="noopener noreferrer" class="">Conscious Business</a>, Fred Kofman talks about the importance of bringing awareness to our (business) lives by recognizing other people’s needs as well as ours and seeing their perspective without taking things personally or handling situations by putting our ego first. By <strong>maintaining a more open attitude</strong>, we are able to see everything as a challenge, as a chance to grow, instead of getting immediately defensive and feeling like a victim. This helps us to get better in our daily interaction with other human beings, and embrace a ‘player attitude’ in which instead of throwing the blame in any direction to avoid solving the problem because this seems easier, we become part of the solution by owning it.</p>
<p>So, what the hell is that Fred dude saying that this blog dude is agreeing with? To own up for other people’s mistakes? To try solving every problem that the company has and be happy about it? To be a “yes” person? To put up a smile while everyone is being aggressive and expressing opinions that have no merit?</p>
<p>Nope. What I am saying is, that, <strong>at the end of the day the only thing you have is your integrity and your values</strong>. No matter what is thrown at you, you will always be left with a positive and proud feeling about yourself, because you chose the right attitude; you stood by your values, handled the situation with openness and maintained your integrity. Even if the outcome was bad.</p>
<p>What is the right attitude you may ask? The following will – probably – give you an idea of the direction you need to go. It’s definitely not easy and I am still working on it myself!</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="taking-responsibility-for-everything">Taking responsibility. For everything<a href="https://www.alexanderlolis.com/fight-or-flight-breathe-instead#taking-responsibility-for-everything" class="hash-link" aria-label="Direct link to Taking responsibility. For everything" title="Direct link to Taking responsibility. For everything" translate="no">​</a></h3>
<p>Taking responsibility does not apply only in situations in which you were personally involved; it rather stands for any situation that might come up and calls for action. It’s about reacting and trying to solve the problem at hand. Even if that does not necessarily guarantee success, you should see it as a challenge where you can influence the result instead of walking away while saying to yourself that “it’s not my problem”.</p>
<p><strong>Choosing to act, even if everything fails, will always be a self-empowering move rather than choosing to do nothing about it and allow the failure to just happen.</strong></p>
<p>Don’t get me wrong; Of course, I understand that this is not always fair and that people should just do their damn job and everybody will have one less thing to worry about. But…life is not fair, and people act stupidly. Even worse, they choose apathy very often. So, instead of feeling resentment and resignation all the time, I <strong>choose</strong> to act whenever I can and do not bitch about it. Any other alternative is simply not good enough for me.</p>
<p>However, the above is not meant to be used as an excuse when things go sideways. There is a huge and obvious difference between honestly <strong>striving for excellence</strong> by wholeheartedly doing your best and when you are doing the “oh well, at least I tried my best” self-assuring routine.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="your-truth-is-not-my-truth-help-me-understand-you">Your truth is not my truth. Help me understand you<a href="https://www.alexanderlolis.com/fight-or-flight-breathe-instead#your-truth-is-not-my-truth-help-me-understand-you" class="hash-link" aria-label="Direct link to Your truth is not my truth. Help me understand you" title="Direct link to Your truth is not my truth. Help me understand you" translate="no">​</a></h3>
<p>Each of us has our own mental model (a representation of the world, that is) which is shaped based on our knowledge, our personal experiences, and our culture. As soon as you realize that everybody has their own perspective and yours is just a version of the truth, you will better handle any future human interaction. Try to see the other person’s perspective and try to create a shared truth which will serve as the common ground for any productive conversation.</p>
<p><strong>Don’t get caught up in trying to enforce your truth</strong>, this is just a controlling attitude that will get you nowhere and hurt the relationship with the other person – and basically make you an unlikable douche.</p>
<p>If you are doing the listening, the first thing you need to do is to <strong>shut up</strong> and actually listen. <strong>ACTUALLY LISTEN</strong>. Do not shut down because you are too eager to respond. Your turn will come but until then be present, be open, and try to connect with the speaker. <strong>Acknowledge</strong> what the other person just said/felt and try to summarize it in order to ensure that you understood correctly. Even if for some reason you start feeling defensive and slowly getting angry, snap out of it and <strong>start an inquiry</strong> instead. This will help you understand better why is that what they are saying has put you on the defensive in the first place and provide you with a new perspective. Finally, make sure that the speaker has <strong>nothing else to add</strong> before you actually start replying. Manners!</p>
<p>If you are doing the speaking, <strong>make sure that you have a beginning, a middle, and an end</strong>, or else mumbling without the slightest trail of thought will exhaust the listener and you will lose their attention. <strong>Do not waste your listeners time</strong> by whining about [insert topic here] or petty annoyances. Instead, <strong>state your ideas/opinions, provide facts</strong> by supporting them with the appropriate research or observations, and <strong>recommend a course of action</strong>. In this way, the conversation will always have <strong>one direction;</strong> <em>forward</em>. <strong>Watch your listener’s reactions</strong> and <strong>ask questions</strong> to make sure they actually understood what you are trying to say. Last but not least, <strong>welcome any feedback</strong> and <strong>do not get defensive</strong>, even if that feedback challenges you. This is how both of you will learn something new from this conversation and make a step closer to an actual solution.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="stop-resisting-challenge-the-reasons-behind-your-opinions-and-your-actions">Stop resisting. Challenge the reasons behind your opinions and your actions<a href="https://www.alexanderlolis.com/fight-or-flight-breathe-instead#stop-resisting-challenge-the-reasons-behind-your-opinions-and-your-actions" class="hash-link" aria-label="Direct link to Stop resisting. Challenge the reasons behind your opinions and your actions" title="Direct link to Stop resisting. Challenge the reasons behind your opinions and your actions" translate="no">​</a></h3>
<p>Sometimes it’s hard to understand the real reasons why another person acts the way they do but it’s even harder to understand our own reasoning behind our own actions.</p>
<p>You need to <strong>ask yourself</strong> whether your opinions and your actions (or lack thereof) are based on your own personal gain/convenience or if you have your team’s/company’s best interest at heart.</p>
<p>Take a breath and <strong>create some space from your emotions</strong> (without suppressing them) in order to try to observe and understand what is going on within yourself and why you acted the way you did. The more time you give to this observation of self, the more self-aware you become and more able to reflect upon your feelings and your actions. As a result, you have a <strong>more objective perspective</strong> of yourself, and of course, more control.</p>
<p><strong>Think globally, not locally</strong>. Think long-term, not short-term. You are part of a larger community that shares a common goal, to fulfill the mission of the organization. Never lose sight of that and you will keep yourself in check and be more objective in a much easier way than you think.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="there-is-no-you-or-they-its-i-or-we">There is no “you” or “they”. It’s “I” or “we”<a href="https://www.alexanderlolis.com/fight-or-flight-breathe-instead#there-is-no-you-or-they-its-i-or-we" class="hash-link" aria-label="Direct link to There is no “you” or “they”. It’s “I” or “we”" title="Direct link to There is no “you” or “they”. It’s “I” or “we”" translate="no">​</a></h3>
<p>One of the things I despise the most (note to self: figure out why) is when someone within the organization, or even worst within the same team, tries to distance themselves from a problem by avoiding self-statements.</p>
<p>By using the subject “you” instead of “I” or “they” instead of “we” during a discussion, you create a negative experience for everyone involved. <strong>Non-self-statements imply blame and foster criticism</strong> towards others which in turn puts them into a defensive mood. If that does not ruin the discussion, it will definitely make it harder to continue smoothly and everyone sooner or later will start feeling a psychological drag.</p>
<p>The ironic thing about self-statements however, is that <strong>the person who avoids them <em>is the one being defensive</em> in the first place</strong>. Even though that person wants to be part of the solution, they do not understand that they must first embrace the problem as a member of the same team, mostly due to insecurity or emotional pressure for other reasons (even unknown to them most of the time).</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="self-actualization-infinite-motivation">Self-actualization. Infinite motivation<a href="https://www.alexanderlolis.com/fight-or-flight-breathe-instead#self-actualization-infinite-motivation" class="hash-link" aria-label="Direct link to Self-actualization. Infinite motivation" title="Direct link to Self-actualization. Infinite motivation" translate="no">​</a></h3>
<blockquote>
<p>A musician must make music, an artist must paint, a poet must write, if he is to be ultimately happy. What a man can be, he must be. This need we may call self-actualization.</p>
<p>Abraham H. Maslow</p>
</blockquote>
<p>What makes a top athlete to keep training even though they are already the best at what they do? What makes a top violinist keep practicing even though they have already mastered the toughest compositions? <strong>What motivates any of the people we admire</strong> to become something more and better from what they already are?</p>
<p>They do not do it for the money, the fame, or any other self-limiting reason. These are simply measures of achievement at this point. They do it because they want to. They play the game for the sake of the game and they try to get a little bit better every day because <strong>the need to improve and to keep testing themselves has no limit.</strong></p>
<p>The good news is that you do not need to be the best of the best in a field in order to reach your full potential. Based on Abraham Maslow, what motivates a person is a <a href="https://en.wikipedia.org/wiki/Maslow%27s_hierarchy_of_needs" target="_blank" rel="noopener noreferrer" class="">hierarchy of needs</a>. In short, when a more basic or psychological need is satisfied, we are no longer motivated by it and we need to change level in order to find motivation again. Of course, a person can have concurrent needs, but one of them is always stronger than the rest and it is the one that drives us. The order of our needs does not need to be progressive either. <strong>By deciding what is really important to you, and by conquering all your lower needs first, you can finally arrive at the only level of infinite source of motivation, creativity, and your highest level of performance; self-actualization.</strong></p>
<p>We spend a large part of our lives at work and that work must offer something more than just money. It has to be able to offer some sense of fulfillment. To be in a safe place where you can stretch your limits, to be able to challenge yourself, to test your character and skill. <strong>Do not waste your life in a working environment that pays the bills but only makes you sad and miserable every day.</strong></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="be-a-pro-at-least">Be a pro at least<a href="https://www.alexanderlolis.com/fight-or-flight-breathe-instead#be-a-pro-at-least" class="hash-link" aria-label="Direct link to Be a pro at least" title="Direct link to Be a pro at least" translate="no">​</a></h3>
<p>Even if you are not really motivated to embark on this psychological and emotional trip, even if you feel that it’s pointless to try and make your working environment a better place for everyone, then let me make it brutally simple for you; <strong>you are a professional</strong>, and you need to act accordingly within your workplace and respect its culture. If you are not satisfied, <strong>instead of creating noise and drama</strong>, do yourself and everyone else a favor and simply move on to somewhere else where you feel that it will be a better fit for you.</p>
<p>But I think that you can do better than that.</p>
<p><strong>Take the trip instead</strong>, because, even though achieving emotional mastery is not easy, its effects are reflected upon you and the ones around you much sooner than you think.</p>]]></content:encoded>
            <category>business</category>
            <category>culture</category>
            <category>teams</category>
        </item>
        <item>
            <title><![CDATA[Remote work tips - How NOT to snap]]></title>
            <link>https://www.alexanderlolis.com/remote-work-tips</link>
            <guid>https://www.alexanderlolis.com/remote-work-tips</guid>
            <pubDate>Sat, 18 Apr 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[Lot of these articles going around these days due to COVID-19, but I felt like giving my two cents since I’ve been a freelancer in the past, mostly working from home, so I know how it feels like.]]></description>
            <content:encoded><![CDATA[<p>Lot of these articles going around these days due to COVID-19, but I felt like giving my two cents since I’ve been a freelancer in the past, mostly working from home, so I know how it feels like.</p>
<p>Despite what everyone thinks about the awesomeness of working from the comfort of your couch, it’s definitely not easy. Αfter a while, no matter how many tips you are going to read, it’s up to you to try and achieve mental toughness.</p>
<p>The following is a small list of what works for me, and might work for you as well, but in the end everyone does their own thing to stay focused.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="keeparoutineatalltimes">Keep.a.routine.at.all.times.<a href="https://www.alexanderlolis.com/remote-work-tips#keeparoutineatalltimes" class="hash-link" aria-label="Direct link to Keep.a.routine.at.all.times." title="Direct link to Keep.a.routine.at.all.times." translate="no">​</a></h3>
<p>Although it sounds tempting to be in your PJs all day long, it is bad for your mentality since it will make you feel and act lazy. You need to maintain a routine as if you were going to a workspace outside your home.</p>
<p>Change your clothes. No need to dress in something formal but do not stay in your PJs. Follow your morning routine, make a small breakfast and some coffee or whatever else you usually do and start working. Persuade that brain of yours that you are not on vacation and try to be productive even if that [insert procrastination reason here] looks tempting.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="set-ground-rules-with-the-people-in-your-space">Set ground rules with the people in your space.<a href="https://www.alexanderlolis.com/remote-work-tips#set-ground-rules-with-the-people-in-your-space" class="hash-link" aria-label="Direct link to Set ground rules with the people in your space." title="Direct link to Set ground rules with the people in your space." translate="no">​</a></h3>
<p>If you live with other people make it clear to them that they need to act like you are not there. They should not annoy you even for the slightest thing, because, you are not really there.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="maintain-regular-hours-take-breaks-leave-home">Maintain regular hours. Take breaks. Leave home.<a href="https://www.alexanderlolis.com/remote-work-tips#maintain-regular-hours-take-breaks-leave-home" class="hash-link" aria-label="Direct link to Maintain regular hours. Take breaks. Leave home." title="Direct link to Maintain regular hours. Take breaks. Leave home." translate="no">​</a></h3>
<p>Don’t overdo it with work or else you will burn out very fast and the endgame here is the long run, not a few days in overdrive. Do not skip lunch breaks. Spend 45 minutes – 1 hour to it and use it as a chance to relax a bit. In the afternoon, go out for a short walk/running and empty your mind. I understand that there will be days which extra work is required but in order to have that extra energy you need to take good care of yourself and respect your limits.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="keep-a-dedicated-and-clean-office-space">Keep a dedicated and clean office space.<a href="https://www.alexanderlolis.com/remote-work-tips#keep-a-dedicated-and-clean-office-space" class="hash-link" aria-label="Direct link to Keep a dedicated and clean office space." title="Direct link to Keep a dedicated and clean office space." translate="no">​</a></h3>
<p>The important thing here is to use a different <strong>space</strong> for work and a different one for relaxing in order for you to associate it with different mind states. If you have a separate room, then that’s great. If not, it’s still ok. Like I said, it’s about mind states. Find the spot in your house that helps you get “in the zone” and keep it clean and tidy.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="socialize-with-colleagues-over-communicate">Socialize with colleagues. Over-communicate.<a href="https://www.alexanderlolis.com/remote-work-tips#socialize-with-colleagues-over-communicate" class="hash-link" aria-label="Direct link to Socialize with colleagues. Over-communicate." title="Direct link to Socialize with colleagues. Over-communicate." translate="no">​</a></h3>
<p>Remote work can create a distance between you and your colleagues, making it hard to be “in the loop” for things that might be happening in the office. It’s lucky for us that nowadays there are a lot of tools to help us communicate with other people in a more direct and live manner. Use these tools and if your company does not already have them then request from the person in charge to choose one for your organization. Keep in touch with your team every day and even try to make time for small talk.</p>
<p>And remember, consider yourself lucky during this crisis that you can work for your company from home. A lot of people cannot, since their physical presence is required.</p>]]></content:encoded>
            <category>self-help</category>
            <category>remote-work</category>
            <category>lifestyle</category>
        </item>
    </channel>
</rss>