<?xml version="1.0" encoding="UTF-8"?> <rss version="2.0" xmlns:media="http://search.yahoo.com/mrss/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/">
        <channel>
            <title>Lau de Bugs' Blog - feed</title>
            <description>Life and Software Development Blog</description>
            <link>https://laudebugs.me/feed</link>
            <atom:link href="https://www.laudebugs.me/api/rss?section=feed" rel="self" type="application/rss+xml"/>
            <language>en</language>
            <lastBuildDate>Sun, 16 Jul 2023 01:26:35 GMT</lastBuildDate>
            <copyright>All rights reserved 2023, Laurence B. Ininda</copyright>
            <category>belonging</category><category>new york</category><category>change</category><category>angular</category><category>conferences</category><category>nairobi</category><category>travel</category><category>git</category><category>productivity</category><category>home</category><category>memories</category><category>typescript</category><category>tips</category><category>nx</category><category>monorepos</category><category>rxjs</category><category>angular-material</category><category>theming</category><category>work</category><category>programming</category><category>imperfect work</category><category>javascript</category><category>dynamic components</category><category>graphql</category><category>aws lambda</category><category>apollo</category><category>career</category><category>coffee</category><category>guides</category><category>eslint</category><category>prettier</category><category>commitlint</category><category>commitizen</category><category>husky</category><category>automation</category><category>standard version</category><category>archive</category><category>college</category><category>mornings</category><category>past</category><category>future</category><category>winter</category><category>github</category><category>api</category><category>database</category><category>express</category><category>faith</category><category>life</category><category>being african</category><category>blackness</category><category>skin</category><category>24</category><category>lige</category><category>family</category><category>on writing</category><category></category><category>poems</category><category>lighthouse</category><category>node.js</category><category>python</category><category>translated thoughts</category><category>doubt</category>
            <image>
                <url>https://www.laudebugs.me/icons/icon-128x128.png</url>
                <title>Lau de Bugs' Blog - feed</title>
                <link>https://laudebugs.me/feed</link>
            </image>
            
            
            <item>
                <title>The Case Against Belonging</title>
                <link>https://www.laudebugs.me/journal/the-case-against-belonging</link>
                <pubDate>2023-07-09T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/journal/the-case-against-belonging</guid>
                <description>
                    <![CDATA[ How does one find belonging in a space where most are wandering spirits attempting with different upbringings and culture? Is finding belonging even a worthwhile goal?]]>
                </description>
                <content:encoded>
                    <![CDATA[ <p>It’s nearly seven years since I first took in the air in this strange landscape from her raging hot Texas summers to her soulless New York winters. She aches with scars from a wretched past while racing uncontrollably toward an unknown future. Her beauty is layered and otherworldly yet punctuated with a haunted, storied underbelly. I know too little of her. </p>
<p>Within me has been a growing watershed of my childhood fading into distant memories that has become fuzzy. I write to remember the smell of earth – mud, the feeling of the piercing thorns, the dew that awaited me in the mornings. These are strange memories now. I beckon them to fill this yearning for a place I once called home, a people that once occupied my sensibilities, food that once warmed my soul. I am caught in between two worlds – continuously dipping one foot into <i>This American Life</i> and yet never fully leaving my Kenyan selfhood behind. My body keeps searching for a sense of belonging that is becoming elusive. This unlearning of my assumptions, unravelling of new ways of thought, peeling off layers of identity is agonizing yet so necessary in my nervous efforts to battle a despair fueled by the nostalgia of a former life that seemed so effortless. </p>
<p>I am terribly homesick. To emerge from my chrysalis requires work – an emotional and mental exercise that does not feel natural. I would prefer sloth. My body senses that it feels alien(ated), estranged, misunderstood. What is this agony I inhabit? In engaging this still seemingly foreign world, it hesitates and dares to fold into dreams from another lifetime. Where does this body want to be? </p>
<p>Mid-conversation, I am listening to the other person’s attempt to fill this hostile void that occupies that space between us. When we hit an inflection point where our lived experience shares no commonality, the mind, body, and spirit are hyper-aware of this chasm beginning to form between I and <i>the other</i>. We each make appeals in this exchange – to be understood, to be met at in the middle. Questions. Are they listening? Am I paying attention? We each attempt to contextualize our stories to reel in the other into our respective worlds. When it flows, there is a feeding frenzy of curiosity. They become interesting. I find myself performing often as such – trying on various arcs of the same stories to navigate different spaces. It becomes exhausting. How does this body that was raised under the temperate tropical sun resist the urge to yield when stretched to its limits? </p>
<p>My heart’s instinct is bending towards distrust; to <i>othering</i> – embellishing difference to use as ammunition in waging emotional and ideological warfare within unfamiliar territories. I am naïve to other worlds. Novel encounters, however, present an opportunity to work through my assumptions, to befriend this tension within me and perhaps to chip away at the fear of being dissimilar. Belonging, on the other hand, assumes a constancy to retreat to whenever I wish. The search for belonging brings with it a desperate attempt to return to formerly familiar grounds, creating a feeling of dissatisfaction with people who think, talk and simply <i>are</i> different from me. This search disregards those who inhabit even the slightest lack of shared experience absolving us of any agency we have in redefining what it means to belong. When I have let go of this urge to look for familiarity, conversations have flowed into uncharted waters, and I have been able to peek into other’s ways of being and experiencing. I am attempting to practice letting go of the search to belong, and perhaps this can yield a deeper empathy with this strange land and the people within her. </p>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/the-case-against-belonging.png" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/the-case-against-belonging.png"/>
                    <media:credit>
                                <![CDATA[ Photo generated from <a href="https://www.midjourney.com/app/jobs/724d56be-6b04-4305-a75a-ea0042283585/"> Midjourney AI<a/> ]]>
                                </media:credit>
                    

            </item>
            
            <item>
                <title>What it takes to change the world</title>
                <link>https://www.laudebugs.me/journal/what-it-takes-to-change-the-world</link>
                <pubDate>2023-07-01T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/journal/what-it-takes-to-change-the-world</guid>
                <description>
                    <![CDATA[ Why it takes much more than a nebulous dream to effect real change in our immediate context, and by extension, in our broken world.]]>
                </description>
                <content:encoded>
                    <![CDATA[ <p>I have often heard the refrain that’s usually a rendition of “I want to change the world”. Swap out “the world” with “my country”, “my continent”, “my community” and this can be generalized to the ambitions that burden a plenty the intelligentsia in most underdeveloped or war-torn nations. While this kind of statement is not one I hear often in my time in the United States from the youngings around me, I do wonder a couple of things about how young and hungry people come to make such a seemingly monumental claim towards their future and that of their people.</p>
<p>At the heart of it is a recognition of the problems around them and the world of possibility. 
These problems could be that there’s too much dust on the road, there’s a pile of trash blocking the drainage of rainwater, the traffic police are collecting bribes from Public Service Vehicle drivers or having blackouts that last days. All these are inefficiencies that can and have become so common that they blend into a person’s lived experience that they don’t think twice about them. In order to speak out, one has to observe and learn that there is a world outside of their own where such issues have been solved. This learning can happen through reading, watching, and even better experiencing such a world. The disconnect between articulating the problems and finding solutions to them comes up in understanding what it takes to solve the problem. One then realizes that solving problems isn’t just about building systems but enabling the people to understand why the systems exist and what problems they solve. </p>
<p>I left my country of birth with a naïve understanding of the complexity of the problems around me. Peeling back the layers of the kind of work it takes to get people to do things can be a heartbreaking process that can leave one hopeless, cynical, and angry. My 9 to 5 brings at times leaves me feeling like I’m not doing anything to chip away at anything worthwhile. It’s easy to get comfortable, and even worse – complacent earning a living and building a life of one’s own in a world that feels like it’s burning down, and I have no active part to play in it to make it better. And this – the being okay with the status quo, becoming averse to change and unwilling to confront my fears is a temptation is that is becoming ever more palpable each waking day. I can see the things that not quite right and could be better – points of friction at work, the uncomfortable conversations that need to happen, or an even more mundane task of cleaning the pile of dirty dishes from the last meal. Do I ignore it or try to explore what might be uncomfortable?  </p>
<p>Growth isn’t easy, isn’t instinctual. It requires facing deep-rooted insecurity about one’s on ability; requires recognition of one’s own inadequacy; requires facing challenges with humility and vulnerability in the moment. Opportunities present themselves in situations where there’s friction of any kind and they provide moments of potential learning. Two possible paths of action usually emerge – either embracing the discomfort and working through it or folding giving in through avoidance and casting blame on others. The latter often boils down to pride. Pride is saying to oneself that I’ll look at his later and not reach out for help now when it’s needed; it’s finding an excuse to fill in the time and wait it out. “We’ll see what happens” is a statement I’ve often used that I am beginning to dislike because it absolves me of my agency to change what’s wrong with the situation. 
What it takes is confrontation, not retreat.</p>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/what-it-takes-to-change-the-world.png" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/what-it-takes-to-change-the-world.png"/>
                    <media:credit>
                                <![CDATA[ Photo generated from <a href="https://www.midjourney.com/app/jobs/b27a5771-2d18-4687-8839-0f8f210901f8/"> Midjourney AI<a/> ]]>
                                </media:credit>
                    

            </item>
            
            <item>
                <title>A Freshman at NgConf 2023</title>
                <link>https://www.laudebugs.me/dev/a-freshman-at-ng-conf-2023</link>
                <pubDate>2023-06-19T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/dev/a-freshman-at-ng-conf-2023</guid>
                <description>
                    <![CDATA[ NgConf 2023 was my first developer conference. It was exciting to attend it in person and engage with other developers working with Angular as well as leave with some helpful takeaways to take into my day-to-day work.]]>
                </description>
                <content:encoded>
                    <![CDATA[ <p>In most of my work and experience builiding applications from my home office, days can morph into weeks of solo-work with the occasional pair programming sessions and routine standup meetings.
To that end, I can admit that I was anxious and very much naive to the craft of attending a developer conference which seemed to me to lie at the center of networking with other developers, learning what&#39;s new in the Angular space and meeting speakers and sponsors.</p>
<p>The Angular Framework is advancing and keeping up by walking the fine line of adopting new features and bringing the community along in the process.
For developers new to the framework, Angular is trying to be more approachable by adding new features such as standalone components that make it easier to bootstrap an Angular application.
More experienced developers are also more excited by the push to embrace features that improve parts of the framework such as hydration, reactivity and performance.
Suffice to say, the conference showcased the bleeding-edge of what the Angular team and community are doing to push the framework and the web in general forward.
The turnout fielded developers from small and large teams as well as those who had just began working in Angular to those who have been there from the Angular.js days.</p>
<p>So what did I take out of the conference?</p>
<p>I learned something new about the framework - how to use auxilliary routes in Angular and how to type the <code>ngTemplateOutletContext</code> directive.
Speaking during breakfast and lunch with developers working in different contexts with Angular, there was this general feeling of excitement about how the framework continually enabled them to build better applications for their users while improving the developer experience(DX) with each release as the framework is embracing tooling from the community such by:</p>
<ul>
<li>integrating tools such as <a href="https://angular.io/guide/esbuild">Vite and esbuild</a> that speed up local development and the build process</li>
<li>embracing more modern testing tools by integrating <a href="https://blog.angular.io/moving-angular-cli-to-jest-and-web-test-runner-ef85ef69ceca">jest support and migrating away from Karma to Web Test Runner</a> as well as having support for community tooling in <a href="https://docs.cypress.io/guides/component-testing/angular/overview">Cypress</a></li>
<li><a href="https://angular.io/guide/hydration">improving the hydration process</a> for server side rendered applications</li>
</ul>
<p>Can Angular be exciting? I don&#39;t know if that&#39;s even the right question to ask.
As more applications are getting older with time, can Angular provide a platform where developers can not only maintain those older applcations but improve them with each new version of Angular? More so, can Angular be the framework of choice for developers building new applications? These seem to be deeper questions that the community is tackling.
The work to improve the framework is continuing with improvements on reactivity, performance and hydration just to name a few areas where work is already in progress.</p>
<p>I left the conference a lot more hopeful and excited to get back to work with my team through the various problems that software development teams face each day.
As much as a lot of grunt work is needed, software delivery teams are people who work in specific group and individual contexts.
The more important lessons from the conference were how to contribute more effectively to the success of my team as we build the various products to meet business needs with the knowledge that the tools out there are meant to help us along the way.</p>
<hr>
<p><a href="https://github.com/laudebugs/conference-notes/blob/main/conferences/ngconf/ngconf-2023.md">Notes from the conference can be found here</a></p>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/a-freshman-at-ng-conf-2023.png" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/a-freshman-at-ng-conf-2023.png"/>
                    <media:credit>
                                <![CDATA[ Photo generated from <a href="https://www.midjourney.com/app/jobs/ec93a7f0-5970-491f-a3a3-606799aae80d/"> Midjourney AI<a/> ]]>
                                </media:credit>
                    

            </item>
            
            <item>
                <title>I am here now again</title>
                <link>https://www.laudebugs.me/journal/i-am-here-now-again</link>
                <pubDate>2023-04-09T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/journal/i-am-here-now-again</guid>
                <description>
                    <![CDATA[ On my recent trip back home to Kenya and how it helped me slow down, reconnect with what's important and gave me time to reflect on my getting back to the hectic life in New York.]]>
                </description>
                <content:encoded>
                    <![CDATA[ <p>I’ve woken up early.
My body had been used to getting up and going somewhere.
Every day I had something to do, someone to see, some place to be – but yesterday I got back to the life I had put on hold for several weeks to immerse myself into a more familiar one of my childhood and early adulthood.
There’s a sense in which I had left my home in New York to run towards something I already knew back home in Kenya – the places I had been, the people, the food, the air – a more comfortable, easy, and less demanding life.
While at home in Nairobi, I was very much unaware of the question of how I fit in, or where I belong, but rather slipped back into a sense of being that I understood.
And now that I’m back to the big apple, there’s an admission that I don’t know how to do life right by myself – estranged from the familiar.
I worry that it isn’t as easy to make long-lasting friendships.
If I am to build my life here, the need to belong, to be seen by those around me, to matter is as much a necessity as it is a thing that requires work that at times becomes exhausting.
The allure of retreating, of giving in to the sense of being alone can grow stronger and mightier by the day.
How does one exist with a detachment from those who are closest to you; when whatever one does may go unseen, unrecognized and to do that in a society can feel like it simply doesn’t care can breed a sense hopelessness.</p>
<p>How easy it was to walk back into an old life, to pick up the pieces, to reconnect with old friends.
The food tasted the way I remember it – motherly.
Caramelized onions littered the wooden spatula dripping saucy soup from the chunks of chicken stew and landing squarely on the smoking hot heavenly ugali.
Salutations turned into unplanned conversations with neighbors talking in unrefined tongues with tones of their upbringing dotting their speech.
They smelled like earth, rust, Omo, Elianto.
Family and friends always smile at me.
Passersby look at me like they know that I am up to no good.
On my way to wherever I’m going, fellow passengers seat a little too close for comfort next to me in the matatu, in silence but listening to whatever tune was playing in the matatu – Classic FM or a DJ mix preloaded onto a USB stick.
No matter how gingerly I step, dust creeps up on me and the next time I glance down, it has formed layers on my sneakers as I take my morning run on a once untrodden but now tarmacked road leading up to the primary school I went to as a kid.
The showers are cold as there’s no such thing as things always working.
They break, often.
My mother has a plumber over to fix the water heater and the next day there’s water leaking through the roof.
The plumber returns to fix it again.
It rains, actually rains – showers pouring in the middle of the night with lightening and thunder so much so that I wake up and listen to it for a few minutes.
The lightning strikes down a pole and there’s no power at home for a few days.
Things take time to happen, and people are accustomed to it – have developed a patience with the fact that very few things operate on schedule.</p>
<p>I am here now.
Is this home again now? There is a wrath that this city brings with it and it is coming – where I will be convinced that I will have to walk, talk, work faster and at the height of it a crushing feeling that it is not okay to just sit in silence when there are so many things to be done, new TV shows to watch, restaurants to check out, events to go to, memes to share, trends to try out.
I will become disillusioned by all this and will try to keep up and fail; will try to fill my calendar with things to do and places to be and things to try out.
I read less these days, find it hard to curve out time to write.
Days go by without taking stock of where my heart and spirit have wandered.
Right at the very moment my flight touched down at JFK my mind went racing into all the things I need to catch up on, the work, people, events, never-ending to-dos.
Lord, grant me the grace and patience to learn to live in the moment.
There’s a church service in two hours and I need to do groceries, organize my room, reach out to friends.
I’ve already been invited over for dinner.
Lord, give me faith and the strength to turn my face toward you and lean on you for understanding, for hope, for peace when my heart wants to wander, want to give up, becomes restless.
I am calculating how much time I have in order to finish this piece and then go do the next thing, then the next thing after that – everything must fit in to whatever little time I have in this day.
Things cost twice as much, even three times more – but even this is an understatement of how I finance the cost of living in this city.
I must be a bit mad to be okay with paying $6 or more for a cup of coffee.
Inflation brings this up to about $8.
Is this a necessary pleasure? To do things cost money and I have to check people’s calendars if I want to do most things with friends.
Is this a necessary labor? Lord, show me how to love others, how to learn from them, and embrace their culture and being; open my eyes to the things that are of importance in my day to day.
Tomorrow morning I am planning take my run, but I have a call scheduled before work and I want to do some reading as well and make coffee and breakfast and plan for lunch and dinner.
It’s already beginning to sound overwhelming, but I am here now and let me begin reliving this life I have now come back to.</p>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/i-am-here-now-again.png" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/i-am-here-now-again.png"/>
                    <media:credit>
                                <![CDATA[ Photo generated from <a href="https://www.midjourney.com/app/jobs/f26c3a36-ae73-4d90-9435-f4105ec5b8fa/"> Midjourney AI<a/> ]]>
                                </media:credit>
                    

            </item>
            
            <item>
                <title>Improving Your Git Workflows with Aliases</title>
                <link>https://www.laudebugs.me/dev/improve-your-git-workflows-with-aliases</link>
                <pubDate>2023-03-11T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/dev/improve-your-git-workflows-with-aliases</guid>
                <description>
                    <![CDATA[ There are plenty of times when I have found myself looking up git commands that I would like to execute in my day to day whether on my day job or side project work. As such, git provides the ability to configure aliases — short commands that represent longer commands that git executes.]]>
                </description>
                <content:encoded>
                    <![CDATA[ <p>There are plenty of times when I have found myself looking up git commands that I would like to execute in my day to day whether on my day job or side project work. As such, git provides the ability to configure aliases — short commands that represent longer commands that git executes.</p>
<h2 id="configuring-git-aliases">Configuring Git Aliases</h2>
<p>A git alias can be set up by running the command <code>git config --global alias.&lt;alias-name&gt; &lt;command-to-execute&gt;</code>. Where <code>&lt;alias-name&gt;</code> represents the command that triggers the <code>&lt;command-to-execute&gt;</code>. A simple example would be setting up a git alias, <code>git ac</code> that will take a commit message, stages all files and commit them all in one go. Setting up this command would look like:</p>
<pre><code class="language-txt">git config --global alias.ac &quot;\!git add -A &amp;&amp; git commit -S -m&quot;
</code></pre>
<p>This command then updates the global <code>.gitconfig</code> file, by adding an entry under <code>[alias]</code> called <code>ac</code> that enables this command to be executed in any git repository:</p>
<pre><code class="language-txt">[alias]
    ac = !git add -A &amp;&amp; git commit -S -m
</code></pre>
<p>After editing some files in a git repository, running <code>git ac &quot;docs: update notes&quot;</code> stages all changed files in that repository and commits them using the commit message <code>&quot;docs: update notes&quot;</code>.</p>
<h2 id="a-list-of-useful-git-aliases">A list of Useful Git Aliases</h2>
<p>Herein, therefore, is a list of git aliases that drive my daily work.</p>
<p>I’ll group the commands into (a) Commands within a git repository and (b) commands within a <strong>bare</strong> git repository</p>
<h3 id="within-a-normal-git-repository">Within a normal git repository</h3>
<Note>
These Commands can also be performed within a worktree in a bare git repository
</Note>

<ol>
<li><p><code>ac</code> - Pass in a commit message to the git alias. The command stage all files (<code>git add -A</code>) and commits with the commit message passed in.</p>
<pre><code class="language-txt">ac = !git add -A &amp;&amp; git commit -S -m
</code></pre>
</li>
<li><p><code>add-string</code> - Among all the changed files in a git repository, Stage only the files where the diff contains a certain substring. Fos instance, if one would want to stage only files that contain the substring <code>randomFilterFunction</code>, then, executing the command <code>git add-string &quot;randomFilterFunction&quot;</code> would only stage that files where the diff contains the substring <code>“randomFilterFunction”</code>.</p>
<pre><code class="language-txt">add-string =  &quot;!sh -c \&quot;git diff -G &#39;$1&#39; --name-only | xargs git add\&quot; -&quot;
</code></pre>
</li>
<li><p><code>last-checked</code> - Within a git repository, find the last <code>n</code> checked out branches.</p>
<pre><code class="language-txt">last-checked = !git reflog | grep -i &#39;checkout: moving&#39; | head -n
</code></pre>
<p> This is useful when trying to find out the last few branches that were worked on, especially when switching between branches. For instance, running <code>git last-checked 5</code> would return output similar to:</p>
<pre><code class="language-txt">ae956a5 HEAD@{4}: checkout: moving from main to chore/update-new-env-gh
ae956a5 HEAD@{5}: checkout: moving from chore/update-node-ci to main
ae956a5 HEAD@{7}: checkout: moving from main to chore/update-node-ci
aafa50f HEAD@{9}: checkout: moving from docs/update to main
fba6d0a HEAD@{11}: checkout: moving from fix/github-actions-permissions to docs/update
</code></pre>
</li>
<li><p><code>parent</code> - For a given branch, find the branch from which the current branch was created from. <a href="https://stackoverflow.com/questions/3161204/how-to-find-the-nearest-parent-of-a-git-branch">Source for this git alias command on StackOverflow.</a></p>
<pre><code class="language-txt">parent = &quot;!git show-branch | grep &#39;*&#39; | grep -v \&quot;$(git rev-parse --abbrev-ref HEAD)\&quot; | head -n1 | sed &#39;s/.*\\[\\(.*\\)\\].*/\\1/&#39; | sed &#39;s/[\\^~].*//&#39; #&quot;
</code></pre>
</li>
<li><p><code>push-origin</code> - Sets up the upstream and pushes the local branch to the remote origin. If a branch is created locally, one would have to publish the branch and push to it. This command provides an easy interface for that process from the command line.</p>
<pre><code class="language-txt">push-origin = !git push --set-upstream origin \&quot;$(git rev-parse --abbrev-ref HEAD)\&quot;
</code></pre>
</li>
<li><p><code>uncommit</code> - This resets the last commit made within a git repository.</p>
<pre><code class="language-txt">uncommit = reset --soft HEAD~1
</code></pre>
</li>
<li><p><code>cb</code> - An alias for the command: <code>git checkout -b {branch_name}</code> - Checks out a new branch</p>
<pre><code class="language-txt">cb = checkout -b
</code></pre>
</li>
<li><p><code>adc</code> - Stages and commits all the edited files in a git repo</p>
<pre><code class="language-txt">adc = !git add -A &amp;&amp; git commit -S -m
</code></pre>
</li>
</ol>
<h3 id="working-with-a-bare-git-repository">Working with a bare git repository</h3>
<Note>
Working within a git repository provides much needed functionality. However, bare repositories with git worktrees. provide an opportunity to switch in between different pieces of work seamlessly, without having to clone the repository over and over again.
</Note>

<ol>
<li><p><code>clone-bare</code> - Pass in a git url and clone into a bare repository.</p>
<pre><code class="language-txt">clone-bare = !git clone --bare
</code></pre>
</li>
<li><p><code>wa</code> -”worktree add” - Within a bare repository, this git alias takes two commands, the first being the new branch name, the second being the base branch. The new worktree will be located at the path that matches the branch name and based on the base branch.</p>
<p> Note: The Command <code>git rev-parse --git-common-dir</code> ensures that worktree paths are always generated from the root of the bare repo</p>
<pre><code class="language-txt">wa = &quot;!sh -c \&quot;cd $(git rev-parse --git-common-dir) &amp;&amp; git worktree add --no-track -b &#39;$1&#39; &#39;$1&#39; &#39;$2&#39;\&quot; -&quot;
</code></pre>
</li>
<li><p><code>fetch-some</code> - Inside a bare repository, fetch and update the local branches  of the comma-delimited list of branches. This is useful if one would want to update the local branches with their remote updates.</p>
<pre><code class="language-txt">fetch-some = &quot;!f() { IFS=&#39;,&#39;; for b in $1; do git fetch origin $b:$b; done; unset IFS; }; f&quot;
</code></pre>
</li>
<li><p><code>fetch-all</code> - For all the worktrees in a bare repository, update with their remotes. This is the equivalent of going into each of the worktrees and running <code>git pull</code></p>
<pre><code class="language-txt">fetch-all = &quot;!git for-each-ref --format=&#39;%(refname:short)&#39; refs/heads/ | while read branch; do git fetch origin $branch:$branch; done&quot;
</code></pre>
</li>
</ol>
<h3 id="the-complete-list">The Complete List</h3>
<p>All the git aliases mentioned above can be directly added to the <code>~/.gitconfig</code> file via a code editor.</p>
<pre><code class="language-txt"># The .gitconfig file

[alias]
        # Pass in a commit Message, This will stage ALL the changed files and commit with the provided commit message
    ac = !git add -A &amp;&amp; git commit -S -m

    # Filter changed files based on a string and ONLY stage files that contain that substring in the diff
    add-string =  &quot;!sh -c \&quot;git diff -G &#39;$1&#39; --name-only | xargs git add\&quot; -&quot;

    c = !git commit -S -m

    # This clones a git repository into bare repository
    clone-bare = !git clone --bare

    # For each the comma-delimited x of provided input a,b,c,...,n run git fetch origin x:x
    fetch-some = &quot;!f() { IFS=&#39;,&#39;; for b in $1; do git fetch origin $b:$b; done; unset IFS; }; f&quot;

    # For each branch y in a git bare repo, run git fetch origin y:y
    fetch-all = &quot;!git for-each-ref --format=&#39;%(refname:short)&#39; refs/heads/ | while read branch; do git fetch origin $branch:$branch; done&quot;

    # List the last n checked out branches in a git repository
    last-checked = !git reflog | grep -i &#39;checkout: moving&#39; | head -n

    # lists the &quot;parent&quot; branch for a given branch
    # Source: https://stackoverflow.com/questions/3161204/how-to-find-the-nearest-parent-of-a-git-branch
    parent = &quot;!git show-branch | grep &#39;*&#39; | grep -v \&quot;$(git rev-parse --abbrev-ref HEAD)\&quot; | head -n1 | sed &#39;s/.*\\[\\(.*\\)\\].*/\\1/&#39; | sed &#39;s/[\\^~].*//&#39; #&quot;

    # Sets up the upstream and pushes the local branch to the remote origin
    push-origin = !git push --set-upstream origin \&quot;$(git rev-parse --abbrev-ref HEAD)\&quot;

    # This resets the last commit made
    uncommit = reset --soft HEAD~1

    # This git alias takes two commands, the first being the new branch name, the second being the base branch
    # The new worktree will be located at the path that matches the branch name and based on the base branch
    # Git Worktree Documentation: https://git-scm.com/docs/git-worktree
    # The Command `git rev-parse --git-common-dir ensures that worktree paths are always generated from the root of the bare repo
    wa = &quot;!sh -c \&quot;cd $(git rev-parse --git-common-dir) &amp;&amp; git worktree add --no-track -b &#39;$1&#39; &#39;$1&#39; &#39;$2&#39;\&quot; -&quot;

    # Adds all the files in the current directory and commits with the provided commit message
    adc = !git add -A &amp;&amp; git commit -S -m

    # checkout a new branch
    cb = checkout -b
</code></pre>
<h2 id="references">References</h2>
<ul>
<li><a href="https://www.git-scm.com/book/en/v2/Git-Basics-Git-Aliases#_git_aliases">Git Documentation on Git Aliases</a></li>
<li><a href="https://www.atlassian.com/git/tutorials/git-alias">Atlassian Guide on Git Alias</a></li>
<li><a href="https://git-scm.com/docs/git-worktree">Git Worktree Documentation</a></li>
<li><a href="https://github.com/laudebugs/uses/blob/master/dotfiles/.gitconfig">A reference to my own <code>.gitconfig</code> file</a></li>
</ul>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/improve-your-git-workflows-with-aliases (1).jpeg" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/improve-your-git-workflows-with-aliases (1).jpeg"/>
                    <media:credit>
                                <![CDATA[ Photo by <a href="https://unsplash.com/@mikes1978?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Michal Pech</a> on <a href="https://unsplash.com/photos/xS5bOLKCSLw?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a> ]]>
                                </media:credit>
                    

            </item>
            
            <item>
                <title>Of Everything Past And Present</title>
                <link>https://www.laudebugs.me/journal/of-everything-past-and-present</link>
                <pubDate>2023-03-01T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/journal/of-everything-past-and-present</guid>
                <description>
                    <![CDATA[ I am home for the first time in a while and the memories of my childhood home have something to say about my being away. What questions am I being asked to stop ignoring and confront with a ruthless honesty?]]>
                </description>
                <content:encoded>
                    <![CDATA[ <p>I am home again.
There’s a little bit of everything in here.
Boxes full of newspaper bits and torn magazines, photos and correspondence, cables and pieces of old tech, half-filled notebooks and dried up pens, hair combs and the hair from the combs, grade reports and exam papers – the family’s history contained in bits and pieces in several of these boxes sprayed throughout the house.
This house that I grew up in feels like a tiny house although is in fact much grander than my New York apartment.
It has succumbed to dust that has settled upon dust to form a crust on the corners and crevices.
Not much has changed.
Rather everything has aged – frozen five, ten, fifteen years from a past they whence appeared.
Nothing is new, and I dislike this feeling of how old things seem.
I rummage through some boxes and can tell a story, one too many of our family’s past.
A past that isn’t talked about on a regular basis.
Mementos my growing up in this family flood into focus.
But now, I am surrounded by siblings who are worlds apart from my immediate experience – having moved into their own houses as I explore this museum of our past.</p>
<p>I find myself organizing, separating the books from papers from photos from trash.
I look around and make a cup of instant coffee.
How quickly I’ve found myself losing the taste for fresh coffee.
Patterns I formed while away fall to the wayside in this malaise of the past.
Present tense in this old familiar place requires other forms of being.
I find myself being absorbed into old routines of when I was last home.
I sense some distance from my peers who have found themselves outside of home through finding one of their own.
Do I have a say as to where all the clutter should go? Should I invest myself into this lifetime project to rid ourselves of the anguish that we have collected over years? How does one even begin? Is the kind of resolve required for this process contained in me? What is my complicity in all this?</p>
<p>How much of my motivations are a reflection of my desire for a sense of accomplishment? My mother has spent her years living and being in the way she knows best – a life that cannot become mine no matter how much I try.
I am me and she is her.
The qualities that I value seem to be at odds with who I am seeing her to be.
Yet, just like her, I have started this slow accumulation of things, memories, notes, experiences that have years to fully mature and someday overflow into a cluttered house that I will one day call my home.
This aversion to home seems to come from a misunderstanding of the kind of person my mother is – a collector of memories fueled by the refusal to simply let things fade away into mercy of the brain’s short memory.
She insists on taking pictures, let’s things bloom to an old age, keeps a record of things she has acquired since she can remember.
These are the simple patterns whence every aspect of her life flows from.
Yet, I seek the very novelty that has drawn me farther away from knowing a lifetime of living.
Five years from now, how can I remember that things I liked, kind of food I ate, books I read, people I talked to if I throw away anchors to the person I am becoming.
And that, is perhaps why I turn to this form of recording through words, though inadequate it is to keep a true account of being, of seeing, of understanding this world and what it lays before me.
Writing, however, can only be a tool in the attempt to confront my past.
It can take me to the gate, but I must enter it myself, and walk through the thorny bushes, jump the cliffs, and brace the storms.
Then, just maybe, I will again find the will to write about what I find on the other side of this journey.</p>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/of-everything-past-and-present.jpeg" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/of-everything-past-and-present.jpeg"/>
                    <media:credit>
                                <![CDATA[ Photo by <a href="https://unsplash.com/@alexrussellsaw?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Alex Russell-Saw</a> on <a href="https://unsplash.com/photos/bLhafHPXuJE?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a> ]]>
                                </media:credit>
                    

            </item>
            
            <item>
                <title>TypeScript Type Utilities and Functions</title>
                <link>https://www.laudebugs.me/dev/typescript-type-utilities-and-functions</link>
                <pubDate>2023-02-07T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/dev/typescript-type-utilities-and-functions</guid>
                <description>
                    <![CDATA[ A list of useful utitity types and functions that I use across various projects.]]>
                </description>
                <content:encoded>
                    <![CDATA[ <p>Here are some utility types and functions that I have found useful that are not enough to qualify publishing them into a library but I end up copy-pasting for use in between projects.</p>
<h1 id="custom-utility-types">Custom Utility Types</h1>
<ol>
<li><p><code>FilteredType</code> - A Type that filters down the <code>TypeToFilter</code> to a tuple of types that match the <code>Condition</code></p>
<pre><code class="language-ts">type FilteredType&lt;TypeToFilter, Condition&gt; = {
    [K in keyof TypeToFilter]: TypeToFilter[K] extends Condition ? K : never
}[keyof TypeToFilter]
</code></pre>
</li>
<li><p><code>Constructable&lt;T&gt;</code>- a type that matches a type that can be instantiated, i.e. classes</p>
<pre><code class="language-ts">type Constructable&lt;T&gt; = new (...args: any[]) =&gt; T
</code></pre>
</li>
<li><p><code>ValueOf</code> - For a given type, this filters down to a tuple of the values of the type</p>
<pre><code class="language-ts">type ValueOf&lt;T&gt; = T[keyof T]
</code></pre>
</li>
<li><p><code>DeepPartial</code> - Makes all nested properties of an object to be Optional.</p>
<pre><code class="language-ts">type DeepPartial&lt;T&gt; = T extends Object ? {
    [K in keyof T] : DeepPartial&lt;T[K]&gt;
} : T
</code></pre>
</li>
</ol>
<h1 id="utility-functions">Utility Functions</h1>
<ol>
<li><p>The <code>filterKeys</code> type filters a type based on the regex provided. This is useful if you would like to get the keys of an object that meet a certain criteria.</p>
<pre><code class="language-ts">function filterKeys&lt;T extends Object, K extends RegExp&gt;(source: T, regExp: K): FilteredType&lt;T, K&gt;[]{
  return Object.keys(source).filter((key) =&gt;
    regExp.test(key)
  ) as FilteredType&lt;T, K&gt;[]
}
</code></pre>
</li>
<li><p><code>getConstructorParams</code> - this returns a list of the constructor parameters of the class. We can also utilize the <code>Constructable&lt;T&gt;</code> type we defined earlier and pass the class into the function:</p>
<pre><code class="language-ts">/* as defined above in the utility types */
type Constructable&lt;T&gt; = new (...args: any[]) =&gt; T

function getConstructorParams&lt;T&gt;(constructor: Constructable&lt;T&gt;) {
    const params = constructor.toString().match(/\(([^)]*)\)/)?.[1]
    return params?.split(&#39;,&#39;).map((param) =&gt; param.trim()) ?? []
}
</code></pre>
</li>
</ol>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/typescript-type-utilities-and-functions.jpeg" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/typescript-type-utilities-and-functions.jpeg"/>
                    <media:credit>
                                <![CDATA[ Photo by <a href="https://unsplash.com/@everhooder?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">ål nik</a> on <a href="https://unsplash.com/photos/77HrB1xRZVc?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a> ]]>
                                </media:credit>
                    

            </item>
            
            <item>
                <title>Guarding against Unsaved Changes in Angular Apps using Route Guards</title>
                <link>https://www.laudebugs.me/dev/guarding-against-unsaved-changes-with-route-guards-angular</link>
                <pubDate>2023-02-04T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/dev/guarding-against-unsaved-changes-with-route-guards-angular</guid>
                <description>
                    <![CDATA[ How to utilize route guards to inform users of unsaved changes when navigating to a different page in Angular apps.]]>
                </description>
                <content:encoded>
                    <![CDATA[ <p>We’ve all sometimes mistakingly closed a document editor with a paragraph or two of unsaved edits, or that browser tab where we were drafting an email. Most of the time, when we attempt to navigate away to other parts of these applications, we get a reminder through a dialog box that we have some information that hasn’t yet been synced in the cloud (if the particular app in question even has an autosave feature).</p>
<p>Within Angular applications, we can leverage the <code>CanDeactivate</code> interface to implement a route guard which can then be added to the <a href="https://angular.io/api/router/Route#properties">route’s <code>canDeactivate</code> property</a> to notify a user that they have unsaved changes in their app.</p>
<h2 id="defining-the-component-that-can-deactivate-navigation">Defining the Component that can deactivate navigation</h2>
<Aside>

<p>The <code>CanDeactivate</code> is an interface that a component has to implement that allows it to “intercept” navigation calls and cancel or proceed with navigation.</p>
</Aside>

<p>The component that checks whether navigation can proceed or be cancelled contains a function, <code>canDeactivate</code>,  that can be called in the route guard to check whether or not navigation away from the component or page can continue or be cancelled.</p>
<p>An interface can be defined which will be implemented by our component. The function to be called can have one of the following return types:</p>
<pre><code class="language-tsx">type CanDeactivateFn = (...args: any[]) =&gt; Observable&lt;boolean | UrlTree&gt; | Promise&lt;boolean | UrlTree&gt; | boolean | UrlTree;
</code></pre>
<p>In our case, we’ll stick to <code>boolean | Observable&lt;boolean&gt;</code>. Our component will call it’s <code>canDeactivate</code> function and return <code>boolean | Observable&lt;boolean&gt;</code> depending on whatever checks the component runs to determine this.</p>
<p>So we have as our interface:</p>
<pre><code class="language-tsx">// deactivatable-component.interface.ts
export interface DeactivatableComponent {
    canDeactivate: () =&gt; boolean | Observable&lt;boolean&gt;
}
</code></pre>
<p>We can now implement this interface in a component. Assuming we have a form page that would possibly contains unsaved changes, the component has the following functions:</p>
<ul>
<li><code>containsUnsavedChanges</code> - a function that returns <code>true</code> or <code>false</code> checking if the form values have been saved by the user. This implementation could be enhanced to fit any use case — even checking the server for unsaved changes or invalid values and so forth</li>
<li><code>ngAfterViewInit</code> - this is a life cycle hook that updates the form with the values saved in local storage. These values could be updated from the state and/or the database</li>
<li><code>save</code> - this function updates the state when the user clicks the “Save” button.</li>
<li><code>canDeactivate</code> - this is our implementation of the <code>CanDeactivate</code> interface that our component implements. This function checks whether changes have been saved when a user tries to navigate away from the page, and if not, opens a dialog to confirm whether the user would like to proceed with or without saving the changes</li>
</ul>
<pre><code class="language-tsx">import { AfterContentInit, Component, ViewChild } from &#39;@angular/core&#39;
import { NgForm } from &#39;@angular/forms&#39;
import { MatDialog } from &#39;@angular/material/dialog&#39;
import { tap } from &#39;rxjs&#39;
import { CoreModule } from &#39;../../core/core.module&#39;
import { DeactivatableComponent } from &#39;../../unsaved-changes.guard&#39;
import { UnsavedChangesDialog } from &#39;../unsaved-changes-dialog.component&#39;

@Component({
        standalone: true,
    imports: [CoreModule],
    templateUrl: &#39;./favorites-form.component.html&#39;,
    styles: [
        `
            h1{
                text-align: center;
            }
            form{
                margin: 1em;
                display: grid;
                gap: 1em;
            }
            footer{
                display: flex;
                justify-content: center;
                align-items: center;
                flex-direction: column;

            }
            .unsaved-changes{
                margin-bottom: 0.5em;
            }
        `,
    ],
})
export FormComponent implements DeactivatableComponent{
        /** Our favorites object to save changes */
    private favorites = { movie: &#39;&#39;, tvShow: &#39;&#39; }
    /** Read the form from the Template */
    @ViewChild(&#39;FavoritesForm&#39;, { static: true }) favoritesForm!: NgForm

    constructor(private dialog: MatDialog) {}

    ngAfterContentInit() {
        /** We need to check the next tick since the controls are not registered yet */
        setTimeout(() =&gt;
            this.favoritesForm.setValue(JSON.parse(window.localStorage?.getItem(&#39;favorites&#39;)) ?? this.favorites)
        )
        this.favorites = JSON.parse(window.localStorage?.getItem(&#39;favorites&#39;)) ?? this.favorites
    }

    /**
     * Checks whether the form contains unsaved changes
     */
    containsUnsavedChanges() {
        return Object.keys(this.favorites)
            .map((key) =&gt; this.favoritesForm.value[key] === this.favorites[key])
            .some((value) =&gt; !value)
    }

    /**
     * Updates the favorites object and saves it to local storage
     */
    save() {
        this.favorites = { ...this.favoritesForm.value }
        window.localStorage.setItem(&#39;favorites&#39;, JSON.stringify(this.favorites))
    }

    /**
     * If changes are not saved, a dialog is opened to confirm with the user
     * that they want to proceed without saving
     */
    canDeactivate() {
        if (!this.containsUnsavedChanges()) {
            return true
        } else {
            return this.dialog.open(UnsavedChangesDialog).afterClosed()
        }
    }}
}
</code></pre>
<p>Here would be the template for this component:</p>
<pre><code class="language-html">&lt;h1&gt;Favourite Movies &amp; TV Shows&lt;/h1&gt;
&lt;form #FavoritesForm=&quot;ngForm&quot;&gt;
    &lt;mat-form-field&gt;
        &lt;mat-label&gt;Favorite Movie of all time&lt;/mat-label&gt;
        &lt;input name=&quot;movie&quot; matInput placeholder=&quot;Ex. Interstellar&quot; value=&quot;&quot; ngModel /&gt;
    &lt;/mat-form-field&gt;

    &lt;mat-form-field&gt;
        &lt;mat-label&gt;Favorite TV Show of all time&lt;/mat-label&gt;
        &lt;input name=&quot;tvShow&quot; matInput placeholder=&quot;Ex. The Last of Us&quot; ngModel /&gt;
    &lt;/mat-form-field&gt;
&lt;/form&gt;
&lt;footer&gt;
    &lt;p *ngIf=&quot;containsUnsavedChanges()&quot; class=&quot;unsaved-changes&quot; color=&quot;warn&quot;&gt;The form contains unsaved changes&lt;/p&gt;
    &lt;button mat-raised-button color=&quot;primary&quot; (click)=&quot;save()&quot;&gt;Save&lt;/button&gt;
&lt;/footer&gt;
</code></pre>
<p>As one can infer from the form, if the user doesn’t save changes, then a dialog will be opened to confirm whether the user would like to save the changes or not</p>
<h2 id="implementing-the-candeactivate-guard">Implementing the CanDeactivate guard</h2>
<p>There are two ways of writing route guards in Angular. Either a class that implements the <code>CanDeactivate</code> interface or a function with <a href="https://angular.io/api/router/CanDeactivateFn">the <code>CanDeactivateFn</code> signature.</a></p>
<ol>
<li><p>Implementing the guard as a function</p>
<p> As of Angular 14.2, Functional Route guards were introduced as a way to simplify the writing and wiring up of various types of guards in Angular. You can read more about the updates in <a href="https://blog.angular.io/advancements-in-the-angular-router-5d69ec4c032">this blog post (Advancements in the Angular Router)</a>.</p>
<p> Our implementation would then simply call the components <code>canDeactivate</code> function</p>
<pre><code class="language-tsx">import { CanDeactivateFn } from &#39;@angular/router&#39;
import { Observable } from &#39;rxjs&#39;
import { DeactivatableComponent } from &#39;./deactivatable-component.interface.ts&#39;

/** Our Route Guard as a Function */
export const canDeactivateFormComponent: CanDeactivateFn&lt;DeactivatableComponent&gt; = (component: DeactivatableComponent) =&gt; {
    if (component.canDeactivate) {
        return component.canDeactivate()
    }
    return true
}
</code></pre>
<p> This function can then be passed directly to the routes as:</p>
<pre><code class="language-tsx">const routes: Routes = [
    {
        path: &#39;favorites&#39;,
        component: FavoritesForm,
        canDeactivate: [canDeactivateFormComponent],
    },
]
</code></pre>
</li>
<li><p>Implementing the route guard as an injectable class</p>
<p> Route guards can also be implemented as an injectable class. This implementation looks very similar to the functional guard. And so we have:</p>
<pre><code class="language-tsx">import { CanDeactivate } from &#39;@angular/router&#39;
import { Observable } from &#39;rxjs&#39;
import { Injectable } from &#39;@angular/core&#39;

/* Our Route Guard as an Injectable Class */
@Injectable({
    providedIn: &#39;root&#39;,
})
export class UnsavedChangesGuard implements CanDeactivate&lt;DeactivatableComponent&gt; {
    canDeactivate: CanDeactivateFn&lt;DeactivatableComponent&gt; = (component: DeactivatableComponent) =&gt; {
        if (component.canDeactivate) {
            return component.canDeactivate()
        }
        return true
    }
}

export interface DeactivatableComponent {
    canDeactivate: () =&gt; boolean | Observable&lt;boolean&gt;
}
</code></pre>
<p> This can then also be added to the <code>canDeactivate</code> property for the route as:</p>
<pre><code class="language-tsx">const routes: Routes = [
    {
        path: &#39;favorites&#39;,
        component: FavoritesForm,
        canDeactivate: [canDeactivateFormComponent],
    }
]
</code></pre>
</li>
</ol>
<p>With our guard in place, if we edit the form and try to navigate away without saving, we will be warned through a dialog</p>
<p>You can <a href="https://angular-zzjpgu.stackblitz.io">preview an example of the app here</a>:
<StackBlitzEmbed
  projectId="angular-zzjpgu"
  openFile="src/app/components/home.component.ts"
  view="preview">
</StackBlitzEmbed></p>
<h2 id="links-and-resources">Links and Resources</h2>
<ul>
<li><a href="https://angular.io/api/router/Route#properties"><code>Route</code> properties (Angular Docs)</a></li>
<li><a href="https://angular.io/api/router/CanDeactivateFn"><code>CanDeactivateFn</code> signature</a></li>
<li><a href="https://angular.io/api/router/CanDeactivate"><code>CanDeactivate</code> interface</a></li>
<li><a href="https://blog.angular.io/advancements-in-the-angular-router-5d69ec4c032">Advancements in the Angular Router</a> (Angular Blog)</li>
</ul>
<h3 id="heres-my-track-of-the-week">Here&#39;s my track of the Week</h3>
<p>&lt;SpotifyTrack
  src=&quot;<a href="https://open.spotify.com/embed/track/5Te2aNOI54aBtQPqLqMsSq?utm_source=generator&amp;theme=0&quot;">https://open.spotify.com/embed/track/5Te2aNOI54aBtQPqLqMsSq?utm_source=generator&amp;theme=0&quot;</a></p>
<blockquote>
</SpotifyTrack>
</blockquote>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/guarding-against-unsaved-changes-with-route-guards-angular.jpeg" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/guarding-against-unsaved-changes-with-route-guards-angular.jpeg"/>
                    <media:credit>
                                <![CDATA[ Photo by <a href="https://unsplash.com/@springwellion?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Catherine Kay Greenup</a> on <a href="https://unsplash.com/photos/36vvGmTl198?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a> ]]>
                                </media:credit>
                    

            </item>
            
            <item>
                <title>NX Tips: Managing TsConfig Compiler Options Paths when building multiple angular libraries</title>
                <link>https://www.laudebugs.me/dev/nx-tips-managing-tsconfig-paths-with-multiple-angular-libraries</link>
                <pubDate>2022-10-19T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/dev/nx-tips-managing-tsconfig-paths-with-multiple-angular-libraries</guid>
                <description>
                    <![CDATA[ When building an angular library lib-a that depends on another angular library lib-b within an nx workspace, you may encounter an error like this: Error: Cannot find module 'lib-b' or its corresponding type declarations. We will see how to fix this error by updating the base tsConfig.]]>
                </description>
                <content:encoded>
                    <![CDATA[ <p>Working with NX allows one to develop publishable or local shared libraries for all the applications and libraries in the monorepo itself.</p>
<p>One issue that one may encounter is when trying to build libraries that depend on other libraries within the same monorepo. Say for instance we are working in an nx workspace called <code>@boomerang</code> and within this workspace we have two angular <a href="https://nx.dev/more-concepts/buildable-and-publishable-libraries#buildable-libraries">buildable libraries</a> called <code>@boomerang/common</code> and <code>@boomerang/utils</code>. Since these are Angular libraries, NX uses it&#39;s own wrapper around <code>ng-packagr</code> called <a href="https://nx.dev/packages/angular/executors/ng-packagr-lite"><code>@nrwl/ng-packagr-lite</code></a>. </p>
<Aside>

<p>Note: When creating a new Angular library with <code>nx generate @nrwl/angular:library</code>, if the library is both buildable and publishable, i.e. you pass in the <code>--buildable</code> and <code>--publishable</code> flags, then nx uses <a href="https://nx.dev/packages/angular/executors/package"><code>@nrwl/angular:library</code></a> to build the library.</p>
</Aside>

<p>If, say <code>@boomerang/common</code> imports <code>@boomerang/utils</code>, when trying to build <code>@boomerang/common</code>, an error I encountered looked like <code>TS2307: Cannot find module &#39;@boomerang/utils&#39; or its corresponding type declarations.</code></p>
<p>When I looked into what was causing the issue, its seems like a small tweak to the <code>tsConfig.base.json</code> at the root of the workspace by adding the <code>@boomerang/utils</code> dist path to the <code>compilerOptions</code> <code>paths</code> fixes the import issue.</p>
<pre><code class="language-diff">{
    &quot;compilerOptions&quot;: {
        &quot;paths: {
            &quot;@boomerang/common&quot;: [&quot;libs/common/src/index.ts&quot;],
-             &quot;@boomerang/utils&quot;: [&quot;libs/utils/src/index.ts&quot;],
+             &quot;@boomerang/utils&quot;: [
+                 &quot;dist/libs/utils&quot;,
+                 &quot;libs/utils/src/index.ts&quot;
+             ]
        }
    }
}
</code></pre>
<p>This solution was inspired by this <a href="https://github.com/nrwl/nx/issues/602#issuecomment-414051299">comment on nx github issues</a> as well as this <a href="https://github.com/zack9433/poc-workspace/commit/7dfedf7fdaf852a64e3b55960ba0678438aedc64">commit diff solution</a>. Both of these mention updating the package.json as well to use the npm scope, i.e. updating the <code>package.json</code> for <code>@boomerang/utils</code> to look like: </p>
<pre><code class="language-json">{
    &quot;name&quot;: &quot;@boomerang/utils&quot;
}
</code></pre>
<p>However, this update doesn’t necessarily fix the build issue if your packages are not publishable. </p>
<p>Happy Hacking!</p>
<h3 id="resources-and-links">Resources and Links</h3>
<ul>
<li><a href="https://nx.dev/more-concepts/buildable-and-publishable-libraries#publishable-and-buildable-nx-libraries">Publishable and Buildable Nx Libraries</a></li>
<li><a href="https://nx.dev/packages/angular/executors/ng-packagr-lite#@nrwl/angular:ng-packagr-lite"><code>@nrwl/ng-packagr-lite</code> docs</a></li>
<li><a href="https://www.notion.so/NX-Tips-Managing-TsConfig-Compiler-Options-Paths-when-building-multiple-angular-libraries-69a68018574242bb80bf27ee1ed0e60c"><code>@nrwl/angular:library</code> docs</a></li>
<li><a href="https://github.com/nrwl/nx/issues/602#issuecomment-414051299">Github Comment with solution</a> </li>
<li><a href="https://github.com/zack9433/poc-workspace/commit/7dfedf7fdaf852a64e3b55960ba0678438aedc64">poc-workspace diff by zack9433 on github to show a solution</a></li>
</ul>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/nx-tips-managing-tsconfig-paths-with-multiple-angular-libraries.jpeg" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/nx-tips-managing-tsconfig-paths-with-multiple-angular-libraries.jpeg"/>
                    <media:credit>
                                <![CDATA[ Photo by <a href="https://unsplash.com/@digital_helium?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Mikhail Abramkin</a> on <a href="https://unsplash.com/s/photos/construction-wallpaper?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a> ]]>
                                </media:credit>
                    

            </item>
            
            <item>
                <title>RxJs Pitfalls: Passing in a Observer's next function as a callback to pipe operators</title>
                <link>https://www.laudebugs.me/dev/rxjx-pitfalls-passing-observer-next-to-pipe-able-operator</link>
                <pubDate>2022-10-17T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/dev/rxjx-pitfalls-passing-observer-next-to-pipe-able-operator</guid>
                <description>
                    <![CDATA[ When using RxJs operators, you can pass in a callback function as the next function of an Observer. A common pitfall is to pass in the next function of an Observer as a callback to an RxJs operator. This post explains why this may be a bad idea causing errors that may be hard to debug and how to avoid it.]]>
                </description>
                <content:encoded>
                    <![CDATA[ <p>When working with observable streams, often one uses <code>RxJs</code> operators to pipe into the stream (i.e. using <a href="https://rxjs.dev/guide/operators">pipe-able operators</a>). Some of these operators take observers as an argument. An observer is an object that consumes values delivered by an observable and often implements one or more of the <code>next</code>, <code>error</code> and <code>complete</code> functions.</p>
<p>The RxJs <code>Subject</code> is a type of observable that is also an observer. A common pattern I find myself implementing in several projects is defining a <code>Subject</code>, often also a <code>BehaviorSubject</code> which then holds different pieces of data to be consumed in differnt parts of an application. In order to pass data to the <code>Subject</code>, which is also an observer, we call the <code>.next</code> with the data the Subject should hold. A simple example would be while using the <a href="https://rxjs.dev/api/index/function/tap"><code>tap</code> operator</a> to perform side effects in our observable stream.</p>
<p>A common pitfall is then passing <code>Subject.next</code> directly as the argument to a pipeable operator. For instance, when using <code>tap</code>, calling <code>tap(new Subject().next)</code>.
We will see how this can cause unexpected errors that are may be hard to debug and how to avoid it.</p>
<hr>
<p>Suppose you have an RxJs subject that is keeping track of the value in an observable stream (say called <code>stream$</code>).</p>
<pre><code class="language-tsx">import { Subject, timer, take, tap } from &#39;rxjs&#39;

const stream$ = timer(0, 1000).pipe(take(5))
</code></pre>
<p>One way to pass the current value to the subject is using the <code>tap</code> operator that accepts an observer, i.e. an object containing the <code>next</code>, <code>error</code> and <code>complete</code> functions.</p>
<p>If we only pass a callback function that logs out the current value in the observable stream, we would have something that looks like:</p>
<pre><code class="language-tsx">stream$.pipe(
    tap(console.log)
).subscribe()
/*
// Result would look like:
0
1
2
3
4
*/
</code></pre>
<p>If we have a subject called <code>_count</code> where we would like to keep track of the current value in the stream, the first instinct would be to replace <code>console.log</code> with <code>_count.next</code>:</p>
<pre><code class="language-tsx">const _count = new Subject&lt;number&gt;()

stream$.pipe(
    tap(_count.next)
).subscribe()
</code></pre>
<p>However, you&#39;ll notice that the above implementation <strong>does not work</strong>, resulting in the error: <code>TypeError: _this._throwIfClosed is not a function</code>.</p>
<p>The reason being that RxJs&#39;s <code>Subject</code> is a class whose <code>next</code> implementation relies on <code>this</code> keyword which refers to the <code>_count</code> instance. You can <a href="https://github.com/ReactiveX/rxjs/blob/8.x/src/internal/Subject.ts#L60">view the source code here</a>. Passing just the <code>_count.next</code> function would reference <code>this</code> which refers to the global scope and not the <code>_count</code> instance.</p>
<p>We can see this in action by implementing our own observer that references <code>this</code>:</p>
<pre><code class="language-tsx">const observerStore = {
    store: new Array&lt;number&gt;(),
    next(value: number) {
        this.store.push(value)
    },
}
/* Replacing the _count with our observer would then look like below👇🏻 */
stream$.pipe(
    tap(observerStore.next)
).subscribe()
</code></pre>
<p>The implementation above would also give us an error: <code>TypeError: Cannot read properties of undefined (reading &#39;push&#39;)</code>. For the same reason that the <code>this</code> reference would refer to the <code>global</code> object and not the <code>observerStore</code> object.</p>
<Aside>

<p>It&#39;s worth noting that if the observer&#39;s <code>next</code> implementation does not reference the <code>this</code> keyword, then passing in the <code>.next</code> function would work as expected.</p>
</Aside>

<p>For instance, if our <code>observerStore</code>&#39;s <code>next</code> function just logged out the value, then passing in the <code>observerStore.next</code> to <code>tap</code> would work as expected since the <code>next</code> function does not reference <code>this</code>:</p>
<pre><code class="language-tsx">const observerStore = {
    store: new Array&lt;number&gt;(),
    next(value: number) {
        console.log(value)
    },
}

/* Works! */
stream$.pipe(tap&lt;number&gt;(_count.next)).subscribe()
</code></pre>
<h3 id="solutions">Solutions</h3>
<ol>
<li><p>Pass in the observer object</p>
<p> The straightforward solution would be to simply to pass in the observer object into the <code>tap</code> operator:</p>
<pre><code class="language-tsx">/* This works */
stream$.pipe(
    tap(observerStore)
).subscribe()
</code></pre>
</li>
<li><p>Bind the observer to <code>this</code></p>
<p> One could use <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind"><code>Function.prototype.bind</code></a> which is available to the function prototype to bind <code>this</code> to the observer object so that when <code>this</code> is referenced, the function references the observer instead of the global <code>this</code> object:</p>
<pre><code class="language-tsx">/* _count Subjct */
stream$.pipe(
    tap(_count.next.bind(_count))
).subscribe()

/* the observerStore */
stream$.pipe(
    tap(observerStore.next.bind(observerStore))
).subscribe()
</code></pre>
<p> <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind">As the MDN docs state</a>, “The <strong><code>bind()</code></strong> method creates a new function that, when called, has its <code>this</code>keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.”</p>
</li>
</ol>
<p>Although both of the solutions work, passing the observer object is much more clear to another reader of the code on what is going on whereas the latter would cause someone who didn&#39;t write the code to stop and ask why we are calling <code>.bind</code> in the first place.</p>
<p>Happy hacking!</p>
<CodeSandBox fileName="index.ts">
console.log('right here')
</CodeSandBox>

<h3 id="references">References</h3>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind">MDN references on <code>Function.prototype.bind()</code></a></li>
<li><a href="https://rxjs.dev/api/operators/tap">RxJs <code>tap</code> operator</a></li>
<li><a href="https://rxjs.dev/guide/operators">RxJs Operators</a></li>
</ul>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/rxjx-pitfalls-passing-observer-next-to-pipe-able-operator.jpeg" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/rxjx-pitfalls-passing-observer-next-to-pipe-able-operator.jpeg"/>
                    <media:credit>
                                <![CDATA[ Photo by <a href="https://unsplash.com/@fakurian?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Milad Fakurian</a> on <a href="https://unsplash.com/s/photos/line-wallpaper?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a> ]]>
                                </media:credit>
                    

            </item>
            
            <item>
                <title>Customizing Angular Material with your own palette</title>
                <link>https://www.laudebugs.me/dev/customizing-angular-material-palette</link>
                <pubDate>2022-09-28T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/dev/customizing-angular-material-palette</guid>
                <description>
                    <![CDATA[ Angular Material provides a lot of flexibility in making your application represent your brand. In this article, we will look at how to customize the colors of Angular Material components to match your brand colors using several open source tools.]]>
                </description>
                <content:encoded>
                    <![CDATA[ <p><a href="https://material.angular.io/guide/theming">Angular Material</a> provides flexibility in making your website look and feel like the brand you are trying to build.</p>
<p>One of these the ways the library achieves this is by adding your own palette instead of <a href="https://material.angular.io/guide/theming#using-a-pre-built-theme">the default themes</a>: <code>deep-purple-amber</code>, <code>indigo-pink</code>, <code>purple-green</code> or <code>pink-bluegrey</code>.</p>
<p>The first thing you&#39;d need to do is select your primary, accent and tertiary colors. <a href="https://material.io/resources/color/#!/">The material.io Color Tool</a> provides a useful visual preview of what your primary and accent colors would look like on an application interface.</p>
<p>Here&#39;s a sample which you <a href="https://material.io/resources/color/#!/?primary.color=673AB7&amp;secondary.color=9C27B0&amp;view.left=0&amp;view.right=0">can also preview here:</a></p>
<p>&lt;img
    alt=&quot;Using the Material UI Color Tool to select your primary and accent colors&quot;
    src={&#39;<a href="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/_/using-material-design-color-tool.png&#39;%7D">https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/_/using-material-design-color-tool.png&#39;}</a>
    width={&#39;100%&#39;}
/&gt;</p>
<p>The tool also shows you how accessible your color palette is against fonts of various colors. <a href="https://material.io/resources/color/#!/?primary.color=673AB7&amp;secondary.color=9b27af&amp;view.left=1&amp;view.right=0">With the above palette</a>, for instance, the <code>P-Dark</code> with black text would not be as visible.</p>
<p>Although this tool doesn&#39;t provide an option to add a <code>warn</code> color, the angular material <a href="https://material.angular.io/guide/theming#defining-a-theme">documentation makes this palette optional</a> and defaults to <code>red</code> if not specified.</p>
<p>With the palette above: (primary: <code>#673ab7</code>, secondary: <code>#9b27af</code>, and a warn color value of <code>#f4511e</code>), we can use an open source tool called <a href="https://github.com/mbitson/mcg">Material Design Palette generator</a> to generate hues of 50 and then each 100 between 100 and 900 without having to do this manually.</p>
<p>Our resulting palette would look like below. You could also <a href="http://mcg.mbitson.com/#!?primary=%23673ab7&amp;accent=%239b27af&amp;warn=%23f4511e&amp;themename=material-palette">play around with the palette here</a> and try out different colors as well.</p>
<p>&lt;img
    alt=&quot;Using the Material UI Palette Generator to generate a palette&quot;
    src={&#39;<a href="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/_/using-the-palette-generator.png&#39;%7D">https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/_/using-the-palette-generator.png&#39;}</a>
    width={&#39;100%&#39;}
/&gt;</p>
<p>The cool thing about the tool above is that it provides an easy export function for various frameworks including Android, Angular, React, Vue, Ember and more.</p>
<p>Since we are using Angular, we would use the Angular 2 Material exported values which we can define in a <code>palette.scss</code> file:</p>
<pre><code class="language-scss">/* For use in src/lib/core/theming/_palette.scss */
$primary: (
    50: #ede7f6,
    100: #d1c4e9,
    200: #b39ddb,
    300: #9575cd,
    400: #7e58c2,
    500: #673ab7,
    600: #5f34b0,
    700: #542ca7,
    800: #4a259f,
    900: #391890,
    A100: #d4c7ff,
    A200: #ad94ff,
    A400: #8661ff,
    A700: #7347ff,
    contrast: (
        50: #000000,
        100: #000000,
        200: #000000,
        300: #000000,
        400: #ffffff,
        500: #ffffff,
        600: #ffffff,
        700: #ffffff,
        800: #ffffff,
        900: #ffffff,
        A100: #000000,
        A200: #000000,
        A400: #ffffff,
        A700: #ffffff,
    ),
);

$accent: (
    50: #f3e5f5,
    100: #e1bee7,
    200: #cd93d7,
    300: #b968c7,
    400: #aa47bb,
    500: #9b27af,
    600: #9323a8,
    700: #891d9f,
    800: #7f1796,
    900: #6d0e86,
    A100: #efb7ff,
    A200: #e384ff,
    A400: #d851ff,
    A700: #d237ff,
    contrast: (
        50: #000000,
        100: #000000,
        200: #000000,
        300: #000000,
        400: #ffffff,
        500: #ffffff,
        600: #ffffff,
        700: #ffffff,
        800: #ffffff,
        900: #ffffff,
        A100: #000000,
        A200: #000000,
        A400: #000000,
        A700: #ffffff,
    ),
);

$warn: (
    50: #feeae4,
    100: #fccbbc,
    200: #faa88f,
    300: #f78562,
    400: #f66b40,
    500: #f4511e,
    600: #f34a1a,
    700: #f14016,
    800: #ef3712,
    900: #ec270a,
    A100: #ffffff,
    A200: #ffe5e2,
    A400: #ffb7af,
    A700: #ffa096,
    contrast: (
        50: #000000,
        100: #000000,
        200: #000000,
        300: #000000,
        400: #000000,
        500: #ffffff,
        600: #ffffff,
        700: #ffffff,
        800: #ffffff,
        900: #ffffff,
        A100: #000000,
        A200: #000000,
        A400: #000000,
        A700: #000000,
    ),
);
</code></pre>
<p>In our <code>theme.scss</code> file, we would then define our palette as below:</p>
<pre><code class="language-scss">@use &#39;./palette.scss&#39; as palette;
@use &#39;@angular/material&#39; as mat;
@import &#39;@angular/material/theming&#39;;

$app-primary: mat.define-palette(palette.$primary);
$app-accent: mat.define-palette(palette.$accent);
$app-warn: mat.define-palette(palette.$warn);

$app-theme: mat.define-light-theme(
    (
        color: (
            primary: $app-primary,
            accent: $app-accent,
            warn: $app-warn,
        ),
    )
);
@include mat.all-component-themes($app-theme);
</code></pre>
<p>And just like that, we have customized our Angular App using Angular Material and a custom theme.</p>
<h2 id="links-and-resources">Links and Resources</h2>
<ul>
<li><a href="https://material.angular.io/">Angular Material</a></li>
<li><a href="https://material.io/resources/color/#!">Material Design Color Tool</a></li>
<li><a href="https://github.com/mbitson/mcg">Material Design Palette Generator</a></li>
</ul>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/customizing-angular-material-palette.jpeg" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/customizing-angular-material-palette.jpeg"/>
                    <media:credit>
                                <![CDATA[ Photo by <a href="https://unsplash.com/@nordwood?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">NordWood Themes</a> on <a href="https://unsplash.com/s/photos/design?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a> ]]>
                                </media:credit>
                    

            </item>
            
            <item>
                <title>In Pursuit of Imperfection</title>
                <link>https://www.laudebugs.me/journal/in-pursuit-of-imperfection</link>
                <pubDate>2022-08-29T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/journal/in-pursuit-of-imperfection</guid>
                <description>
                    <![CDATA[ What can we make of work that never really feels finished? So much of what we aim to achieve is much more primal in nature than we care to admit. Where can we strike the balance between what we can do versus what we have no control over?]]>
                </description>
                <content:encoded>
                    <![CDATA[ <p>It seems, at least to me, that there is this urge, a vengeful pursuit towards cleaning up the corners and crevices of what we produce. We want it to be this little egg that never gets spoilt — stuck in time. Even in programming, there&#39;s nothing sweeter than getting something done. Yet, it&#39;s never really done and once it&#39;s commited, it&#39;s privy to enter such an imperfect system that includes other developers, the business&#39; changing needs, evolving technologies. And yet — this very fact becomes so present and true everyday that nothing else seems like its meant to last. Why, one may ask, is it a thing for an engineer to be pumped up to start a new job only to switch in about two years? For the experience? To become a multifaceted problem-solver only to want so badly that one to two year sabbatical that promises insight into the ether.</p>
<p>Why should we write tests? Why should we refactor? Why should we write documentation? The answers to such questions are easy to bullet point and yet, I see in them a pursuit of some kind of impact. Because we don&#39;t do each of these tasks out of pleasure — as some would like to claim, but out of desiring that (a) our code “feel” perfect and immutable or on a more altruistic sense that (b) our efforts might help someone understand why we wrote the code the way we did.</p>
<p>In a sense, it helps to understand that our efforts may or may not matter. Yet, it&#39;s we keep adding, giving even though we may not know if it will be received or not. We move on, start something here, pick up something else there and wander in pursuit of imperfection, adding our own, because that&#39;s unmistakably what remains when we leave.</p>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/in-pursuit-of-imperfection.jpeg" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/in-pursuit-of-imperfection.jpeg"/>
                    <media:credit>
                                <![CDATA[ Photo by <a href="https://unsplash.com/@brazofuerte?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Collin Armstrong</a> on <a href="https://unsplash.com/s/photos/abandoned?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a> ]]>
                                </media:credit>
                    

            </item>
            
            <item>
                <title>Writing a Custom RxJS Operator</title>
                <link>https://www.laudebugs.me/dev/writing-a-custom-rxjs-operator</link>
                <pubDate>2022-07-23T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/dev/writing-a-custom-rxjs-operator</guid>
                <description>
                    <![CDATA[ RxJs has a ton of operators that you can use within your observable streams. But how would you approach writing your own custom operators for some specific use case?]]>
                </description>
                <content:encoded>
                    <![CDATA[ <p>While working with <code>rxjs</code>, there are plenty of operators that one can use within the <code>.pipe</code> operator of an observable. Just take a look at the api reference here and you&#39;ll realize that <code>rxjs</code> provides all the operators that you need for most cases.</p>
<p>However, what if you needed to write your custom operator to transform data the way you wanted, or tweak the <code>captureError</code> operator to handle the error in a certain way and return something else in case an error happened.</p>
<p>In an application I was working on, for which I was using <a href="https://docs.sentry.io/">Sentry</a> to handle any errors, I wanted to have the <code>captureError</code> to send the error message to Sentry, and return fallback data in case an error happened.</p>
<pre><code class="language-tsx">import { captureException } from &#39;@sentry/angular&#39;
import { BehaviorSubject, Observable, OperatorFunction, catchError, tap } from &#39;rxjs&#39;

export function consume&lt;T&gt;(consumer: BehaviorSubject&lt;T&gt;, fallback$: Observable&lt;T&gt;): OperatorFunction&lt;T, T&gt; {
    return (source$: Observable&lt;T&gt;) =&gt; {
        return source$.pipe(
            catchError((error) =&gt; {
                captureException(error)
                return fallback$
            }),
            tap(consumer),
        )
    }
}
</code></pre>
<Aside>

<p>Note that the <code>rxjs</code> tap operator takes either an observer object. In our case, our <code>consumer</code> is a <code>Behavior Subject</code> <a href="https://rxjs.dev/guide/subject">which is also an observer</a>.</p>
</Aside>

<p>The operator above is written in the form of <a href="https://javascript.info/currying-partials">a curried function</a>, that accepts two initial inputs: <code>consumer</code> - which in this case was a behavior subject that stores the current value in the observable stream, and <code>fallback$</code> which is the data to return in case an error happens. If no error happens, the <code>captureError</code> rxjs operator isn&#39;t called. In all cases, either the data from the observable stream or the fallback data is passed on to the consuming <a href="https://rxjs.dev/api/index/class/BehaviorSubject">behavior Subject.</a></p>
<p>And there we have it! Our own custom RxJS operator 🧞‍♂️</p>
<h2 id="links">Links</h2>
<ul>
<li><a href="https://rxjs.dev/">RxJs documentation</a></li>
<li><a href="https://rxjs.dev/api/index/class/Subject">RxJs Subject reference</a></li>
<li><a href="https://www.learnrxjs.io/learn-rxjs/operators/error_handling/catch">catch / catchError in RxJs</a></li>
<li><a href="https://docs.sentry.io/platforms/javascript/">Sentry JavaScript library documentation</a></li>
<li><a href="https://javascript.info/currying-partials">Currying in JavaScript</a></li>
</ul>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/writing-a-custom-rxjs-operator.jpeg" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/writing-a-custom-rxjs-operator.jpeg"/>
                    <media:credit>
                                <![CDATA[ Photo by <a href="https://unsplash.com/@solenfeyissa?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Solen Feyissa</a> on <a href="https://unsplash.com/s/photos/flow?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a> ]]>
                                </media:credit>
                    

            </item>
            
            <item>
                <title>Dynamic Components in Angular</title>
                <link>https://www.laudebugs.me/dev/dynamic-components-angular</link>
                <pubDate>2022-02-07T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/dev/dynamic-components-angular</guid>
                <description>
                    <![CDATA[ Building dynamic components in Angular can be tricky at times. However, with Dynamic components, we have granular control over how we can generate components in our application and use this feature to our advantage.]]>
                </description>
                <content:encoded>
                    <![CDATA[ <p>Angular components can be created(instantiated) at different points of the applications cycle wither at build-time or at run-time. Creating components at run-time (dynamically) is what we are going to look at.</p>
<p>Broadly speaking, there are two ways to create dynamic components in Angular:</p>
<ol>
<li>Using a <code>ViewContainerRef</code> - that <a href="https://angular.io/api/core/ViewContainerRef#viewcontainerref">“represents a container where one or more views can be attached to a component.”</a> or</li>
<li>By using Angular&#39;s built in <a href="https://angular.io/api/common/NgComponentOutlet"><code>NgComponentOutlet</code> directive</a></li>
</ol>
<p>The main focus of this article will be the former, using a <code>ViewContainerRef</code> to create dynamic components since the Angular documentation is really clear on the second way - using <code>NgComponentOutlet</code>.</p>
<h1 id="using-viewcontainerref">Using <code>ViewContainerRef</code></h1>
<p>The <code>ViewContainerRef</code> is a class that gets access to a container where other components (host views) can be inserted at run time using the <code>createComponent()</code> method of the <code>ViewContainerRef</code> class.</p>
<p>To dynamically create a component, we have to decide how and where we would like to place the component (the “anchor point”).</p>
<h2 id="step-1-defining-the-anchor-point">Step 1. Defining the anchor point</h2>
<p>How you define the anchor point determines where you can place it within a host component.</p>
<h3 id="i-the-anchor-directive">(i) The Anchor Directive</h3>
<p>Following the <a href="https://angular.io/guide/dynamic-component-loader#the-anchor-directive">Angular docs example</a> on creating a dynamic component, one can utilize a directive placed on an element such that the element will act as an insertion point to the dynamic component (a host - i.e. “Create the dynamic component and place me wherever you see this directive” on an element.)</p>
<p>To achieve this, we first create the directive and inject the <code>ViewContainerRef</code> . The <code>ViewContainerRef</code> will get a reference to the element on which the directive is placed, dynamically create the component and insert it into the view at the position where the element is.</p>
<p>Take the scenario where we would like to display <a href="https://github.com/laudebugs/dynamic-components-angular/wiki/Movie-Data">a list of movies</a> with their information (a simple example that can be achieved with other ways but easy enough to demonstrate with dynamic components).</p>
<p>A movie has the following interface:</p>
<pre><code class="language-ts">export interface IMovie {
    id: number
    title: string
    poster: string
    synopsis: string
    genres: Array&lt;string&gt;
    year: number
    director: string
    actors: Array&lt;string&gt;
    hours: Array&lt;string&gt;
}
</code></pre>
<p>We can then define our <code>MovieHostDirective</code> as follows:</p>
<pre><code class="language-ts">import { Directive, ViewContainerRef } from &#39;@angular/core&#39;

@Directive({
    selector: &#39;[movieHost]&#39;,
})
export class MovieHostDirective {
    constructor(public viewContainerRef: ViewContainerRef) {}
}
</code></pre>
<p>When we then create our host component template, we can place this directive on an element such as a <code>div</code> or using angular&#39;s <code>ng-template</code> or <code>ng-container</code>.</p>
<pre><code class="language-html">&lt;!-- using an html element --&gt;
&lt;div movieHost&gt;&lt;/div&gt;

&lt;!-- Using ng-template --&gt;
&lt;ng-template movieHost&gt;&lt;/ng-template&gt;

&lt;!-- using ng-container --&gt;
&lt;ng-container movieHost&gt;&lt;/ng-container&gt;
</code></pre>
<Aside>

<p>All of the above ways are valid ways of attaching an anchor directive. However, as the Angular docs point out, <a href="https://angular.io/guide/dynamic-component-loader#loading-components">&quot;[t]he <code>ng-template</code> element is a good choice for dynamic components because it doesn&#39;t render any additional output.&quot;</a></p>
</Aside>

<p>We can then get a reference to the element with the directive by querying the component template for the first occurrence of the directive (using <code>ViewChild</code> ), or all occurrences of the directive (using <code>ViewChildren</code>):</p>
<pre><code class="language-ts">/* Using ViewChild to get the first occurrence */
@ViewChild(MovieHostDirective, { static: true }) movieHost: MovieHostDirective;

/* Using ViewChildren to get the all occurrences */
@ViewChildren(MovieHostDirective, { static: true }) movieHosts: QueryList&lt;MovieHostDirective&gt;;
</code></pre>
<Aside>

<p>Note that <code>@ViewChildren</code> will return a <code>QueryList</code> - that contains a list of of <code>ViewContainerRef</code> types. To get elements inside of the <code>QueryList</code>, one can use the <code>QueryList</code>&#39;s <code>.length</code> property together with the <code>.get</code> method to get an element at a particular index. For example, for a query list of length 1, we can get the first element by <code>movieHosts.get(0)</code>. In our example this will return a <code>ViewContainerRef</code> that contains a <code>MovieHostDirective</code> instance. Documentation on <a href="https://angular.io/api/core/ViewChildren"><code>@ViewChildren</code> can be found here</a> .</p>
</Aside>

<h3 id="ii-using-a-template-variable-to-target-an-element-as-an-anchor">(ii) Using a Template Variable to target an element as an Anchor</h3>
<p>One can target an element to act as a host for our dynamic component by using a template variable that follows the syntax <code>#</code> followed by whatever variable name we would like. For example, if our template variable name is to be called: <code>TemplateRefAnchor</code> then, in our html template, we would have:</p>
<pre><code class="language-html">&lt;!-- using an html element --&gt;
&lt;div #TemplateRefAnchor&gt;&lt;/div&gt;

&lt;!-- Using ng-template --&gt;
&lt;ng-template #TemplateRefAnchor&gt;&lt;/ng-template&gt;

&lt;!-- using ng-container --&gt;
&lt;ng-container #TemplateRefAnchor&gt;&lt;/ng-container&gt;
</code></pre>
<h3 id="iii-the-host-view-as-the-anchor">(iii) The Host View as the Anchor</h3>
<p>We can use the host view (the component in which we will create the dynamic component) as an anchor for the newly created component. In this case, the created components would be appended at the end of the DOM tree. This works if we know that we don&#39;t have to insert the newly created elements before other elements that were generated at build time.</p>
<p>In this case, we inject the <code>ViewContainerRef</code> into the component:</p>
<h2 id="step-2-creating-the-dynamic-component">Step 2: Creating the Dynamic Component</h2>
<p>Taking the <code>@ViewChild</code> use case, (the same applies to the <code>@ViewChildren</code> case), the view queries are set before the <code>ngAfterViewInit</code> life cycle hook is called. We can then create our dynamic components at this point.</p>
<pre><code class="language-ts">import { Component, AfterViewInit, ViewChild, } from &#39;@angular/core&#39;;

/* We defined the MovieHostDirective earlier */
import { MovieHostDirective } from &#39;../core/directives/movie-host.directive&#39;
/* Assume we created this MovieComponent component somewhere else */
import { MovieComponent } from &#39;../core/components/movie/movie.component&#39;

@Component({
    selector: &#39;app-home&#39;,
    template: `&lt;ng-template movieHost&gt;&lt;/ng-template&gt;`,
})
export class HomeComponent extends AfterViewInit {
    /* Look up the element containing the MoviehostDirective */
    @ViewChild(MovieHostDirective, { static: true, read: ViewContainerRef }) movieHost!: ViewContainerRef

    /* Look Up the template reference called TemplateRefAnchor */
    @ViewChild(&#39;TemplateRefAnchor&#39;, { static: true, read: ViewContainerRef }) templateRefAnchor!: ViewContainerRef

    /* Another template reference to use createOptions */
    @ViewChild(&#39;UsingCreateComponentOptionsAnchor&#39;, { static: true, read: ViewContainerRef }) createComponentOptionsAnchor!: ViewContainerRef

    /* Create dynamic components here */
    ngAfterViewInit(){
        const component = this.movieHost.createComponent(MovieComponent)
        component.instance.movie = MOVIES[0]
    }
}
</code></pre>
<p>Similar example while using a template reference as an anchor:</p>
<pre><code class="language-ts">// ... code ommited
@Component({
    selector: &#39;app-home&#39;,
    template: `&lt;ng-template #TemplateRefAnchor&gt;&lt;/ng-template&gt;`,
})
export class HomeComponent extends AfterViewInit {
    /* Look Up the template reference called TemplateRefAnchor */
    @ViewChild(&#39;TemplateRefAnchor&#39;, { static: true, read: ViewContainerRef }) templateRefAnchor!: ViewContainerRef

    ngAfterViewInit(){
        /* Create the dynamic component using the template reference as an anchor */
        const component = this.templateRefAnchor.createComponent(MovieComponent)
        component.instance.movie = MOVIES[0]
    }
}
</code></pre>
<p>Similar example using the host element as the anchor:</p>
<pre><code class="language-ts">// ... code ommited
@Component({
    selector: &#39;app-home&#39;,
    template: `&lt;h1&gt; Host element &lt;/h1&gt;`,
})
export class HomeComponent extends AfterViewInit {

    constructor(private viewContainerRef: ViewContainerRef) { }

    ngAfterViewInit(){
        const component = this.viewContainerRef.createComponent(MovieComponent)
        component.instance.movie = MOVIES[0]
    }
}
</code></pre>
<Aside>

<p>Notice that we are directly modifying the data of the dynamically created component.😱
This works, but it becomes problematic when you want to be strict about immutability or uni-directional data flow, and with <code>QueryLists</code>, we can&#39;t use this approach since the elements in the <code>QueryList</code> are immutable.</p>
</Aside>

<p>We also see another issue with this approach. Since we mutate the data of the dynamic component, Angular&#39;s change detection will throw the <code>ExpressionChangedAfterItHasBeenCheckedError</code> warning that you may have encountered before:</p>
<div style={{display: 'flex', justifyContent: 'center'}}>

<p><img
    width="80%"
    src="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/_/expression-changed-after-checked.png"
/></p>
</div>

<p>The reason for this particular case is that angular has detected that we have changed the variable after it was last checked when the dynamically created component&#39;s view was initialized.</p>
<p>Some ways to resolve this is to move our code into the <code>ngAfterContentInit</code> life cycle hook or injecting the change detector (<code>ChangeDetectorRef</code>) and calling its <code>detectChanges()</code> method:</p>
<ol>
<li><p><code>ngAfterContentInit</code></p>
<pre><code class="language-ts">// code ommitted
export class HomeComponent implements AfterContentInit {

    /* Implement the AfterContentInit life cycle hook */
    ngAfterContentInit() {
        /* Create the component */
        const movieComponent = this.viewContainerRef.createComponent&lt;MovieComponent&gt;(MovieComponent)

        /* Pass data to the dynamically created component here. */
        movieComponent.instance.movie = MOVIES[0]
    }
}
</code></pre>
</li>
<li><p>using the <code>ChangeDetectorRef</code></p>
<pre><code class="language-ts">// code ommitted
export class HomeComponent implements AfterViewInit {
    /* Inject the ChangeDetectorRef */
    constructor(private cd: ChangeDetectorRef, private viewContainerRef: ViewContainerRef) {}

    ngAfterViewInit() {
        /* Create the component */
        const movieComponent = this.viewContainerRef.createComponent&lt;MovieComponent&gt;(MovieComponent)

        /* Pass data to the dynamically created component here. */
        movieComponent.instance.movie = MOVIES[0]

        /*  Tell angular to check for changes */
        this.cd.detectChanges()
    }
}
</code></pre>
</li>
</ol>
<p>The <code>ViewContainerRef</code> also provides a way to inject dependencies into our dynamically created component in the options object (<a href="https://angular.io/api/core/ViewContainerRef#createcomponent">the optional second parameter of the <code>createComponent</code> method</a>). The options object contains the following extra parameters (quoted from the Angular docs):</p>
<ul>
<li><code>index</code> - the index at which to insert the new components host view</li>
<li><code>injector</code> - the injector to use as the parent for the new component</li>
<li><code>ngModuleRef</code> - an ngModuleRef of the component&#39;s <code>NgModule</code></li>
<li><code>projectableNodes</code> - list of Dom nodes that should be projected though <code>ng-content</code></li>
</ul>
<p>Things begin to get a little more interesting as we explore this options object and how it expands our flexibility with dynamically created components. We&#39;ll explore the first two for now (<code>index</code> and <code>injector</code>).</p>
<h3 id="index"><code>index</code></h3>
<p>The <code>index</code> parameter allows us to insert a dynamically created component at a particular index. Say we would like to create a second component dynamically and place it at index <code>0</code>, then we would do something like:</p>
<pre><code class="language-ts">const movieComponent2 = viewContainerRef.createComponent&lt;MovieComponent&gt;(MovieComponent, { index: 0 })
</code></pre>
<p>Of course, we can&#39;t place an element at index <code>1</code> if the length of dynamically created components in the <code>ViewContainerRef</code> is <code>0</code>.</p>
<h3 id="injector"><code>injector</code></h3>
<p>We can inject dependencies into our dynamic components using the <code>injector</code> parameter.</p>
<p>Say, for instance, we want to pass in the movie when we create the component. We can first create an injector token that we can pass in for the movie dependency:</p>
<pre><code class="language-ts">import { InjectionToken } from &#39;@angular/core&#39;

export const MOVIE_TOKEN = new InjectionToken&lt;string&gt;(&#39;movie&#39;)
</code></pre>
<p>In our <code>movie.component.ts</code> file, we can then pass this dependency as a required dependency, or mark it as an optional dependency in the constructor - if you want to:</p>
<pre><code class="language-ts">// In movie.component.ts
constructor(@Inject(MOVIE_TOKEN) public movie: IMovie) {}

// As an optional dependency
constructor(@Inject(MOVIE_TOKEN) @Optional() public movie: IMovie) {}
</code></pre>
<p>In our app.component.ts, we would then create an injector and pass it to our component when we create it. Firstly, we provide our token and pass in the value that we would like to be available to the dynamically created component.</p>
<pre><code class="language-ts">// ... code ommitted
export class HomeComponent extends AfterViewInit {
    constructor(public parentInjector: Injector) {}
    // code ommited
    ngAfterViewInit() {
        /* Create the injector to be passed to the component and provide the value */
        const injector = Injector.create({
            providers: [{ provide: MOVIE_TOKEN, useValue: MOVIES[0] }],
            parent: this.parentInjector,
        })

        /* Create the component with the injector passed in */
        const movieComponent = viewContainerRef.createComponent&lt;MovieComponent&gt;(MovieComponent, {
            index: 0,
            injector,
        })
    }
}
</code></pre>
<Aside>

<p>It&#39;s interesting to point out that marking the dependency as optional (using the <code>@Optional()</code> decorator) means that we can leave it out when creating the dynamic component. Otherwise, we would run into a null injector error when we call <code>createComponent</code> (<code>R3InjectorError</code>):</p>
<div style={{display: 'flex', justifyContent: 'center'}}>

<img width="80%" src="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/_/null-injector-token.png" />

</div>

</Aside>

<h2 id="noteworthy">Noteworthy</h2>
<p>In previous versions of Angular, Dynamically created components had to be added to the <code>entryComponents</code> of the <code>NgModule</code> in which they are to be imported. With Ivy, this is no longer a requirement and they can be imported the same way as other components. <a href="https://angular.io/guide/deprecations#entrycomponents-and-analyze_for_entry_components-no-longer-required">You can read more about it here.</a></p>
<h2 id="recap">Recap</h2>
<p>However you decide to choose where to anchor your components, all these ways are valid and can go far in and of themselves. One advantage of dynamically generating the component using the <code>ViewContainerRef</code> is that you have access to the current state of the newly created component and can leverage it for your particular use case.</p>
<h1 id="ngcomponentoutlet"><code>NgComponentOutlet</code></h1>
<p>The <code>NgComponentOutlet</code> directive provides a “declarative approach for dynamic component creation” (<a href="https://angular.io/api/common/NgComponentOutlet#ngcomponentoutlet">quoted from the Angular docs</a>). Here, the documentation is pretty clear on how to use this directive to dynamically create a component.</p>
<Aside>

<p>The example code can be found in the <a href="https://stackblitz.com/edit/dynamic-components-angular-demo">stackblitz here.</a></p>
</Aside>

<h2 id="resources-and-links">Resources and Links</h2>
<ul>
<li><a href="https://angular.io/guide/dynamic-component-loader">Dynamic component loader</a></li>
<li><a href="https://angular.io/api/core/ViewContainerRef">ViewContainerRef</a></li>
<li><a href="https://angular.io/api/common/NgComponentOutlet#ngcomponentoutlet">NgComponentOutlet</a></li>
<li><a href="https://angular.io/api/core/QueryList">QueryList</a></li>
<li><a href="https://stackblitz.com/edit/dynamic-components-angular-demo">Stackblitz example on Dynamic Components</a></li>
<li><a href="https://github.com/laudebugs/dynamic-components-angular/wiki/Movie-Data">GitHub repository to run locally</a></li>
</ul>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/dynamic-components-angular.jpeg" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/dynamic-components-angular.jpeg"/>
                    <media:credit>
                                <![CDATA[ Photo by <a href="https://unsplash.com/@theshubhamdhage?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Shubham Dhage</a> on <a href="https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a> ]]>
                                </media:credit>
                    

            </item>
            
            <item>
                <title>Implementation of GraphQL subscriptions on React Native using Apollo Client with a Lambda GrahphQL Server</title>
                <link>https://www.laudebugs.me/dev/graphql-subs-with-aws-lambda-and-apollo-client</link>
                <pubDate>2022-01-24T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/dev/graphql-subs-with-aws-lambda-and-apollo-client</guid>
                <description>
                    <![CDATA[ A simple walkthrough on how to use AWS Apollo links with the Apollo GraphQL client on a React Native project]]>
                </description>
                <content:encoded>
                    <![CDATA[ <p>GraphQL queries and mutations to an AWS Lambda GraphQL api can be achieved by implementing libraries such as <a href="https://www.apollographql.com/docs/react/get-started/">Apollo Client</a>.
However, implementing subscriptions isn&#39;t as straight forward since the <a href="https://stackoverflow.com/questions/53734213/apollo-server-lambda-subscriptions#:~:text=GraphQL%20subscriptions%20are%20not%20supported,which%20kills%20the%20websocket%20connection.">AWS Lambda is a serverless architecture.</a>
Implementing subscriptions can be done <a href="https://docs.aws.amazon.com/appsync/latest/devguide/real-time-websocket-client.html#appsynclong-real-time-websocket-client-implementation-guide-for-graphql-subscriptions">following the AWS implementation docs</a> - although we don&#39;t want to go into that rabbit hole as well as the option of using <a href="https://docs.amplify.aws/lib/q/platform/js/">AWS&#39; Amplify JavaScript Libraries</a>.</p>
<blockquote>
<p>In our case, we would like to take advantage of <a href="https://www.apollographql.com/docs/react/why-apollo/">Apollo Client&#39;s</a> apis that offer useful features within a react application such as hooks, caching, and also since it&#39;s simpler (one can argue). 💁</p>
</blockquote>
<h2 id="setup">Setup</h2>
<p>To get started, working with queries and mutations can be achieved by following <a href="https://www.apollographql.com/docs/react/get-started/">Apollo&#39;s documentation.</a>. Where it get&#39;s interesting is once a subscription is needed to be made by the client.</p>
<p>First, we need to configure our Apollo client to <a href="https://github.com/awslabs/aws-mobile-appsync-sdk-js#aws-appsync-links-for-apollo-v3">make use of the Apollo links to connect to the AppSync api</a>. These are: <code>aws-appsync-auth-link</code> and <code>aws-appsync-subscription-link</code>. The former provides authentication for the Apollo client to connect to the api while the later provides the subscription tooling that AWS Lambdas need to work with subscriptions, which <a href="https://docs.aws.amazon.com/appsync/latest/devguide/real-time-websocket-client.html#appsynclong-real-time-websocket-client-implementation-guide-for-graphql-subscriptions">we would have otherwise needed to implement ourselves</a>.🤢</p>
<p>The example provided in <a href="https://github.com/awslabs/aws-mobile-appsync-sdk-js#using-authorization-and-subscription-links-with-apollo-client-v3-no-offline-support">the docs here</a> is pretty straightforward.
In our case, the <code>auth</code> provided to the Apollo links would look like:</p>
<pre><code class="language-ts">const auth = {
  type: &#39;OPENID_CONNECT&#39;
  jwtToken: async () =&gt; token, // Required when you use Cognito UserPools OR OpenID Connect. token object is obtained previously
};
</code></pre>
<p>Although, the only thing to note is that, since we need to provide a token to the client, we just pass in our token that retrieves the most current Open ID JWT to pass to our requests.</p>
<p>Thus:</p>
<pre><code class="language-ts">const link = ApolloLink.from([
    createAuthLink({
        url: API_URL,
        region: AWS_REGION,
        auth: { type: &#39;OPENID_CONNECT&#39;, jwtToken: async () =&gt; await getToken() },
    }),
    createSubscriptionHandshakeLink(
        { url: API_URL, region: AWS_REGION, auth: { type: &#39;OPENID_CONNECT&#39;, jwtToken: async () =&gt; await getToken() } },
        httpLink,
    ),
])
</code></pre>
<p>This workaround was noted <a href="https://github.com/aws-amplify/amplify-js/issues/992">in this issue on Github.</a></p>
<h2 id="issues">Issues</h2>
<ul>
<li><h4 id="unable-to-resolve-module-buffer">Unable to resolve module <code>buffer</code>:</h4>
&lt;img width={&#39;100%&#39;} alt=&quot;Screen Shot 2022-01-24 at 4 57 07 PM&quot; src=&quot;<a href="https://user-images.githubusercontent.com/25782070/150878930-80928c4c-0e1e-421b-8314-3c1c580ebf97.png&quot;/&gt;">https://user-images.githubusercontent.com/25782070/150878930-80928c4c-0e1e-421b-8314-3c1c580ebf97.png&quot;/&gt;</a></li>
</ul>
<p>This can be solved by installing <code>buffer</code> and adding it to to the <code>App.tsx</code> file as noted in <a href="https://stackoverflow.com/questions/55226768/error-unable-to-resolve-module-buffer-react-native">here on Stack overflow</a>.</p>
<pre><code class="language-ts">import { Buffer } from &#39;buffer&#39;;

global.Buffer = Buffer;
</code></pre>
<h2 id="links">Links</h2>
<ul>
<li><a href="https://stackoverflow.com/questions/62502579/appsync-subscriptions-with-apolloclient-in-react">AppSync subscriptions with ApolloClient in React</a></li>
<li><a href="https://github.com/awslabs/aws-mobile-appsync-sdk-js#using-authorization-and-subscription-links-with-apollo-client-v3-no-offline-support">Using Authorization and Subscription links with Apollo Client V3 (No offline support)</a></li>
<li><a href="https://docs.aws.amazon.com/appsync/latest/devguide/real-time-websocket-client.html#appsynclong-real-time-websocket-client-implementation-guide-for-graphql-subscriptions">Building a real-time WebSocket client</a></li>
</ul>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/graphql-subs-with-aws-lambda-and-apollo-client.jpeg" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/graphql-subs-with-aws-lambda-and-apollo-client.jpeg"/>
                    <media:credit>
                                <![CDATA[ Photo by <a href="https://unsplash.com/@timmossholder?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Tim Mossholder</a> on <a href="https://unsplash.com/s/photos/connection?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a> ]]>
                                </media:credit>
                    

            </item>
            
            <item>
                <title>Six Months at the Job</title>
                <link>https://www.laudebugs.me/journal/six-mois-au-travail</link>
                <pubDate>2021-12-12T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/journal/six-mois-au-travail</guid>
                <description>
                    <![CDATA[ It has been quite the ride so far this past several months at a new position. But there are always somethings to take away from my experience as the year winds down. Here are some few thoughts.]]>
                </description>
                <content:encoded>
                    <![CDATA[ <p>My eyes crack open with fatigue weighing down on them. It&#39;s 8.34 am. I slept through the alarm - or I forgot to set an alarm - or I trusted my body to wake itself before the stand-up. Either way, my sleep over the past weeks has become much more routinely irregular. </p>
<p>I power on my machine, perhaps spending a minute or two to get my cold brew, swivel on my chair and listen. There&#39;s always someone running the show, and I turn on my editor back to where I&#39;d left over the previous day. </p>
<p>I&#39;m definitely late for that meeting and assemble my thoughts before I&#39;m picked on to give an update. A few Uhhms as I open my pending PRs. &quot;Working on [this and that]&quot; I say - but If I were really stretching myself I could&#39;ve finished all the work for this sprint in a day or two. I like the &quot;corporate&quot; nature of work sometimes - but I know there&#39;s a temptation to sink into the benignness of getting assigned tasks - and being expected to get into the routine of delivering things on certain timelines. That predictability of what I do at work I have found frustrating - so during my free time, I&#39;ve spent my evenings and nights shipping something of my own - without timelines, without well-known requirements - chipping away at a project one google search after another - since my four year degree taught me how to google somewhat well enough so far. </p>
<p>And yet - I know there&#39;s plenty of things to learn at times it has felt overwhelming - so I&#39;ve used the projects I&#39;ve been involved in as a guide to learning something new - because it&#39;s not how much I know about something that&#39;s been helpful but rather being flexible and ready to learn. So sometimes I tell my PM or Scrum Master that the implementation doesn&#39;t matter because it&#39;s a learning opportunity for me. I think that&#39;s important for my curiosity - or I&#39;ll get bored changing margins and font-sizes.</p>
<p>I worked from my bed, from my closet, from my kitchen, from the train, from the airport. This thing I do - isn&#39;t work anymore - at least not in the traditional sense that I&#39;ve seen it. Perhaps it&#39;s the whole work from anywhere that&#39;s erased the boundaries. </p>
<p>In a sense - there&#39;s always this dance to play — between staying curious and contributing to a vision - even in the smallest of ways. Or else we&#39;d be all trying to get our own things done and not achieve as much as we could together.</p>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/six-mois-au-travail.jpeg" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/six-mois-au-travail.jpeg"/>
                    <media:credit>
                                <![CDATA[ Photo by <a href="https://unsplash.com/@teckhonc?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">T.H. Chia</a> on <a href="https://unsplash.com/s/photos/unique?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a> ]]>
                                </media:credit>
                    

            </item>
            
            <item>
                <title>Organizing Codebases with Automation Tools</title>
                <link>https://www.laudebugs.me/dev/organizing-codebases-js</link>
                <pubDate>2021-11-17T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/dev/organizing-codebases-js</guid>
                <description>
                    <![CDATA[ JavaScript or TypeScript codebases can be a hustle to manage. Some people like their spacebars a lot more than their tabs; How can we track changes effectively on each commit? Or how can we automate versioning? We all can do with some standards in our codebase that allow us to keep track of what's going on in them.]]>
                </description>
                <content:encoded>
                    <![CDATA[ <Aside>
  Over the past couple of months, shuffling between different projects, and also work, I found it particularly useful to have some standard
  way to organize a codebase - even if each of the projects differed in some way or another. This little guide is what I come back to
  whenever I'm scaffolding a new project.
</Aside>

<br />

<h2 id="tools-mentioned-in-this-guide">Tools Mentioned in this guide</h2>
<ul>
<li><a href="https://typicode.github.io/husky/#/">Husky</a></li>
<li><a href="https://prettier.io/">Prettier</a></li>
<li><a href="https://eslint.org/">Eslint</a></li>
<li><a href="https://www.npmjs.com/package/@commitlint/cli">Commitlint</a></li>
<li><a href="https://www.npmjs.com/package/commitizen">Commitizen</a></li>
<li><a href="https://www.npmjs.com/package/standard-version">Standard Version</a></li>
</ul>
<h3 id="husky"><a href="https://typicode.github.io/husky/#/">Husky</a></h3>
<p>Husky is used to make our commits more cool, and powerful to help development easier. In this guide, husky will be used to format both the files (source code) and commits themselves before they are executed (thus &quot;pre&quot; in the pre-commit hooks).</p>
<p><a href="https://typicode.github.io/husky/#/">Read more here.</a></p>
<ul>
<li><p><a href="https://typicode.github.io/husky/#/?id=automatic-recommended">Initialize Husky:</a></p>
<pre><code class="language-bash"># install husky
npm install -D husky

# initialize
npx husky-init
</code></pre>
</li>
</ul>
<h2 id="prettier--eslint">Prettier &amp; EsLint</h2>
<p>If we would like to have our commits automatically format our code on every commit - to ensure that the codebase follows a specified standard, defined in the <code>prettierrc</code> file, then this is a handy tool to have.</p>
<ol>
<li><p>Add Prettier and eslint to your project:</p>
<pre><code class="language-bash"># prettier
npm install -D prettier

# eslint
npm install -D eslint
</code></pre>
</li>
<li><p>Add a prettier <a href="https://prettier.io/docs/en/configuration.html">config file</a> to the repository - named <code>.prettierrc.json</code> (or following the <a href="https://prettier.io/docs/en/configuration.html">specified format</a> for configuration files:</p>
<pre><code class="language-json">{
  &quot;trailingComma&quot;: &quot;all&quot;,
  &quot;tabWidth&quot;: 4,
  &quot;semi&quot;: false,
  &quot;singleQuote&quot;: true
  //more rules below
}
</code></pre>
</li>
<li><p>Initialize EsLint:</p>
<pre><code class="language-bash">npx eslint --init
</code></pre>
</li>
<li><p>Set up ESLint to work with prettier</p>
<p>Add prettier plugin to the eslint configuration file:</p>
<pre><code class="language-json">// .eslintrc.json
{
  &quot;extends&quot;: [
    // other extensions,
    &quot;prettier&quot;
  ]
}
</code></pre>
<p>Now, you can specify prettier rules to work with your linter and not have both ESLint and Prettier enforcing different styles</p>
</li>
<li><p>Add <a href="https://prettier.io/docs/en/precommit.html#option-2-pretty-quickhttpsgithubcomazzpretty-quick">Prettier Pre-commit Hook</a>:</p>
<pre><code class="language-bash">npm install -D pretty-quick

npx husky set ./husky/pre-commit &quot;npx pretty-quick --staged&quot;
</code></pre>
</li>
</ol>
<blockquote>
<p>There are more ways to configure your prettier pre-commit hooks <a href="https://prettier.io/docs/en/precommit.html#docsNav">found here</a>.</p>
</blockquote>
<h2 id="commitlint--commitlint-hooks"><a href="https://github.com/conventional-changelog/commitlint">CommitLint</a> &amp; CommitLint Hooks</h2>
<p>Commitlint is a tool that lints commits - and make sure they are up to standard.
We will also add a husky pre-commit hook that lints our commit messages</p>
<ol>
<li><p><a href="https://github.com/conventional-changelog/commitlint#getting-started">Install Commitlint</a></p>
<pre><code class="language-bash">npm install -D @commitlint/config-conventional @commitlint/cli

# Configure commitlint to use conventional config
echo &quot;module.exports = {extends: [&#39;@commitlint/config-conventional&#39;]}&quot; &gt; commitlint.config.js
</code></pre>
</li>
<li><p>. Add the commitlint hooks:</p>
<pre><code class="language-bash"># Add hook
npx husky add .husky/commit-msg &#39;npx --no -- commitlint --edit &quot;$1&quot;&#39;
</code></pre>
</li>
<li><p>Add a husky pre-commit hook config to the package.json</p>
<pre><code class="language-json">&quot;husky&quot;: {
  &quot;hooks&quot;: {
    &quot;prepare-commit-msg&quot;: &quot;exec &lt; /dev/tty &amp;&amp; git cz --hook || true&quot;
  }
}
</code></pre>
</li>
</ol>
<h2 id="commitizen"><a href="https://github.com/commitizen/cz-cli">Commitizen</a></h2>
<p>Commitizen is a command line interface tool that can be helpful in making commits a pretty-forward process following your linting rules.</p>
<ol>
<li><p>Install the tool</p>
<pre><code class="language-bash">npm install -D commitizen
</code></pre>
</li>
<li><p>Add a script to the package.json to easily run commitizen:</p>
<pre><code class="language-json">{
    &quot;scripts&quot;: {
      &quot;commit&quot;: &quot;cz&quot;
    }
}
</code></pre>
<p>An Example use:</p>
</li>
</ol>
<p><img
  src="https://raw.githubusercontent.com/MechanicalHuman/hnp-utilities/master/packages/commitizen-adapter/hero.png"
  width="100%"
/></p>
<p><a href="https://www.npmjs.com/package/@hnp/cz/v/1.0.7">source</a></p>
<Note>
You can customize the fields available in your commitizen CLI by <a href="https://medium.com/vlad-arbatov/development-how-to-adapt-a-custom-conventional-changelog-33ff3b13c832"> following this guide</a>.
</Note>

<h2 id="conventional-changelog">Conventional Changelog</h2>
<p>This will assist in generating changelogs automatically from commits:</p>
<ol>
<li>Initialize:<pre><code class="language-bash">npx commitizen init cz-conventional-changelog -D --save-exac
</code></pre>
</li>
</ol>
<h2 id="versioning-and-release">Versioning and Release</h2>
<p>We can use <a href="https://github.com/conventional-changelog/standard-version#standard-version">Standard Version</a> to automatically generate versions for out projects.</p>
<ol>
<li><p>Install Standard Version</p>
<pre><code class="language-bash">npm install -D standard-version
</code></pre>
</li>
<li><p>Add scripts to easily run releases and generate changelogs automaticallly:</p>
<pre><code class="language-json">{
  &quot;scripts&quot;: {
    &quot;release&quot;: &quot;standard-version&quot;
  }
}
</code></pre>
<p>Another option to Standard version is <a href="https://github.com/semantic-release/semantic-release"><code>semantic-release</code></a></p>
</li>
</ol>
<p>You can now run your first release by:</p>
<pre><code class="language-bash">npm run release
</code></pre>
<blockquote>
<p>Plug: Here is an example of a <a href="https://www.laudebugs.me/changelog">changelog for my website</a>.</p>
</blockquote>
<p>If you create a release - then you can push that release by running:</p>
<pre><code class="language-bash">git push --follow-tags
</code></pre>
<hr />

<h2 id="update-25th-feb-2022">Update (25th Feb 2022)</h2>
<p>I ended up writing a simple npm package to automate setting this whole process up for new projects
You can find the package <a href="https://www.npmjs.com/package/organize-codebase">here</a>.</p>
<h2 id="references--resources">References &amp; Resources</h2>
<h3 id="articles">Articles</h3>
<ul>
<li><a href="https://blog.logrocket.com/never-guess-about-project-history-again-31f65091f668/">How to control your deployments and versioning with semantic-release &amp; friends</a>- (logrocket)</li>
<li><a href="https://dev.to/migu3l/commit-standard-and-semantic-versioning-for-any-project-1ihc">Commit Standard and Semantic Versioning for any project</a> - (dev)</li>
<li><a href="https://medium.com/@jsilvax/automate-semantic-versioning-with-conventional-commits-d76a9f45f2fa">Automate Semantic Versioning with Conventional Commits </a>(medium)</li>
<li><a href="https://blog.logrocket.com/automatically-generate-and-release-a-changelog-with-node-js/">Automatically generate and release a changelog using Node.js</a> (logrocket)</li>
<li><a href="https://medium.com/vlad-arbatov/development-how-to-adapt-a-custom-conventional-changelog-33ff3b13c832">Development: How to adapt a custom conventional changelog</a> (medium)</li>
<li><a href="https://dev.to/sohandutta/make-everyone-in-your-project-write-beautiful-commit-messages-using-commitlint-and-commitizen-1amn">Make everyone in your project write beautiful commit messages using commitlint and commitizen</a> - loved this one!</li>
</ul>
<h3 id="documentation">Documentation</h3>
<ul>
<li><a href="https://typicode.github.io/husky/#/">Husky</a></li>
<li><a href="https://eslint.org/docs/user-guide/getting-started">ESLint - Getting started</a></li>
<li><a href="https://prettier.io/docs/en/precommit.html#docsNav">Prettier - Pre-Commit Hooks</a></li>
<li><a href="https://github.com/commitizen/cz-cli">Commitizen</a></li>
<li><a href="https://github.com/conventional-changelog/commitlint#getting-started">Commitlint</a></li>
<li><a href="https://github.com/conventional-changelog/standard-version#standard-version">Conventional Changelog</a></li>
<li><a href="https://github.com/semantic-release/semantic-release">Semantic Release</a></li>
</ul>
<h3 id="other-useful-links">Other Useful Links</h3>
<ul>
<li><a href="https://gitmoji.dev/">Gitmoji</a></li>
</ul>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/organizing-codebases-js.jpeg" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/organizing-codebases-js.jpeg"/>
                    <media:credit>
                                <![CDATA[ Photo by <a href="https://unsplash.com/@maxvdo">Birmingham Museums Trust</a> on <a href="https://unsplash.com/photos/MZ0zABL7Bwo">Unsplash</a> ]]>
                                </media:credit>
                    

            </item>
            
            <item>
                <title>Endings</title>
                <link>https://www.laudebugs.me/journal/endings</link>
                <pubDate>2021-11-03T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/journal/endings</guid>
                <description>
                    <![CDATA[ And it's almost as if I left through the back door. The sense of escaping what I knew to be how things were supposed to end. How are things supposed to end?]]>
                </description>
                <content:encoded>
                    <![CDATA[ <p>And it&#39;s almost as if I left through the back door.
The sense of escaping what I knew to be how things were supposed to end.
How are things supposed to end? With expectations forcing actions into predefined paths.
Of all the ways that I imagined my four years of college turning out, I didn&#39;t think that they would turn out with me not graduating on time, with no job, and staying over at a friend&#39;s place for an extended period of time.
The overwhelming feeling last Spring after having finished the semester was the that of defeat.
Now, as has been a couple of times before, it&#39;s more of indirection. What do I want to do in life, what kind of job do I want to pursue?</p>
<p><small>From May, 2020</small></p>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/endings.jpeg" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/endings.jpeg"/>
                    <media:credit>
                                <![CDATA[ Photo by <a href="https://unsplash.com/@birminghammuseumstrust?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Birmingham Museums Trust</a> on <a href="https://unsplash.com/s/photos/generative-art?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a> ]]>
                                </media:credit>
                    

            </item>
            
            <item>
                <title>Dawn</title>
                <link>https://www.laudebugs.me/journal/dawn</link>
                <pubDate>2021-03-08T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/journal/dawn</guid>
                <description>
                    <![CDATA[ I turn the faucet all the way to the right for the last few seconds of my shower – a ritual I have taken up this past winter. The practice reminds me of high school when I didn't have the luxury of steamy personal bathrooms but rather run-down swaths of stalls – some of which didn't work entirely.]]>
                </description>
                <content:encoded>
                    <![CDATA[ <p>I turn the faucet all the way to the right for the last few seconds of my shower – a ritual I have taken up this past winter. The practice reminds me of high school when I didn&#39;t have the luxury of steamy personal bathrooms but rather run-down swaths of stalls – some of which didn&#39;t work entirely. Toward the end of my junior year, we were lucky to have one or two of the stalls installed with running hot water – but even with the option to take a hot shower in the morning, I at times preferred the chilling water to awaken myself to a new day.</p>
<p>Of all the past winters I&#39;ve experienced, this past winter was also a time when I was trying to figure out my career, my faith, writing, family, friends and more. Most of that is still work in progress – I&#39;m writing again – which I&#39;m happy about. The opportunity intern at Antra, a consulting company, has been a process of learning. I recently also became a daily coffee drinker which I didn&#39;t ever see coming. I still convince a part of myself that it&#39;s not an addiction.</p>
<p>At the beginning of this very past winter, the was a kind of longing that hung in the air as I moved back to New York. This longing was for the certain past – the good and the bad as well as the future – always a time to look forward to and hope. This longing was at times deep and frustrating and at other times faded amidst the daily rhythms and rituals. Past victories and failures are both in themselves alluring. The former because I can easily hold them too close and close my eyes to what lays before me, now. The latter because I can also easily jump back right into them and stimy any progress I made to learn from my mistakes. However, the most attractive thing about the past is that the past has a certainty to it – it&#39;s complete and bound up in itself. How I badly wanted that – the certainty that the past could offer.</p>
<p>The future holds hope as an appeal. Often at times I tend to want to feed into that hope now – by doing what I can to make the future become the present. At times I don&#39;t want to wait – why wait for something so deeply attractive as a future. The risk here is, I think far more dangerous because I may then become frustrated and dissatisfied with the work I need to do to get there. The work needed to get there remains very much so in the present. I am writing all this as a reminder to myself that the living part is very much in the now.</p>
<p>As a side note, I&#39;ve also been trying new recipes. Although I have tried different recipes, my beef stew isn&#39;t quite there yet as last weekend I skipped the tomatoes and added too much water but managed to salvage a meal for my room mates.</p>
<p>That&#39;s it for now, but I hope to share more in the future.</p>
<p>Thank you for reading.</p>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/dawn.jpeg" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/dawn.jpeg"/>
                    
                    

            </item>
            
            <item>
                <title>Using your GitHub Repository as a Database</title>
                <link>https://www.laudebugs.me/dev/using-your-github-repository-as-a-database</link>
                <pubDate>2021-03-01T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/dev/using-your-github-repository-as-a-database</guid>
                <description>
                    <![CDATA[ You don't have to look far for somewhere to store your content. GitHub is a great place to store your content. Together with the GitHub API, you can use GitHub make this happen.]]>
                </description>
                <content:encoded>
                    <![CDATA[ <h1 id="how-to-use-your-repo-as-a-database-with-the-github-graphql-api">How To: Use your Repo as a Database with the Github GraphQL API</h1>
<Aside>
You can sort of use your Github repository as a database for your blog posts by leveraging the Github <a href="https://docs.github.com/en/graphql">Graph QL API</a> 😱.
</Aside>

<h2 id="motivation">Motivation</h2>
<p>The Github GraphQL API provides a lot more capabilities than I can cover in one <em>snack</em>.</p>
<p>However, in searching for a way to share the small lessons that I pick up week in week out, I knew that publishing through <a href="https://www.contentful.com/">Contentful</a>, which it the CMS, I use for the main content in my blog site, was a little too much to share easily and quicly.
Posting on Github and sharing that as <a href="https://gist.github.com/laudebugs">gists</a> seemed like I would be writing too much for a gist that is supposed to be a short code snippet - which was the original title of this section. Although <a href="https://gist.github.com/MichaelCurrin/6777b91e6374cdb5662b64b8249070ea">MichaelCurrin</a> proves me otherwise! His article is what got me goint in the first place!</p>
<h2 id="querying-the-graphql-api-for-posts">Querying the GraphQL API for posts.</h2>
<p>In order to use the Github GraphQL API, you can either use the <a href="https://docs.github.com/en/graphql/overview/explorer">API explorer</a> by logging in through your github account, use an api testing tool like <a href="https://support.insomnia.rest/article/61-graphql">Insomnia</a>, or you can use it programmatically.</p>
<p>Since I was using the Apollo Graph QL library to query my backend - that helps me manage comments and likes, I began to do a little research on how to query the Github GraphQL API. I will link the articles below.
This <a href="https://stackoverflow.com/questions/58576940/how-to-handle-authorization-header-with-apollo-graphql">stack overflow</a> answered how to add an authorization header to an Apollo Client Query.</p>
<h2 id="the-query">The Query</h2>
<p>We will be using writing our query in a node.js environment using both <a href="https://github.com/apollographql/apollo-client">Apollo Client</a> and <a href="https://github.com/axios/axios">Axios</a></p>
<h3 id="1-obtain-your-github-public-access-token">1. Obtain your github public access token</h3>
<p>The only permission you need for this task is <code>public_repo</code> under <code>repo</code>:</p>
<ul>
<li><input disabled="" type="checkbox"> repo<ul>
<li><input checked="" disabled="" type="checkbox"> public_repo</li>
</ul>
</li>
</ul>
<p>Give your token a name and will look something like this:</p>
<pre><code class="language-txt">89fdd35bcd40787b519e97462cec0f9975a66a58
</code></pre>
<p>Note the token above is revoked and you&#39;ll need to generate yours. Once you&#39;re done, we&#39;re ready for the next step!</p>
<h3 id="2-querying-the-repo">2. Querying the repo</h3>
<p>In my case, I will be looking for files in my repository called <code>articles</code>. If you&#39;d like to use your own repository, simply make note of your repository name.</p>
<h3 id="using-the-apollo-client">Using the Apollo Client</h3>
<ol>
<li><p>Install the Apollo client</p>
<pre><code class="language-bash">npm install @apollo/client graphql
</code></pre>
</li>
<li><p>Working in your js file, import the Apollo client and a few methods that we will make use of:</p>
<pre><code class="language-js">import { ApolloClient, InMemoryCache, gql, ApolloLink, HttpLink } from &#39;@apollo/client&#39;
</code></pre>
</li>
<li><p>Initialize a new apollo client with the github graphQL endpoint and your token</p>
<pre><code class="language-js">const token = &#39;89fdd35bcd40787b519e97462cec0f9975a66a58&#39;

const endpoint = &#39;https://api.github.com/graphql&#39;

// Add the toke to the header of your client for all your requests
const githubLClient = new ApolloClient({
  uri: endpoint,
  headers: {
    authorization: `Bearer ${token}`
  },
  cache: new InMemoryCache({
    addTypename: false
  })
})
</code></pre>
</li>
<li><p>Make the query
I referenced github user <a href="https://gist.github.com/int128/b0e75e3043c8a33808cea0089d988ed3">int128&#39;s gist</a> for the structure of the graphQl query:</p>
<pre><code class="language-js">let request = await githubClient.query({
  query: gql`
    {
      repository(owner: &quot;laudebugs&quot;, name: &quot;articles&quot;) {
        defaultBranchRef {
          target {
            ... on Commit {
              file(path: &quot;/&quot;) {
                type
                object {
                  ... on Tree {
                    entries {
                      name
                      object {
                        ... on Blob {
                          text
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  `
})
</code></pre>
</li>
<li><p>Parse your output to obtain the file&#39;s contents.
By making this same query on Github&#39;s GraphQL explorer, the data returned, which is essentially a json object, at the time of writing this, looks like this:</p>
<pre><code class="language-json">{
  &quot;data&quot;: {
    &quot;repository&quot;: {
      &quot;defaultBranchRef&quot;: {
        &quot;target&quot;: {
          &quot;file&quot;: {
            &quot;type&quot;: &quot;tree&quot;,
            &quot;object&quot;: {
              &quot;entries&quot;: [
                {
                  &quot;name&quot;: &quot;QraphQL.md&quot;,
                  &quot;object&quot;: {
                    &quot;text&quot;: &quot;# Exploring GraphQL\n&quot;
                  }
                },
                {
                  &quot;name&quot;: &quot;README.md&quot;,
                  &quot;object&quot;: {
                    &quot;text&quot;: &quot;# Articles&quot;
                  }
                }
              ]
            }
          }
        }
      }
    }
  }
}
</code></pre>
<p>So, if to obtain the entries, we would access them by:</p>
<pre><code class="language-js">let result = request.data.repository.defaultBranchRef.target.file.type.object.entries
</code></pre>
</li>
</ol>
<h4 id="using-axios">Using Axios</h4>
<ol>
<li><p>Install the <a href="https://github.com/axios/axios">axios npm package</a></p>
<pre><code class="language-bash">npm install axios
</code></pre>
</li>
<li><p>Import exios into your node project:</p>
<pre><code class="language-js">import axios from &#39;axios&#39;
</code></pre>
</li>
<li><p>initialize an authentication object and the query string that will be attatched to your request</p>
<pre><code class="language-js">// The Authorization in the header of the request
const oauth = { Authorization: &#39;bearer &#39; + token }

// The Query String
const query = `
        {
          repository(owner: &quot;laudebugs&quot;, name: &quot;articles&quot;) {
            defaultBranchRef {
              target {
                ... on Commit {
                  file(path: &quot;/&quot;) {
                    type
                    object {
                      ... on Tree {
                        entries {
                          name
                          object {
                            ... on Blob {
                              text
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      `
</code></pre>
</li>
<li><p>Make the request, adding in the query and the header</p>
<pre><code class="language-js">let request = axios.post(githubUrl, { query: query }, { headers: oauth })
</code></pre>
</li>
<li><p>Parse your output as above:</p>
<pre><code class="language-js">let result = request.data.repository.defaultBranchRef.target.file.type.object.entries
</code></pre>
</li>
</ol>
<h2 id="referenced-articles">Referenced articles</h2>
<ul>
<li><a href="https://gist.github.com/MichaelCurrin/6777b91e6374cdb5662b64b8249070ea">Graph QL query</a> for getting files.</li>
<li><a href="https://support.insomnia.rest/article/61-graphql">GraphQL Queries using Insomnia</a></li>
<li>How to create a personal access token from your github account: <a href="https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token">Creating a personal access token</a></li>
<li><a href="https://medium.com/risan/set-authorization-header-with-apollo-client-e934e6517ccf">Set Authorization Header with Apollo Client</a></li>
<li><a href="https://stackoverflow.com/questions/58576940/how-to-handle-authorization-header-with-apollo-graphql">How to handle authorization header with apollo graphql? - Stack Overflow</a></li>
<li><a href="https://dev.to/thomasaudo/get-started-with-github-grapql-api--1g8b">Get started with GitHub GraphQL API 👨‍🔬</a> on Dev</li>
</ul>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/using-your-github-repository-as-a-database.webp" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/using-your-github-repository-as-a-database.webp"/>
                    
                    

            </item>
            
            <item>
                <title>How To: Build an Express GraphQL API in TypeScript</title>
                <link>https://www.laudebugs.me/dev/build-graphql-api-in-typescript</link>
                <pubDate>2021-02-23T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/dev/build-graphql-api-in-typescript</guid>
                <description>
                    <![CDATA[ Learn how to build a Node.js GraphQL API with Express and TypeScript]]>
                </description>
                <content:encoded>
                    <![CDATA[ <Aside>
So you wanna write a node GraphQL backend in TypeScript? Well, Let's put all the pieces together and begin.
</Aside>

<h2 id="initilizing-the-project">Initilizing the project</h2>
<p>We&#39;ll be working in a terminal - be it the standalone teminal - or the integrated terminal in your text editor.</p>
<ol>
<li><p>Initialize a repository in an empty folder, say, we call ours <code>express-gql-api</code>.:</p>
<pre><code class="language-bash">mkdir express-gql-api

# enter the project folder
cd express-gql-api
</code></pre>
</li>
<li><p>Initialize the folder as a node and git project:</p>
<pre><code class="language-bash">npm init -y

git init
</code></pre>
</li>
<li><p>Initialize a Readme
Having a Readme is essential for any project - this is the document people will see when they come across your repository in Github.</p>
<br />
We'll start off with a simple description but feel free to add more information about your project as you go. ```bash echo '# Express GraphQLAPI
in TypeScript' > Readme.md ```</li>
<li><p>Initalize the source directory
This is where we will place our <code>.ts</code> files</p>
<pre><code class="language-bash">mkdir src
</code></pre>
</li>
</ol>
<h2 id="typescript-and-project-setup">TypeScript and Project SetUp</h2>
<p>TypeScript is usually compiled to JavaScript and which is the code that is actually run.
Assuming you already have the <a href="https://www.typescriptlang.org/id/download">TypeScript</a> installed, we will write the configuration file that tells the TypeScript compiler how to compile out files into JavaScript:</p>
<ol>
<li><p>Initialize a <code>tsconfig.json</code> file in your root directory from the terminal</p>
<pre><code class="language-bash">tsc --init
</code></pre>
</li>
<li><p>. Working in the text editor, we will set the following compiler options:</p>
<ol>
<li><p><code>&quot;rootDir&quot;</code> - this is the directory where the TypeScript compiler will search for <code>.ts</code> files to compile into JavaScript. In our case, the root directory is the <code>src</code> folder:</p>
<pre><code class="language-json">{
  &quot;compilerOptions&quot;: {
    //...
    &quot;rootDir&quot;: &quot;./src&quot;
    //...
  }
}
</code></pre>
</li>
<li><p><code>&quot;outDir&quot;</code> - this is the directory where the compiled JavaScript will be placed:
In our case, we will call our output directory <code>&quot;dist&quot;</code></p>
<pre><code class="language-json">{
  &quot;compilerOptions&quot;: {
    //...
    &quot;rootDir&quot;: &quot;./src&quot;,
    &quot;ourDir&quot;: &quot;./dist&quot;
    //...
  }
}
</code></pre>
</li>
</ol>
</li>
<li><p>Finally, we will edit the package.json file so that we have a smooth time running the project. Add the following line under script in <code>package.json</code>:</p>
<pre><code class="language-json">&quot;scripts&quot;:{
  //...
   &quot;start&quot; : &quot;nodemon dist/index.js&quot;,
   //...
}
</code></pre>
</li>
</ol>
<p>This is the basic setup that we need before we get started</p>
<h2 id="setup">SetUp</h2>
<p>We will be working with a few packages that we need to install:</p>
<ol>
<li><a href="https://www.npmjs.com/package/express"><code>express</code></a> - since we are buiding an express server</li>
<li><a href="https://www.npmjs.com/package/express-graphql"><code>express-graphql</code></a> - this is the express middleware that will allow us to build our graphQL endpoint</li>
<li><a href="https://github.com/ardatan/graphql-tools#readme"><code>graphql-tools</code></a> - A package that helps build the GraphQL Schema</li>
<li><a href="https://www.npmjs.com/package/mongoose"><code>mongoose</code></a> - The library that will allow us to connect to a MongoDB database</li>
</ol>
<p>Before we jump right into installing the packages, let&#39;s create a <code>.gitignore</code> file at the root folder and add <code>node_modules</code> so that git doesn&#39;t track npm packages:</p>
<pre><code class="language-bash">echo node_modules &gt; .gitignore
</code></pre>
<p>To install all the tools, we can do so in one command:</p>
<pre><code class="language-bash">npm install -s express express-graphql graphql-tools mongoose nodemon
</code></pre>
<p>Before we start writing some code, we need to have our TypeScript compiler running so that we can generate the JavaScript files as we go. So, in a separate window, run the typescript compiler with a watch flag:</p>
<pre><code class="language-bash">tsc -w
</code></pre>
<p>And now we are ready to build our api</p>
<h2 id="the-api-🥑">The API 🥑</h2>
<p>Let&#39;s add some files to our file structure first:</p>
<pre><code>📦express-gql-api
┣ 📂src
┣ 📜.gitignore
┣ 📜Readme.md
┣ 📜package.json
┗ 📜tsconfig.json
</code></pre>
<p>However, let&#39;s add some files in the <code>src</code> folder first
Create an empty directory in the <code>src</code> folder called <code>data</code> - this is where we willl be placing out database connectors, types, schemas and resolver files.
Create the following files to match the following structure:</p>
<pre><code>📦express-gql-api
┣ 📂src
┃ ┣ 📂data
┃ ┃ ┣ 📜db.ts
┃ ┃ ┣ 📜resolvers.ts
┃ ┃ ┣ 📜schema.ts
┃ ┃ ┗ 📜types.ts
┃ ┗ 📜index.ts
┣ 📜Readme.md
┣ 📜package.json
┗ 📜tsconfig.json
</code></pre>
<h3 id="schema-definition">Schema Definition</h3>
<p>GraphQL requires a schema to be defined. A schema what graphQL uses to know what type of data to expect.</p>
<br />
We will define the schema in the `schema.ts` file in the following way: We will use our graphQL endpoint to create and query a user. So we need
to define:

<ul>
<li><p>a <code>user</code> type</p>
</li>
<li><p>a <code>UserInput</code> input - that has the same structure as the <code>User</code> type</p>
</li>
<li><p>a <code>Query</code> type - where we will define all the queries</p>
</li>
<li><p>a <code>Mutation</code> type - where we will define the mutations</p>
<pre><code class="language-ts">import { resolvers } from &#39;./resolvers&#39;
import { makeExecutableSchema } from &#39;graphql-tools&#39;

const typeDefs = `
    type User {
      name: String
      username: String
    }
    input UserInput {
      name: String
      username: String
    }
    type Query {
      getUser(username: String): User
    }
    type Mutation{
      createUser(user: UserInput): User
    }
  `
// Build the schema and export
const schema = makeExecutableSchema({ typeDefs, resolvers })
export { schema }
</code></pre>
</li>
</ul>
<h3 id="definine-the-type---typets">Definine the type - <code>type.ts</code></h3>
<pre><code class="language-ts">export class UserType {
  constructor(public name: String, public username: String) {}
}
</code></pre>
<h3 id="connecting-the-database">Connecting the Database</h3>
<h4 id="setting-up-the-mongodb-instance-🗄️">Setting up the MongoDB instance 🗄️</h4>
<p>Before we move into this step, we will need to first set up our database. One can do so by following this process:</p>
<ol>
<li>create a free MongoDB account <a href="https://account.mongodb.com/account/login">here</a></li>
<li>Create a <strong>free</strong> cluster.</li>
<li>Once the cluster has been created, click <code>connect</code> to your cluster. Further instructions can be found <a href="https://docs.atlas.mongodb.com/connect-to-cluster/">here</a></li>
<li>You will need to add a connection IP address - typically your own IP for development locally</li>
<li>create a database user with a username and password - You will need this to login to your database later</li>
<li>Proceed to choosing a connection method - in our case we will use the <code>connect your application</code> option</li>
<li>This will lead us to a page to select our driver and version - which in our case should be <code>Node.js</code> Version <code>3.6 or later</code>.</li>
<li>Copy your connection string somewhere safe that you can edit. You will notice that the username is included in the connection string but you will need to replace the <code>&lt;password&gt;</code> with your password and also pick a name for our database
{&#39; &#39;}<br />
Assuming our username was `amani` with password `AEDPfTeq61WH04NL`, and we want our database to be called `bliss`, our connection string would
look like:</li>
</ol>
<pre><code class="language-txt">mongodb+srv://amani:AEDPfTeq61WH04NL@cluster0.9ntf0.mongodb.net/bliss?retryWrites=true&amp;w=majority
</code></pre>
<ol start="9">
<li>Save this connection string somewhere where you can reference it later as we will need it when running our program.</li>
</ol>
<h4 id="connecting-to-the-database-programmatically---dbts">Connecting to the Database programmatically - <code>db.ts</code></h4>
<p>In the <code>db.js</code> file, we will import <code>mongoose</code> and then define a new schema for the database - in our case, the only schema we will need is the user schema.</p>
<br />
We will then create a new mongoose model that will be exported for use to query the database. Notice that we have set the `connectionString`
variable to an environment variable - this is safer than pasting the connection string right into your code because it makes your database vulnerable.
In our case, will set the connection string to an environment varible when we are ready to run the application.

<pre><code class="language-ts">import mongoose from &#39;mongoose&#39;
const Schema = mongoose.Schema

// @ts-ignore
const connectionString: String = process.env.MONGO_DB
// @ts-ignore
mongoose.connect(connectionString, {
  useNewUrlParser: true,
  useUnifiedTopology: true,
  useFindAndModify: false,
  useCreateIndex: true
})

const UserSchema = new Schema({
  name: String,
  username: String
})

const User = mongoose.model(&#39;User&#39;, UserSchema)

export { User }
</code></pre>
<h3 id="declaring-the-resolvers---resolversts">Declaring the resolvers - <code>resolvers.ts</code></h3>
<p>The resolvers are the functions that are run whenever the endpoint is run - so you need to define a function for each query and mutation as we will do below:</p>
<pre><code class="language-ts">import { UserType } from &#39;./types&#39;

// import the User from the database
import { User } from &#39;./db&#39;

export const resolvers = {
  Query: {
    //@ts-ignore
    getUser: (root, { username }) =&gt; {
      return User.findOne({ username: username })
        .then((user: UserType) =&gt; {
          return user
        })
        .catch((error: any) =&gt; {
          console.log(error.message)
        })
    }
  },
  Mutation: {
    // @ts-ignore
    createUser: async (root, { user }) =&gt; {
      const newUser = new User({ name: user.name, username: user.username })
      await newUser.save()
      return newUser
    }
  }
}
</code></pre>
<h2 id="piece-the-pie-together-🥧---indexts">Piece the pie together 🥧 - <code>index.ts</code></h2>
<p>Our <code>index.ts</code> file is where all the majic happens. We will begin by importing the necessary packages and instantiating a new express app. Then we will initialize the connection to the database and attach the <code>grapqlHTTP</code> middleware function with our schema and <code>graphiql</code> - which we can use to explore the api:</p>
<pre><code class="language-ts">import express from &#39;express&#39;
import { graphqlHTTP } from &#39;express-graphql&#39;
import { schema } from &#39;./data/schema&#39;

// Initialize app
const app = express()
require(&#39;./data/db&#39;)

// the graphQL endpoint at /graphql.
app.use(&#39;/graphql&#39;, graphqlHTTP({ schema: schema, graphiql: true }))

app.get(&#39;*&#39;, (req, res) =&gt; {
  res.json({ message: &#39;Welcome to the api&#39; })
})

const PORT = 7000
app.listen(PORT, () =&gt; {
  console.log(`api is running on port ${PORT}`)
})
</code></pre>
<h2 id="running-the-server">Running the server</h2>
<p>Before we run the server, we will need to add our mongoDB connection string to the environment variables:</p>
<pre><code class="language-bash">  export MONGO_DB=&#39;mongodb+srv://amani:AEDPfTeq61WH04NL@cluster0.9ntf0.mongodb.net/bliss?retryWrites=true&amp;w=majority&#39;
</code></pre>
<p>Now, we are ready to run the server 🚀</p>
<pre><code class="language-bash">  npm run start
</code></pre>
<p>And we can run the server and explore our api.
Here&#39;s an example of a mutation that you can make with the api:</p>
<h3 id="mutation">Mutation</h3>
<p>We can add a user by making a mutation on the api:</p>
<pre><code class="language-txt">  mutation createUser ($input:UserInput){
    createUser(user:$input) {
      name
      username
    }
  }
</code></pre>
<p>We can then pass in the user input using the query variables:</p>
<pre><code class="language-txt">  {
    &quot;input&quot;: {
      &quot;name&quot;: &quot;Laurence&quot;,
      &quot;username&quot;: &quot;laudebugs&quot;
    }
  }
</code></pre>
<p>Here&#39;s how the mutation looks like:</p>
<p>&lt;img src={&#39;<a href="https://raw.githubusercontent.com/laudebugs/articles/main/resources/graphqlmutation.png&#39;%7D">https://raw.githubusercontent.com/laudebugs/articles/main/resources/graphqlmutation.png&#39;}</a> width={&#39;100%&#39;} /&gt;</p>
<h3 id="query">Query</h3>
<p>If we were to then ask the api for a certain user, we can make the query by:</p>
<pre><code class="language-txt">  query {
    getUser (username:&quot;laudebugs&quot;){
      name
    }
  }
</code></pre>
<p>You can check out the <a href="https://github.com/laudebugs/express-gql-api">repo here</a></p>
<h3 id="common-issues-that-you-may-run-into">Common Issues that you may run into:</h3>
<ol>
<li><strong>IP isn&#39;t whitelisted</strong>: If you&#39;re running into this issue, it may be the case that your ip address has changed and you need to add your current IP to be able to connect.</li>
<li><strong>Could not find a declaration file for &#39;express&#39;</strong>
Install the declaratio file for <code>express</code>:</li>
</ol>
<pre><code class="language-bash">    npm install --save-dev express
</code></pre>
<h2 id="further-reading">Further Reading</h2>
<ul>
<li><a href="https://medium.com/atheros/graphql-quick-tip-how-to-pass-variables-into-a-mutation-in-graphiql-23ecff4add57">GraphQL quick tip: How to pass variables in GraphiQL</a></li>
</ul>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/build-graphql-api-in-typescript.jpg" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/build-graphql-api-in-typescript.jpg"/>
                    
                    

            </item>
            
            <item>
                <title>Bubbling</title>
                <link>https://www.laudebugs.me/journal/bubbling</link>
                <pubDate>2021-01-17T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/journal/bubbling</guid>
                <description>
                    <![CDATA[ In the dust there lays some sense of what was past, what was forgotten and what was lost. My feet sink into the particles as I grab onto what are my calloused toes from too much running away from something. I dare not look back – dare not tempt the shadows that I conquered.]]>
                </description>
                <content:encoded>
                    <![CDATA[ <p>In the dust there lays some sense of what was past, what was forgotten and what was lost. My feet sink into the particles as I grab onto what are my calloused toes from too much running away from something. I dare not look back – dare not tempt the shadows that I conquered. Hair – loose and breaking I wonder if I&#39;ll ever see its full length. Several things are amiss. I miss several things. The feeling is severe, wretched and I hold my breath.</p>
<p>Now would be a good time to wake up from this dream. I think I&#39;ve slept too long and my head hurts from too much wine. I need water. Where can we find things that seem to not want to be found? With limited space, how will I know what to bring on this journey? What lies are worth believing?</p>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/bubbling.jpg" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/bubbling.jpg"/>
                    
                    

            </item>
            
            <item>
                <title>Sinking</title>
                <link>https://www.laudebugs.me/journal/sinking</link>
                <pubDate>2020-12-28T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/journal/sinking</guid>
                <description>
                    <![CDATA[ I sink Into patterns, A slow agony of todos and expectations, Demanding of me, wanting from me, What I do not seem to have, but questions of: What not to do, what to prioritize, When to sleep?]]>
                </description>
                <content:encoded>
                    <![CDATA[ <p>I sink Into patterns, A slow agony of todos and expectations, Demanding of me, wanting from me, What I do not seem to have, but questions of: What not to do, what to prioritize, When to sleep? What to want? What is necessary? Seem to blur into each other. How can I answer all these and take care of my mind, remember to eat, run, laugh and pray? But isn&#39;t this struggle the whole point of life – friend remarks. And so I dive, into the depths, finding myself often in places my senses are muted. Do I have the courage to fall is the all important question.</p>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/sinking.jpg" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/sinking.jpg"/>
                    
                    

            </item>
            
            <item>
                <title>Thinking About The Color of My Skin</title>
                <link>https://www.laudebugs.me/journal/thinking-about-the-color-of-my-skin</link>
                <pubDate>2020-11-18T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/journal/thinking-about-the-color-of-my-skin</guid>
                <description>
                    <![CDATA[ The question I often asked was some flavor of “how do I tell stories that portray the diversity and complexity of our people?”]]>
                </description>
                <content:encoded>
                    <![CDATA[ <p>Coming from the East of Africa, I grew up without a perception that my skin came with a certain sense of existence different from others. Rather than my existence being centered around the color of my skin, the question I often asked was some flavor of “how do I tell stories that portray the diversity and complexity of our people?” To answer this question, I needed to look back in order to look ahead. Although colonization was motivated by the idea of one group of people being of lesser value viewed as lower than another, this concept of caste is not only remnant but has morphed into bitter and negative ethnicity among Kenyan tribes. The color of my skin, then, was just a happenstance even as it took much more meaning in college.</p>
<p>In general, English places adjectives before the subject unlike Swahili or French (the later that I spoke since 14), . So, what ends up happening is that in describing a person, their skin color is pointed out before we know that subject is first and foremost a person. With an attempt then, to place the person before the color of their skin (a person who is black as opposed to a black person), I witnessed the slight dissociation of skin color from becoming the defining characteristic of a person. I can therefore be a human being before I am black, I can be a man before I am black, I can be a Kenyan before I am black. Since in Kenya there was no concept of being black, I began to try to understand Black America, through conversation, the media, and readings. Back home, there is a loose concept of blackness and so, as I asked myself the question of “how do I move through this space as a black person,” I found myself having to stop and think in a state of mind that did not occur naturally to me. To have to think about the tone of my voice, the cadence of my speech, how my feet bounced off the ground, how my hands and neck revealed my body language, whether, if in fact, I was a danger to anyone was exhausting. I think of how my female friends talked about how they work against the grain of a misogynist culture, how they have to be aware of their senses because you can never be too careful. How then could I weave my identity as a person of color while sustaining my Kenyan heritage, and could the two exist in concert?</p>
<p>It was in the fall of 2016 that I became awakened to the color my skin having implications on how I moved through spaces. More and more, I began to see how the color of one&#39;s skin can be a reference point to the kinds of lives that could be extrapolated solely from what color that is. How black America was presented was very much a culture as well as the stories that are attached to its invention. To be black was also taking up and actualizing an identity from the color of my skin. Superficial blackness is for instance, cool, slick, having that delicate bounce and smooth swagger. The deeper work was internal – from understanding my relationship with my skin – learning how to take care of it in the cold and dry winters and reaffirming its affinity to the warm tropical weather. I was growing out my hair since the end of high school and taking delight in how it looked up with its coarse texture. And this process intrigued me, along with the idea of becoming black. An African such as I could empathize with the African American history of being enslaved and discriminated against, judged, lynched because of the color of one&#39;s skin. But in as much as this dark history seemed to take center stage in discussions and conversations about racism against African Americans, there were stories of tremendous triumph, stories of black enterprise and far-reaching cultural movements in music, art and literature among others. Whether I could juggle these two aspects as well as discover other manifestations of the black experience remains an unresolved journey. Three realities emerged. There were environments where I became fully conscious of the color of my skin, there were also spaces where I could forget the color of my skin mattered and there were situations where the consciousness and lack thereof existed in a kind of tug of war.</p>
<p>There is a kind of inward gaze about black America to which I observed: that the story of the black America is often centered on his experience and how that experience shapes his worldview. For me, a Kenyan who looked outward into the world, and from the outward gaze into America, the process of looking inside led me, first, to take a closer look at how I was raised and how that upbringing did, if in any way, shape my black experience; and secondly, to begin to pay attention to how I navigate different spaces and social situations and how this navigation is informed by (a) my being a man and (b) having black skin. Very quickly after I arrived in New York that Fall of 2016, I became more aware of how, as a black man, could walk the streets of New York at odd hours of the night and carry little fear – unlike some of my fellow female friends who kept mentioning the amount of caution that they exercised in the city at night. With the #MeToo movement, various celebrities, and public figures, primarily men, began facing the consequences of taking advantage of their power and influence to assault and/or take advantage of women. This calling out of men couple with conversations with others, as well as reading, began to open my eyes to the power dynamics that exist when I interact with women and how much work there still remains to be done, especially back home, for men to understand how it is also up to them, not only women, to examine the ways they are brought up to view women. While growing up there was a lack of direct conversation surrounding what it means to be a man, I took mental notes from my father who was a preacher/minister and an academic, in that order. Looking then, into my blackness, I defaulted towards my faith and my upbringing – neither of which seemed to provide adequate fodder for my nascent conceptualizations of Black America. Black America needed me, a man with black skin, to keep engaging with its gripping past to bring myself to an understanding of its present form in daily life.</p>
<p>Toni Morrison, in her book, The Origin of Others talks about how easy it is to estrange others. I think about this idea of Othering, how many times I have made it possible for someone to feel different, how deliberate as well as easy this act can be – to not listen deeply, to make lazy assumptions, to not want to know someone&#39;s experience, to put value to one person over another. I think again of how simple the process of seeing the world with my eyes alone. “Why should we want to close the distance when we can close the gate?” – Morrison asks. Yet, to the many who have stretched themselves to me, it has been a selfless gift as well as a humanizing and healing process. How much more, then, am I to give an ear, to take a pause, be open to another point of view, step into the discomfort and know another?</p>
<p>Credit to one dear friend who helped me edit this piece.</p>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/thinking-about-the-color-of-my-skin.jpg" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/thinking-about-the-color-of-my-skin.jpg"/>
                    
                    

            </item>
            
            <item>
                <title>A Meditation on 24</title>
                <link>https://www.laudebugs.me/journal/a-meditation-on-24</link>
                <pubDate>2020-10-17T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/journal/a-meditation-on-24</guid>
                <description>
                    <![CDATA[ Twenty-four 1. is slow, is fast, is blurry and gets lost often, in its becoming. 2. It has no ring to it, no newness, no uniqueness. 3. Everything is mundane with rabid desires and ugly hair in odd places. 4. Memory remains of the past and the future is yet one day at a time.]]>
                </description>
                <content:encoded>
                    <![CDATA[ <p>Twenty-four</p>
<ol>
<li><p>is slow, is fast, is blurry and gets lost often, in its becoming.</p>
</li>
<li><p>It has no ring to it, no newness, no uniqueness.</p>
</li>
<li><p>Everything is mundane with rabid desires and ugly hair in odd places.</p>
</li>
<li><p>Memory remains of the past and the future is yet one day at a time.</p>
</li>
<li><p>One day at a time. How much further can I look but one day at a time.</p>
</li>
<li><p>For I cannot escape the loop unless I can see it.</p>
</li>
<li><p>Where can I find questions?</p>
</li>
<li><p>How do I begin to question myself and not risk falling into myself?</p>
</li>
<li><p>Deep cuts hurt less yet much more than ever.</p>
</li>
<li><p>Can I find time to pause?</p>
</li>
<li><p>My black skin seems even darker when I look at it.</p>
</li>
<li><p>Sometimes I think it&#39;s easier to forget.</p>
</li>
<li><p>At times I figure it is much simpler to get out of my head.</p>
</li>
<li><p>There are certain selves that seem so elusive.</p>
</li>
<li><p>Can we fix those parts that are worn out?</p>
</li>
<li><p>Who I am and who I think I am are two worlds apart.</p>
</li>
<li><p>But I walk that thin line between hope and fear,</p>
</li>
<li><p>In 100 years perhaps I will have mastered the art of high-wire artistry,</p>
</li>
<li><p>No longer needing these doubts that seem to fuel my demise.</p>
</li>
<li><p>Going back one step back to take two steps forward.</p>
</li>
<li><p>Yellow birds across the sky – turn to grey dust and fade away.</p>
</li>
<li><p>Just a single amen can save a soul.</p>
</li>
<li><p>Under which pressures will I bow?</p>
</li>
<li><p>XOXO</p>
</li>
</ol>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/a-meditation-on-24.jpg" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/a-meditation-on-24.jpg"/>
                    
                    

            </item>
            
            <item>
                <title>September</title>
                <link>https://www.laudebugs.me/journal/september</link>
                <pubDate>2020-09-07T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/journal/september</guid>
                <description>
                    <![CDATA[ I'm in September. And it almost feels like a transition with an end of the longest summer in my mind. The days are now here and there marked by rain, the shrinking daylight, and later the deadening of leaves. This September my mother turns sixty.]]>
                </description>
                <content:encoded>
                    <![CDATA[ <p>&#39;m in September. And it almost feels like a transition with an end of the longest summer in my mind. The days are now here and there marked by rain, the shrinking daylight, and later the deadening of leaves. This September my mother turns sixty. The more I remember that, the more I try to remember when she turned 50 or 45 and the kinds of things that happened in between those years and think that I would want to white out some parts and keep others. But again, it&#39;s September and how can I not let this month pass by yet is stands between me and the rest of the year; between me and thinking of the uncertainty what&#39;s to come. But even more so than ever, each day that is not closely watched can turn into a week and month passing by and forgotten and so I want to note that I am here now, in September, watching the birds make their enclaves outside my window the heat claiming more of the morning and the people stepping into their forays. Oh, but how can I grab on to this month and not let it slip?</p>
<p>I begin a schedule – early mornings and perhaps I won&#39;t spend too much time thinking about regressing into lax streaks. I&#39;m trying to bring our conversations down to earth with a friend because they risk being over there and not in the moment and wonder how to do this without being too introspective. I am reading, and writing, listening, learning to get across what I mean to say. Tomorrow I will run. Though there&#39;s still today that just only began. Only just reared its head and let&#39;s deal with this day first.</p>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/september.jpg" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/september.jpg"/>
                    
                    

            </item>
            
            <item>
                <title>Chicken Soup</title>
                <link>https://www.laudebugs.me/journal/chicken-soup</link>
                <pubDate>2020-08-24T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/journal/chicken-soup</guid>
                <description>
                    <![CDATA[ Ibegin to meet with my mother on call each week in the Summer during the pandemic and we decide to talk about anything and everything. As we speak, my intention is to learn more about her and that, eventually, she can learn more about me.]]>
                </description>
                <content:encoded>
                    <![CDATA[ <p>I begin to meet with my mother on call each week in the Summer during the pandemic and we decide to talk about anything and everything. As we speak, my intention is to learn more about her and that, eventually, she can learn more about me. She shares stories of how she met my father and studied in America. She tells me of her joyous and happy moments as well as her struggles and mistakes. All the while I am thinking back to my own version of events of the past few years and the parallels I could draw and realize how much more than a mother she is. What these conversations do for me is help me expand my view of my mom to more than just that one-dimensional aspect of being mother. I learn a little more about what she loved doing, her motivations and perspectives.</p>
<p>The stories she tells draw out time in a way by bringing the choices of others to influence my existence at this moment in time stretching as well as into the lives of many of those after me. That life is uneasy, rarely takes the form of a straight line is a recurrent theme in our conversations. Without being explicit, she hints that I will have to brave the unknowable future – with faith – evident in how she herself took on challenges with grace.</p>
<p>With the lot of advice given to me before I left for America – some in passing and most alluded within the jokes, goodbyes and prayers, my mother then and even now in our talks tells me that I am the best. Although she has and always had the best intentions in mind, I begin to wonder how this kind of affirmation translates to knowing how to brave the tough times when things are out of control or when I fail. How will I find the courage to come to speak to her during the difficult moments when she projects a version of me that falls seems flawless? Each time after these conversations that I have had with my mother, I think it is necessary that I do hear the stories of her ambitions, joys as well as the trials in her life as the listening saves me from thinking that my own shortcomings are too big to overcome, that I should as well celebrate each small victory in my life</p>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/chicken-soup.jpg" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/chicken-soup.jpg"/>
                    
                    

            </item>
            
            <item>
                <title>Passive Observations</title>
                <link>https://www.laudebugs.me/journal/passive-observations</link>
                <pubDate>2020-08-22T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/journal/passive-observations</guid>
                <description>
                    <![CDATA[ It is imperative for you look beyond my words for truth. Be wary of simplifications and too much complexity. Even more, centrist conclusions for how we wrestle with an idea is particularly suspicious and may be flavored sometimes with anger, other times with pain and at times with hope just to make them that more palatable. ]]>
                </description>
                <content:encoded>
                    <![CDATA[ <p>It is imperative for you look beyond my words for truth. Be wary of simplifications and too much complexity. Even more, centrist conclusions for how we wrestle with an idea is particularly suspicious and may be flavored sometimes with anger, other times with pain and at times with hope just to make them that more palatable. Probe each argument, metaphor and choice of words. The questions are almost always more important than what answers they solicit – how each is framed, the assumptions each makes, who it includes and leaves out, what follows next. Think ahead and try to guess where I am taking you and if I take you somewhere else, I will have had something to offer. If the words remain in your mind, the writing was an intellectual process; if they move you, I will have done my part. I will be biased and shortsighted, eccentric and cunning. Therefore, I am asking you to do some hard but necessary work when reading me and every day in what you see, read and share – the kind of deep personal work that remains of each of us.</p>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/passive-observations.jpg" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/passive-observations.jpg"/>
                    
                    

            </item>
            
            <item>
                <title>Arrays of Hope Held Loose</title>
                <link>https://www.laudebugs.me/journal/arrays-of-hope-held-loose</link>
                <pubDate>2020-08-08T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/journal/arrays-of-hope-held-loose</guid>
                <description>
                    <![CDATA[ There is a lot that's going on. A lot of disarray within our composed lives that ask us to retract from what we know about ourselves and others and our worlds. Different forms of us emerge, perhaps for revealing what frames need testing, understanding.]]>
                </description>
                <content:encoded>
                    <![CDATA[ <p>There is a lot that&#39;s going on. A lot of disarray within our composed lives that ask us to retract from what we know about ourselves and others and our worlds. Different forms of us emerge, perhaps for revealing what frames need testing, understanding.</p>
<p>Funneling this energy, emotion and body into meaning is a task that&#39;s ridiculously slippery. And I for one, in fits and starts, only begin to scratch the surface – confronting the truths, the histories, the stories that I weave.</p>
<p>And holding on to these hopes I have with not the whys that inform them, how they form, what makes them persist is letting them stick loose enough to be move, shift, evolve while I look away.</p>
<p>It&#39;s the kind of thirst that only God can fill – fully – completely – dwelling in him. The next months will be dedicated to this exposition of the attitudes, hopes and joys, pain and brokenness that describe lives of pursuits.</p>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/arrays-of-hope-held-loose.jpg" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/arrays-of-hope-held-loose.jpg"/>
                    
                    

            </item>
            
            <item>
                <title>Looking Into The Gaps</title>
                <link>https://www.laudebugs.me/journal/looking-into-the-gaps</link>
                <pubDate>2020-07-10T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/journal/looking-into-the-gaps</guid>
                <description>
                    <![CDATA[ As a means of expression, I see writing to be a medium to clarify my thoughts, to work through experiences and to conceptualize meaning and make sense.]]>
                </description>
                <content:encoded>
                    <![CDATA[ <p>As a means of expression, I see writing to be a medium to clarify my thoughts, to work through experiences and to conceptualize meaning and make sense. When I wrapped myself around my sheets at the happiest times, or when I hugged my body during the darkest of moments and even when there was nothing else to do but stare into thin air to try and eke out a sense of the world around me, I took down notes. Whether that is the most characteristic of me, some will say yes.</p>
<p>Ask me what I think is the most pressing issue for me, and I pause, then stutter between, one: the upside and downsides of my “born-tao” (brought up in the city) childhood growing up in an ethnically diverse community and two: bridging the gaps in technology, education and empowerment for women, men and the poor in the society. Where all this becomes possible is through understanding what it takes to build an idea from concept to product and to then leverage the designs that are not only elegant but also consider other points of view.</p>
<p>The simplest way I can describe the intersection between having an inclination to write poetry and code is that each is a an attempt to bridge the gap between the inconsistencies that presents themselves in the world that we exist in today. By acknowledging the brokenness in systems, recognizing how fragmented and diverse human desire is – then I find the courage to begin to look into the gaps, plough through my own apathy and cynicism.</p>
<p>Lingering on this very question – of where the intersection lies between poetic eye and debugging code is a slope undefined for several reasons. One is tempted, as I have, to draw clear distinctions of where the former ends and the latter begins – a line so faint that when staring into it too much, it can feel crystal one day and maddeningly blurred the very next. Both are ways to escape into imagination. Yet neither can deliver what they promise fully and most of the time it ends up unfinished business and so, they both are a good lesson in learning when and how to look away. And so some days – most in actual sense – it feels as though my left and right eye are seeing two different renderings of reality, and I fear they may never reconcile fully.</p>
<p>&quot;Everyday each of us invents the world and the self who meets that world, opens up or closes down space for others within that.&quot; - Rebecca Solnit</p>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/looking-into-the-gaps.jpeg" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/looking-into-the-gaps.jpeg"/>
                    
                    

            </item>
            
            <item>
                <title>Demands</title>
                <link>https://www.laudebugs.me/journal/demands</link>
                <pubDate>2020-07-08T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/journal/demands</guid>
                <description>
                    <![CDATA[ I miss the appointment. They send a note. In looking over our records, it was noted that you have not returned to this chest center for re-examination. There are calls from mama. She wants to know how I'm doing.]]>
                </description>
                <content:encoded>
                    <![CDATA[ <p>I miss the appointment. They send a note.</p>
<p>In looking over our records, it was noted that</p>
<p>you have not returned to this chest center for re-examination</p>
<p>There are calls from mama. She wants to know how I&#39;m doing.</p>
<p>A new appointment has been scheduled for you.</p>
<p>If it is not possible to keep this appointment,</p>
<p>please call us immediately.</p>
<p>They&#39;re testing me for all these things that I never knew existed.</p>
<p>Mama says to call her!</p>
<p>There are drugs for everything.</p>
<p>Rifampin treats TB and other types of infections</p>
<p>I&#39;m experiencing other types of feelings.</p>
<p>Could you treat ME - I want to ask.</p>
<p>Do not use more than directed</p>
<p>It is important to take rifampin on a regular schedule</p>
<p>It is important to have a regular schedule.</p>
<p>I think about home cooked food.</p>
<p>Possible side effects while using this medication:</p>
<p>Itching or hives, Swelling in your face or hands,</p>
<p>I&#39;m adding weight.</p>
<p>Swelling or tingling in your mouth or throat, Chest tightness, trouble breathing</p>
<p>I have trouble breathing.</p>
<p>Blistering, peeling or red skin rash</p>
<p>I skipped the shower while I slept in for days.</p>
<p>Bloody or very dark urine</p>
<p>Blurred vision or eye pain</p>
<p>I need glasses to see what the teacher is writing.</p>
<p>Decrease in how much or how often you urinate</p>
<p>I go to the toilet every five minutes</p>
<p>Diarrhea that may contain blood</p>
<p>Fever, chills, cough, sore throat, and body aches</p>
<p>Sleep is what I need.</p>
<p>Nausea, vomiting, loss of appetite, or pain in your upper stomach</p>
<p>Numbness, pain, or tingling in your arms or legs.</p>
<p>Shortness of breath or troubled breathing</p>
<p>Unsteadiness or weakness</p>
<p>I need to go home.</p>
<p>Unusual bleeding bruising or weakness</p>
<p>Yellowing of your skin or the whites of your eyes</p>
<p>The sun is too bright.</p>
<p>Call your doctor right away if you notice any of these side effects</p>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/demands.jpg" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/demands.jpg"/>
                    
                    

            </item>
            
            <item>
                <title>Kenya Web Project</title>
                <link>https://www.laudebugs.me/dev/kenya-web-project</link>
                <pubDate>2020-06-05T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/dev/kenya-web-project</guid>
                <description>
                    <![CDATA[ Examining performance and data download sizes of Kenya's top 500 websites. Expecially in emerging markets, website developers need to build small and efficient web applications.]]>
                </description>
                <content:encoded>
                    <![CDATA[ <h2 id="foreground">Foreground</h2>
<p>The internet is a vast “land” with plenty of room to create, share and find content. Internet usage in Kenya is increasing by the year. COVID-19 brought to the forefront, the importance of having an online presence. Companies like Amazon, that were internet-first, widened their market share of internet sales and became more relevant. Those companies that didn&#39;t have an online presence realized that they needed to adapt quickly to the times. Even people realized that they needed to build an online presence to showcase their work or seek jobs.</p>
<p>In developing countries like Kenya, with companies shifting online, there is a growing need to track these trends. However, data is still expensive for the average Kenyan even though data consumption in the country has been increasing by the year. Therefor, in as much as companies are moving online, they need to take into consideration this very fact that accessing websites should not require a large amount of data – say more than 2MB (to be revised later). And web developers need to be creative in ways that they can reduce data consumption on subsequent website visits to the sites they build. For instance, through caching or giving users a low-data version of their site once they land on the page before loading any subsequent content.</p>
<h2 id="conceptualization">Conceptualization</h2>
<p>Through this project, I would like to accomplish two things:</p>
<ul>
<li>Create a directory of Kenyan websites - websites with extensions “.co.ke” or “.ke” or that include “kenya” within the domain name.</li>
<li>Of the collected sites, note how much data is downloaded on the landing page of the site and organize this data into a repository.</li>
</ul>
<h2 id="data-collection-and-preparation">Data Collection and Preparation</h2>
<p>How many websites can I find by randomly searching common words and the big companies in Kenya?</p>
<p>After considering this, I searched the internet to try to find a difinitive list of websites in Kenya. To find this list, I would have to contact organizations like The Kenya Network Information Centre (KENIC) to obtain this information. And although this seemed like the route to go, I also found a list of the top 500 websites in Kenya provided by Alexa and figured that this list would be more useful to use as a baseline of websites in Kenya. And as the list showed, there&#39;s a large number of domains that do not end with .co.ke or .ke that Kenyans use. After signing up for the free trial, I parsed the data to this json file using a simple java script.</p>
<p>Considering all the information that lighthouse provided, two metrics seemed important: the performance of the site as well as the amount of data downloaded when a user accesses the website., i.e. Total size.
Two metrics: Performance and total size downloaded on page load
The size will vary from device to device since mobile devices might have cached the websites before or CDN&#39;s might be used to deliver a faster and smaller payload.</p>
<h3 id="why-lighthouse">Why lighthouse?</h3>
<p>Google provides Lighthouse as a developer tool to generate a report on a website. The tool is also available as a commandline interface tool. However, I wanted to use the tool as part of a node.js project. Therefore, I stumbled on this project by Sahava on github from which I borrowed heavily and modified to my own use.
Generating the Reports</p>
<p>Since my computer crawled at even the thought of running lighthouse on 500 websites, I decided to run the runLightHouse.js script on AWS.
Here are the steps for creating an AWS instance:
After Creating an Amazon AWS account,
Create an EC2 instance. You can proceed with launching an instance that contains the free tier.</p>
<pre><code>Launch instance &gt; Ubuntu 20.04 (with Free tier eligible) &gt; General Purpose Instance Type &gt; Choose an Existing Key-pair for ssh into your instance or download a new instance &gt;Launch instance
</code></pre>
<p>I however created an instance type with a GPU (just for fun) and to perform lighthouse processes fast. (this cost a couple of cents/hr)
SSH into your instance:</p>
<pre><code class="language-bash"># move your .pem file into the come folder
# Assuming you downloaded the .pem file to the downloads folder:
# cd into the root directory
cd
# check whether a .ssh folder exists
ls -al
# if it doesn&#39;t, create the folder
mkdir .ssh
#move the .pem file into the .ssh folder. Here assume I call my .pem file myKeyPair
mv [path where the .pem file exists]/myKeyPair.pem myKeyPair.pem
# change the permissions of the .pem file
chmod 400 .ssh/myKeyPair.pem
</code></pre>
<p>Copy your Public DNS (IPv4) from your AWS instance - this is usually located at your instances page when you click on your instance</p>
<p>SSH into your instance</p>
<pre><code class="language-bash"># replace [my public ip with the actual ip]
ssh -i ~/.ssh/myKeyPair.pem ubuntu@[Public DNS (IPv4)]
# you may be asked whether to type yes or no to proceed. Type yes to proceed

# Prepare the server to run your script
install git
sudo apt install git -y
# Install latest version of npm
curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash
sudo apt-get install -y nodejs
# Install Chrome. I referenced this article

# Download Google chrome
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
# install google chrome
sudo apt install ./google-chrome-stable_current_amd64.deb
</code></pre>
<h3 id="running-the-script">Running the script</h3>
<pre><code class="language-bash"># Clone the repo
git clone https://github.com/laudebugs/kenya-web-project.git

# enter project folder
cd kenya-web-project

# Install packages
npm install

# Run the script
node generateReports.js
# Exit the AWS instance
</code></pre>
<h2 id="update">Update</h2>
<p>I tried running the script on an amazon linux server but there was always an error generated, so I switched to using an ubuntu 20.04 instance.
Below are the instructions fro setting up and to ssh into an Amazon linux ec2 instance:
After Creating an Amazon AWS account,
Create an EC2 instance. You can proceed with launching an instance that contains the free tier.</p>
<pre><code class="language-text">Launch instance &gt; Amazon Linux (with Free tier eligible) &gt; General Purpose Instance Type &gt; Choose an Existing Key-pair for ssh into your instance or download a new instance &gt;Launch instance
</code></pre>
<p>I however created an instance type with a GPU (just for fun) and to perform lighthouse processes fast. (this cost a couple of cents/hr)
SSH into your instance:</p>
<pre><code class="language-bash"># move your .pem file into the come folder
# Assuming you downloaded the .pem file to the downloads folder:
# cd into the root directory
cd [root directory]

# check whether a .ssh folder exists
ls -al

# if it doesn&#39;t, create the folder
mkdir .ssh
#move the .pem file into the .ssh folder. Here assume I call my .pem file myKeyPair
mv [path where the .pem file exists]/myKeyPair.pem myKeyPair.pem

Change the permissions of the .pem file
```bash
chmod 400 .ssh/myKeyPair.pem
</code></pre>
<p>Copy your Public DNS (IPv4) from your AWS instance - this is usually located at your instances page when you click on your instance</p>
<pre><code>SSH into your instance
```bash
# replace [my public ip with the actual ip]
ssh -i ~/.ssh/myKeyPair.pem ec2-user@[Public DNS (IPv4)]
</code></pre>
<p>You may be asked whether to type yes or no to proceed. Type yes to proceed</p>
<pre><code class="language-bash"># Prepare the server to run your script
# install git
sudo yum install git -y
</code></pre>
<p>install npm using Amazon&#39;s instructions</p>
<pre><code class="language-bash"># Install latest version of npm
npm install -g npm@latest
# Install Chrome. I referenced this article
curl https://intoli.com/install-google-chrome.sh | bash
</code></pre>
<h4 id="running-the-script-1">Running the script</h4>
<pre><code class="language-bash">#Clone the repo
git clone https://github.com/laudebugs/kenya-web-project.git

# enter project folder
```bash
cd kenya-web-project
# Install packages
npm install
# Exit the AWS instance
exit
</code></pre>
<h2 id="analysis">Analysis</h2>
<p>Having obtained two additional metrics of each website, i.e. the performance and size of page downloaded, then we can plot several graphs to gauge how one metric affects the other.</p>
<p>In order to answer questions that the data presented, there still remained missing pieces of information that would enable me to analyze the data accurately. With metrics such as performance, size of webpage downloaded and average time spent on a website, we could take a look at the general trend of the top websites in Kenya as shown below.</p>
<p>&lt;img
    alt=&quot;Size of the web page downloaded&quot;
    src={
        &#39;<a href="https://raw.githubusercontent.com/laudebugs/kenya-web-project/master/analysis/graphs/Size%20of%20the%20web%20page%20downloaded.png&#39;">https://raw.githubusercontent.com/laudebugs/kenya-web-project/master/analysis/graphs/Size%20of%20the%20web%20page%20downloaded.png&#39;</a>
    }
    width={&#39;100%&#39;}
/&gt;</p>
<p>The optimal size of a web page is 0 - 1 MB downloaded once a use logs onto a site. This is of course, not taking into account cached resources that might reduce the size of the page downloaded.</p>
<p>&lt;img
    alt=&quot;TimeSpentOnWebsite&quot;
    src={&#39;<a href="https://raw.githubusercontent.com/laudebugs/kenya-web-project/master/analysis/graphs/TimeSpentOnWebsite.png&#39;%7D">https://raw.githubusercontent.com/laudebugs/kenya-web-project/master/analysis/graphs/TimeSpentOnWebsite.png&#39;}</a>
    width={&#39;100%&#39;}
/&gt;</p>
<p>The modal time spent on a website by Kenyans is 3 minutes with the average coming to 7.2682 minutes.</p>
<h2 id="postscript">Postscript</h2>
<p>In analyzing the dataset, there&#39;s a temptation to draw immediate conclusions from the various datapoints such as comparing the time spent and how this changes based on the size of the web page downloaded when a user accesses the website. However, this analysis doesn&#39;t take into account the fact that different websites serve different functions. For instance, a person logging into the Kenya Revenue Authority website would perhaps use the site for a specific predetermined use case while a person using YouTube might not have a goal in mind while using the site. And therefore one would need to make assumptions to immediately draw conclusions from the data. On modelling the graph, the size of the page downloaded doesn&#39;t relate to how much time is spent on the site.
Further information is needed to ask deeper questions from the dataset. One such piece is the genre of the website which would be able to draw distinctions between the different websites and make comparisons within websites of a certain type.
As of now, the dataset is freely available to use and for more research to be done. Especially at a time when the internet is crucial to keep systems moving during Covid-19, we need to examine more closely how Kenyans use the internet.
Hiccups along the way
In generating the lighthouse reports for the site, I decided to splice the list of websites into groups of 30 websites at a time - because even AWS servers weren&#39;t running all the reports smoothly. At other times, I ran 50 reports at a time. However, while doing this, I realised I skipped over close to 70 websites spread over my input set of 500. And so, I wrote a small python script to find the missing sites.
I had to manually run the website Bet365.com using the lighthouse cli because the node script kept timing out</p>
<h3 id="after-installing-lighthouse">After installing lighthouse</h3>
<p><code>npm install -g lighthouse</code></p>
<pre><code class="language-bash">lighthouse https://www.bet365.com/ --quiet --output json --output-path ./www_Bet365_com.json
</code></pre>
<h3 id="references">References</h3>
<ul>
<li><a href="https://github.com/sahava/multisite-lighthouse">Multisite Lighthouse</a></li>
<li><a href="https://www.alexa.com/topsites/countries/KE">Top Sites in Kenya</a></li>
<li>Google Chrome Lighthouse <a href="https://github.com/GoogleChrome/lighthouse">Github Repository</a></li>
</ul>
<p>-&gt; Checkout the <a href="https://github.com/laudebugs/kenya-web-project">Github Repository</a></p>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/kenya-web-project.jpeg" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/kenya-web-project.jpeg"/>
                    <media:credit>
                                <![CDATA[ Photo by <a href="https://unsplash.com/@joecalih?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Joecalih</a> on <a href="https://unsplash.com/s/photos/nairobi?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a> ]]>
                                </media:credit>
                    

            </item>
            
            <item>
                <title>Smirks</title>
                <link>https://www.laudebugs.me/journal/smirks</link>
                <pubDate>2020-04-21T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/journal/smirks</guid>
                <description>
                    <![CDATA[ I am stuck thus Gone are the spaces where I lie. Where does truth become a lie Becoming has a sly tune to it maybe because I realize I needed to be naive in the first place.]]>
                </description>
                <content:encoded>
                    <![CDATA[ <p>I am stuck thus Gone are the spaces where I lie. Where does truth become a lie Becoming has a sly tune to it maybe because I realize I needed to be naive in the first place and what happens is When no-one but me can see I find it harder and harder to speak, to tell, to describe, to know The places I go become the spaces I grow Out of myself and into myself – pushing past forms of me, letting new ones take root, but the old – that part that remains, in memory, in the scars, in the diary entries, the Layers and bones of the past forms don&#39;t simply sit still. They Hobble and steal fragrances that I spared bringing them back like it was yesterday</p>
<p>how easy Essences weave into skin, from memory, into the present,</p>
<p>Forming shapes and escapes into The wind that blows away memories and hopes I attempt to replace.</p>
<p>i Best be prepped for death in the many ways it comes. The Angel is holding her breath, letting her teeth sink into her lips, Awaiting a slip of a step, expectations can change the outcome, and even those I hide, find their way out, hungrier, fiercer to achieve their aim, If … when I failed to pay attention, [the lot of] demons bid for [my] skin. Can I Allow [my]self one fine glance to salvation&#39;s hope in prayers I already lost many days ago, or will I give someone the final dance, before I allow my soul to sink into rubble. Steep slopes emerge and so does any grip of fading lines that were once thick and clear. to get to where my gods really want, I let thin ropes merge my desire and transgression Empty hopes diverge and there&#39;s not just one path, but many that lead nowhere. I paste my disfigured portrait Onto faces and the like and all I get in return is a letting go of grains of myself that fall between the cracks. for this person I become, Family&#39;s never been too close yet too far gone. Voices bring more silence as they scream on the inside. not. out. On the walls are pictures, previous versions of the us stuck in time, events to commit to memory, But down and under brews peace, love and disunity.</p>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/smirks.jpg" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/smirks.jpg"/>
                    
                    

            </item>
            
            <item>
                <title>Certain Hopes</title>
                <link>https://www.laudebugs.me/journal/certain-hopes</link>
                <pubDate>2020-02-25T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/journal/certain-hopes</guid>
                <description>
                    <![CDATA[ Fragility is embedded in the hope of a better next time, Dust begins to settle in such a present, a comfortable promise]]>
                </description>
                <content:encoded>
                    <![CDATA[ <p>Fragility is embedded in the hope of a better next time,</p>
<p>Dust begins to settle in such a present,</p>
<p>a comfortable promise,</p>
<p>each day layering up with more fantasies,</p>
<p>a future that is always coming.</p>
<p>sunrises eating into time.</p>
<p>Faith prompts acting on the future of a present to come.</p>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/certain-hopes.jpg" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/certain-hopes.jpg"/>
                    
                    

            </item>
            
            <item>
                <title>Well Intended Forays Into Homecomings</title>
                <link>https://www.laudebugs.me/journal/well-intended-forays-into-homecomings</link>
                <pubDate>2020-02-16T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/journal/well-intended-forays-into-homecomings</guid>
                <description>
                    <![CDATA[ I know – for a fact! from whence I be. The earth sentenced me to foraging the dust until I've bored deep into the skin...]]>
                </description>
                <content:encoded>
                    <![CDATA[ <p>I know – for a fact! from whence I be.</p>
<p>The earth sentenced me to foraging the dust until I&#39;ve bored deep into the skin;</p>
<p>Playing the part of becoming something else.</p>
<p>Compromise is easy.</p>
<p>For the moments now barrage what could be.</p>
<p>To go back may be the only way to move forward.</p>
<p>Baba, I will bring you that which I promised:</p>
<p>A heart full of unfulfilled desires.</p>
<p>What you told me though,</p>
<p>I seem to let go the more I think of staying.</p>
<p>I don&#39;t own the tissues wrapping my shell together.</p>
<p>Should I let alternate ideas get birthed in me,</p>
<p>I know not where they lead.</p>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/well-intended-forays-into-homecomings.jpg" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/well-intended-forays-into-homecomings.jpg"/>
                    
                    

            </item>
            
            <item>
                <title>Who No Hears</title>
                <link>https://www.laudebugs.me/journal/who-no-hears</link>
                <pubDate>2020-02-15T00:00:00.000Z</pubDate>
                <author>Laurence B. Ininda</author>
                <guid>https://www.laudebugs.me/journal/who-no-hears</guid>
                <description>
                    <![CDATA[ At times, many times, the urge to fulfill others – to fill myself up with the noise and voices, to take all of the outside in and gobble it down with a shot of anxiety – is everywhere I turn my face. It is to me, one of the most compelling feelings yet the reality of what I face each time.]]>
                </description>
                <content:encoded>
                    <![CDATA[ <p>At times, many times, the urge to fulfill others – to fill myself up with the noise and voices, to take all of the outside in and gobble it down with a shot of anxiety – is everywhere I turn my face. It is to me, one of the most compelling feelings yet the reality of what I face each time. How I circulate the differing fluid feels that emanate from the extremities to the heart is a path clouded. There are ways I have dissociated the body from the mind, from the spirit, from the emotions, from the earth – in ways that make me wake up each morning and try to collect these parts of me. If I&#39;d like, I put on some way that I&#39;d like to look, place in my bag notes that I never read but that remind me I used to once collected, place aside my Kenyanness because it won&#39;t be needed, decide I want to smell a little nicer and watch myself walk to take the school bus. I never felt so divided up yet fitted all into one. I feel that I need to feel the earth below my feet but there&#39;s so much I don&#39;t know about the earth that I am walking on. If you look close enough at the eyes – they give it all away.</p>
<p>I chase memory.</p>
<p>No longer do I record moments because they, in part, I tend to plan to go out, plan to eat, plan to dance, and recording thrusts me into trying to control the past in the present. Give me time, this is where I am. I will learn again to forget.</p>
<p>Being in temporary disarray happens more often than I would like. And when I consider context, I reason to blame dislocation from memory. My past roots became upset and tried to adjust to new ground. Deep they sank into the self, only to unearth fears and dreams I have to consider giving up for a future that wants to etch itself onto me. I gaze, and my mouth wriggles into the shape shame if I bend to it.</p>
<p>For the best. They reckon.</p>
<p>Everyone out there – beating out their chests and wrapping warmly their seductive selves. So hangs the overcast skies. Bidding for the rain to plunder the earth with tears that ache.</p>
<p>I look up so the sky will replace my dried up ducts.</p>
 ]]>
                </content:encoded>
                <media:content height="720" medium="image" url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/who-no-hears.jpg" width="1280"/>
                    <media:thumbnail url="https://raw.githubusercontent.com/laudebugs/blog-posts/main/assets/who-no-hears.jpg"/>
                    
                    

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