<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Cole Gawin]]></title><description><![CDATA[Cole Gawin]]></description><link>https://blog.colegaw.in</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1622951682101/I2BD9njGr.png</url><title>Cole Gawin</title><link>https://blog.colegaw.in</link></image><generator>RSS for Node</generator><lastBuildDate>Sat, 10 Oct 2026 23:10:20 GMT</lastBuildDate><atom:link href="https://blog.colegaw.in/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Web-Scraping Techniques to Source Reliable Information for ChatGPT]]></title><description><![CDATA[Introduction
Since the release of OpenAI's ChatGPT, and later its REST API, the technology world has been enveloped by the vast capabilities of this paradigm-shifting technology and pushing it to its limits. However, for as many strengths as it has, ...]]></description><link>https://blog.colegaw.in/web-scraping-techniques-to-source-reliable-information-for-chatgpt</link><guid isPermaLink="true">https://blog.colegaw.in/web-scraping-techniques-to-source-reliable-information-for-chatgpt</guid><category><![CDATA[Python]]></category><category><![CDATA[chatgpt]]></category><category><![CDATA[Artificial Intelligence]]></category><category><![CDATA[web scraping]]></category><category><![CDATA[chatbot]]></category><dc:creator><![CDATA[Cole Gawin]]></dc:creator><pubDate>Wed, 05 Jul 2023 00:53:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1688518269361/f8fc8aac-94d3-4850-abdb-bb66ea0bf3b0.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>Since the release of OpenAI's ChatGPT, and later its REST API, the technology world has been enveloped by the vast capabilities of this paradigm-shifting technology and pushing it to its limits. However, for as many strengths as it has, ChatGPT has two pronounced weaknesses: 1) its training data cutoff is 2021, meaning it has no knowledge of information in more recent times, and 2) it is prone to hallucinations or making up information.</p>
<p>To circumvent this issue, software developers and prompt engineers alike have taken on the task of supplementing ChatGPT's knowledge base with additional information that can provide more relevant, reliable, and informed responses. This process involves a combination of technologies and prompt engineering that we'll discuss in depth in this series.</p>
<p>In this article, we'll investigate how we can write a program to scrape the internet for content that we can use to supplement an LLM like ChatGPT's knowledge beyond its training data. Specifically, we'll gather information from <a target="_blank" href="https://www.webmd.com/diet/default.htm">Nourish by WebMD</a> and process articles to create a databank of sources on nutrition that will be used to power a nutrition chatbot.</p>
<hr />
<p><em>This is part one of my series on creating a nutrition chatbot powered by data from WebMD using</em> <code>sentence-transformers</code>, FAISS, and ChatGPT.</p>
<ul>
<li><p><em>Part 1: Web-Scraping Techniques to Source Reliable Information for ChatGPT</em></p>
</li>
<li><p><em>Part 2: Leveraging Vector Embeddings and Similarity Search to Supplement ChatGPT's Training Data (coming soon)</em></p>
</li>
<li><p><em>Part 3: ChatGPT Prompt-Engineering Techniques for Providing Contextual Information (coming soon)</em></p>
</li>
</ul>
<hr />
<p><a target="_blank" href="https://www.webmd.com/diet/default.htm">Nourish by WebMD</a> provides a plethora of articles related to nutrition and eating well. Each article is conveniently broken up into multiple sub-headings, with the text underneath each heading providing additional relevant information to the main article topic. Since each sub-heading contains specific information on a sub-topic related to the main topic, instead of choosing the save the entire article as one "document" so to say, we'll want to break the article up by headings into multiple documents.</p>
<p>All of the articles are listed on WebMD's <a target="_blank" href="https://www.webmd.com/diet/medical-reference/default.htm">Health &amp; Diet Medical Reference</a>, which is paginated to 23 pages at the time of writing. Each page in this reference contains additional articles that we will need to scrape.</p>
<p>To perform the web scraping, we'll use the BeautifulSoup library, as well as the built-in <code>requests</code> library. Recall that we have to iterate through each paginated page in the Reference. We'll then use <code>html2text</code> to convert the plain HTML of each page into markdown content and split the page by markdown headings into multiple documents.</p>
<p>You can do this project in a Jupyter Notebook, or on a platform like Google Colab. Alternatively, you can create a new Python project in an IDE like Pycharm or VS Code. I recommend using a Jupyter Notebook for following along with this article.</p>
<p>The code in this article is also available for you to follow along (and run yourself) on <a target="_blank" href="https://colab.research.google.com/drive/1r5CPdxTOFeZVdHU8bmYvqQlB9B6tIH7l?usp=sharing">Google Colab</a>.</p>
<h1 id="heading-getting-started">Getting Started</h1>
<p>Let's first install the necessary dependencies for this project:</p>
<pre><code class="lang-plaintext">pip install bs4 html2text
</code></pre>
<p>Next, we'll define some custom request headers to spoof a web-browser client that we will use when making requests:</p>
<pre><code class="lang-python">headers = {
    <span class="hljs-string">'User-Agent'</span>: <span class="hljs-string">'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'</span>,
    <span class="hljs-string">'Accept-Language'</span>: <span class="hljs-string">'en-US,en;q=0.9'</span>,
}
</code></pre>
<h1 id="heading-crawling-through-the-reference">Crawling through the Reference</h1>
<p>Because each article we want to scrape is located within WebMD's <a target="_blank" href="https://www.webmd.com/diet/medical-reference/default.htm">Health &amp; Diet Medical Reference</a>, and the Reference itself is paginated, we need to first create a web-crawler program to identify each link we will need to scrape.</p>
<p>To do so, let's first make a <code>get_links_from_page</code> function that will retrieve all of the links to articles from a specific page within the Reference:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> requests
<span class="hljs-keyword">from</span> bs4 <span class="hljs-keyword">import</span> BeautifulSoup


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_links_from_page</span>(<span class="hljs-params">index</span>):</span>
  <span class="hljs-comment"># Send a GET request to the page we want to crawl</span>
  url = <span class="hljs-string">f"https://www.webmd.com/diet/medical-reference/default.htm?pg=<span class="hljs-subst">{index}</span>"</span>
  response = requests.get(url, headers=headers)

  <span class="hljs-comment"># Parse the HTML content using BeautifulSoup</span>
  soup = BeautifulSoup(response.content, <span class="hljs-string">'html.parser'</span>)

  <span class="hljs-comment"># Find the ul element with the links to articles</span>
  ul_element = soup.find(<span class="hljs-string">'section'</span>, class_=<span class="hljs-string">'dynamic-index-feed'</span>).find(<span class="hljs-string">'ul'</span>, class_=<span class="hljs-string">'list'</span>)

  <span class="hljs-comment"># Find all the anchors (a tags) within the ul element</span>
  anchors = ul_element.find_all(<span class="hljs-string">'a'</span>)

  <span class="hljs-comment"># Extract the href attribute from each link</span>
  links = [anchor[<span class="hljs-string">'href'</span>] <span class="hljs-keyword">for</span> anchor <span class="hljs-keyword">in</span> anchors]

  <span class="hljs-keyword">return</span> links
</code></pre>
<p>Next, we'll create a <code>get_all_links_from_reference</code> function that will iterate through all 23 pages in the Reference and fetch all of the links to articles.</p>
<pre><code class="lang-python">all_links = []

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_all_links_from_reference</span>():</span>
  <span class="hljs-keyword">for</span> index <span class="hljs-keyword">in</span> range(<span class="hljs-number">1</span>, <span class="hljs-number">24</span>):
    all_links.extend(get_links_from_page(index))
</code></pre>
<p>Once we run that, we now have all of the links in the WebMD Health &amp; Diet Reference, and we can now move on to scraping the individual articles for their content.</p>
<h1 id="heading-scraping-and-processing-articles">Scraping and Processing Articles</h1>
<p>As previously discussed, we will break up each article into multiple "documents" separated by headings within the article. Let's make a utility <code>Document</code> class to help us structure our code and keep track of the title and URL of the article, as well as the content of the document:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Document</span>:</span>
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, title, url, content</span>):</span>
    self.title = title
    self.url = url
    self.content = content
</code></pre>
<p>We'll also write a brief function that utilizes <code>requests</code> and BeautifulSoup to scrape a given article via its URL:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> requests
<span class="hljs-keyword">from</span> bs4 <span class="hljs-keyword">import</span> BeautifulSoup


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">scrape_webpage</span>(<span class="hljs-params">url</span>):</span>
  response = requests.get(url, headers=headers)
  soup = BeautifulSoup(response.content, <span class="hljs-string">'html.parser'</span>)
  <span class="hljs-keyword">return</span> soup
</code></pre>
<p>We now want to begin the work on processing the HTML into documents. First, let's write a function that converts the webpage into markdown text:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> html2text <span class="hljs-keyword">import</span> html2text


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">webpage_to_markdown</span>(<span class="hljs-params">soup</span>):</span>
  <span class="hljs-comment"># Find the article body, either by `class_='article__body'` or `class_='article-body'`</span>
  article_body = soup.find(class_=<span class="hljs-string">'article__body'</span>)
  <span class="hljs-keyword">if</span> article_body <span class="hljs-keyword">is</span> <span class="hljs-literal">None</span>:
    article_body = soup.find(class_=<span class="hljs-string">'article-body'</span>)

  <span class="hljs-comment"># Extract the HTML content from the article body</span>
  html_content = str(article_body)

  <span class="hljs-comment"># Convert the HTML content into markdown</span>
  markdown_content = html2texthtml2text(html_content, bodywidth=<span class="hljs-number">0</span>)
  <span class="hljs-keyword">return</span> markdown_content
</code></pre>
<p>For the sake of making the content more legible for ChatGPT, we'll make a function that strips links from the markdown content and replaces them with just the text of the link. We can do so using regular expressions, like so:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> re


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">strip_links_from_markdown</span>(<span class="hljs-params">markdown_content</span>):</span>
  <span class="hljs-comment"># Make a RegEx pattern</span>
  link_pattern = re.compile(<span class="hljs-string">r'\[(.*?)\]\((.*?)\)'</span>)

  <span class="hljs-comment"># Find all matches in markdown_content</span>
  matches = link_pattern.findall(markdown_content)

  <span class="hljs-keyword">for</span> _match <span class="hljs-keyword">in</span> matches:
    full_match = <span class="hljs-string">f'[<span class="hljs-subst">{_match[<span class="hljs-number">0</span>]}</span>](<span class="hljs-subst">{_match[<span class="hljs-number">1</span>]}</span>)'</span>

    <span class="hljs-comment"># Replace the full link with just the text</span>
    markdown_content = markdown_content.replace(full_match, _match[<span class="hljs-number">0</span>])

  <span class="hljs-keyword">return</span> markdown_content
</code></pre>
<p>Lastly, split the articles by markdown headings into multiple documents.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">split_markdown_by_headings</span>(<span class="hljs-params">markdown_content</span>):</span>
  <span class="hljs-comment"># Define the regular expression pattern to match H1 and H2 headings</span>
  pattern = <span class="hljs-string">r'(#{1,2}.*)'</span>

  <span class="hljs-comment"># Split the Markdown text based on the headings</span>
  sections = re.split(pattern, markdown_content)

  <span class="hljs-comment"># Combine each heading with its corresponding text</span>
  combined_sections = []
  <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-number">1</span>, len(sections), <span class="hljs-number">2</span>):
    heading = sections[i].strip()  <span class="hljs-comment"># Get the heading from sections[i]</span>
    text = sections[i + <span class="hljs-number">1</span>].strip() <span class="hljs-keyword">if</span> i + <span class="hljs-number">1</span> &lt; len(
        sections) <span class="hljs-keyword">else</span> <span class="hljs-string">''</span>  <span class="hljs-comment"># Get the text from sections[i + 1] if it exists, otherwise use an empty string</span>
    combined_section = <span class="hljs-string">f"<span class="hljs-subst">{heading}</span>\n<span class="hljs-subst">{text}</span>"</span>  <span class="hljs-comment"># Combine the heading and text using a newline character</span>
    combined_sections.append(combined_section)  <span class="hljs-comment"># Add the combined section to the list</span>

  <span class="hljs-keyword">if</span> len(combined_sections) == <span class="hljs-number">0</span>:
    combined_sections = [markdown_content]

  <span class="hljs-keyword">return</span> combined_sections
</code></pre>
<p>We now have all of the logic to both scrape and process articles from the Reference. We can put it together in a function, <code>create_documents_from_webpage</code>:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_documents_from_webpage</span>(<span class="hljs-params">url</span>):</span>
  <span class="hljs-comment"># Wrap function in a try-except block to handle any potential exceptions</span>
  <span class="hljs-keyword">try</span>:
    soup = scrape_webpage(url)

    <span class="hljs-comment"># Extract the title from the article</span>
    title = soup.find(<span class="hljs-string">'h1'</span>).get_text().strip()

    <span class="hljs-comment"># Convert the webpage HTML to markdown</span>
    markdown_content = webpage_to_markdown(soup)

    <span class="hljs-comment"># Strip links from the markdown content</span>
    markdown_content = strip_links_from_markdown(markdown_content)

    <span class="hljs-comment"># Split the article up headings</span>
    contents_by_heading = split_markdown_by_headings(markdown_content)
    docs = []
    <span class="hljs-keyword">for</span> content <span class="hljs-keyword">in</span> contents_by_heading:
      docs.append(Document(title=title, url=url, content=content))
    <span class="hljs-keyword">return</span> docs
  <span class="hljs-keyword">except</span>:
    <span class="hljs-keyword">return</span> []
</code></pre>
<p>With this function, we can provide any URL from the Reference, and it will generate a list of documents with content from each of the subheadings.</p>
<h1 id="heading-generate-databank-of-sources">Generate Databank of Sources</h1>
<p>Now that we have all of the logic required to scrape and process articles, we can move on to generating the databank of nutritional sources that will be utilized by the chatbot. To do so, we must iterate through each of the articles on of the pages in the Reference, and save all of the documents created.</p>
<p>We'll put together all of our previous work in the <code>generate_databank</code> function:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">generate_databank</span>():</span>
  docs = []

  <span class="hljs-comment"># Get all of the links in the Reference</span>
  all_links = get_all_links_from_reference()

  <span class="hljs-comment"># Iterate through every link in the Reference</span>
  <span class="hljs-keyword">for</span> link <span class="hljs-keyword">in</span> all_links:
    <span class="hljs-comment"># Add all of the documents created from the article</span>
    docs_from_article = create_documents_from_webpage(link)
    docs.extend(docs_from_article)

  <span class="hljs-keyword">return</span> docs
</code></pre>
<p>Feel free to add additional debug comments, such as which link is currently being processed, how many links are left, how many documents were added, etc.</p>
<p>After running <code>generate_databank</code>, we now have a databank of nutrition sources!</p>
<p><em>Keep in mind that this may be a memory-intensive process, and depending on your machine, you may want to consider running this program in a hosted environment like Google Colab. I was able to run this program on my 2023 M2 Pro MacBook Pro, but results may vary by your specific setup.</em></p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>Through following the steps in this article, we utilized real-world web-scraping techniques to gather and process information from the internet and create a databank of sources; this databank will be used to provide reliable information to our nutrition chatbot.</p>
<p>We used the BeautifulSoup library and the <code>requests</code> library to crawl through the WebMD Health &amp; Diet Medical Reference, and wrote functions to convert the webpage into markdown text, strip links from the markdown content, and split the articles by markdown headings. Finally, we generated the databank of nutritional sources by iterating through all of the articles on pages in the Reference and saving all of the documents created.</p>
<p>In the next article in this series, we'll explore using <code>sentence-transformers</code> and FAISS to create a vector store index to connect the databank we created to an LLM like ChatGPT.</p>
]]></content:encoded></item><item><title><![CDATA[Why the New Firebase Web v9 Modular SDK is a Game-Changer]]></title><description><![CDATA[This article, along with the rest of my blog, has been ported over to Instructive.dev, my new home for all of my programming articles. I invite you to check it out!

Firebase is one of the most popular Backend-as-a-Service options for a modern tech s...]]></description><link>https://blog.colegaw.in/why-the-new-firebase-web-v9-modular-sdk-is-a-game-changer</link><guid isPermaLink="true">https://blog.colegaw.in/why-the-new-firebase-web-v9-modular-sdk-is-a-game-changer</guid><category><![CDATA[Firebase]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[full stack]]></category><category><![CDATA[Frontend Development]]></category><dc:creator><![CDATA[Cole Gawin]]></dc:creator><pubDate>Sat, 07 Aug 2021 01:23:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1628304475972/uJLvnHfJs.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>This article, along with the rest of my blog, has been ported over to <a target="_blank" href="https://instructive.dev">Instructive.dev</a>, my new home for all of my programming articles. I invite you to check it out!</strong></p>
<hr />
<p>Firebase is one of the most popular Backend-as-a-Service options for a modern tech stack. In addition to offering a NoSQL database solution called Firestore, the Firebase platform provides solutions for authentication, file storage, hosting, and analytics. The Firebase SDK is available for many platforms, including mobile, Unity, Java, C++, and the web.</p>
<p>One of the major shortcomings of Firebase on the web, however, was its sheer size. According to <a target="_blank" href="https://bundlephobia.com/">BundlePhobia</a>, a tool used to determine the size of NPM packages, the <code>firebase</code> Web Javascript package <a target="_blank" href="https://bundlephobia.com/package/firebase@8.9.0">weighs in at 235.5kB when minified &amp; g-zipped</a>. This can result in an additional 0.59s of loading time for some users with slower network connections. For comparison, <code>lodash</code> is another notoriously heavy NPM package yet it only <a target="_blank" href="https://bundlephobia.com/package/lodash@4.17.21">weighs 24.5kB when minified &amp; g-zipped</a>: a 10th of the size of Firebase.</p>
<p><img src="https://res.craft.do/user/full/2a960b4f-1324-04c1-6008-a9adf8c53a2a/doc/BDE6EA65-F475-49E4-B3FA-668D9E3206AD/AF807877-706D-411E-B81C-C897E22C1C3D_2/Image.png" alt="image" /></p>
<p>This is a known issue with the Firebase Web Javascript SDK, and has turned many developers away from the product. Especially for developers building products for end-users who may not have access to a fast internet connection, loading a package as large as Firebase was simply not an option for them.</p>
<p>Thankfully, the Firebase team has been hard at work recreating the Firebase Web SDK from the group up. On July 27th, 2021, the official Firebase Blog account <a target="_blank" href="https://firebase.googleblog.com/2021/07/introducing-the-new-firebase-js-sdk.html">announced the pre-release of the a new, modular JavaScript SDK</a> that can be “up to 80% smaller!”</p>
<p><a target="_blank" href="https://twitter.com/Firebase/status/1420112970453897216"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1628284443278/rVvPvk-5PR.png" alt="Firebase Twitter announcement" /></a></p>
<p>Firebase Web v9 will completely change how web developers use Firebase. With the introduction of a fully overhauled, modular, functional programming style and the inclusion of a Firestore “lite” library, web apps powered by Firebase Web v9 will run faster, loader quicker, and dramatically enhance both the user and developer experience.</p>
<p>With all that said, let’s take a look at some of the radical changes introduced in this new modular Firebase Web SDK.</p>
<hr />
<h1 id="heading-side-effect-free-imports">Side-effect-free imports</h1>
<p>Previously, the Firebase Javascript SDK incorporated what is known as <em>side-effect imports</em>. In simplest terms, a side-effect occurs when a function modifies state outside of its provided scope. For example, if function <code>a</code> were to modify global variable <code>x</code>, then function <code>a</code> would produce a side-effect. Side-effect <em>imports</em> effect the state, logic, or functionality of your program without calling any methods or referencing any variables that are exported from the package. The mere presence of the package in your program (via an <code>import</code> statement or <code>require</code> call) can affect the functionality of your program.</p>
<p>The old Firebase Web SDK heavily relied on side-effect imports. For each additional Firebase functionality you wanted to include in your app (authentication, Firestore, cloud storage, analytics, etc.), you had to import an additional package like so:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// main firebase app import</span>
<span class="hljs-keyword">import</span> app <span class="hljs-keyword">from</span> <span class="hljs-string">"firebase/app"</span>;

<span class="hljs-comment">// SIDE EFFECT PACKAGES</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">"firebase/auth"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"firebase/firestore"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"firebase/storage"</span>;
</code></pre>
<p>If you have experience with working with the old Firebase Web SDK, you might have incorporated lazy-loading for importing the Firebase packages. This solution would decrease the initial load size and time of your web app, but users would still be forced to wait for all of these packages to load before the app became fully functional.</p>
<p>Firebase Web v9 changes all of this. The concept of side-effect packages is non-existent in the new Firebase Web SDK, and all of the packages are completely tree-shakable. That means that only the parts of Firebase that are needed by your app will be imported on the client. This drastically reduces the final bundle size of your app and will lead to much faster loading times!</p>
<h1 id="heading-native-javascript-es-modules">Native Javascript ES modules</h1>
<p>In the new Firebase Web SDK, each individual functionality of Firebase that your app requires is imported separately thanks to the introduction of modular packages. Because the new SDK is built into native Javascript ES modules, you can directly import only the features that your program needs: nothing more, nothing less. For example, let’s say you want to initialize your Firebase app and then watch for auth changes:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// imports with ES modules</span>
<span class="hljs-keyword">import</span> { initializeApp } <span class="hljs-keyword">from</span> <span class="hljs-string">"firebase/app"</span>;
<span class="hljs-keyword">import</span> { getAuth, onAuthStateChanged } <span class="hljs-keyword">from</span> <span class="hljs-string">"firebase/auth"</span>;

<span class="hljs-comment">// initialize firebase app</span>
initializeApp(firebaseConfig);

<span class="hljs-comment">// watch for auth changes</span>
<span class="hljs-keyword">const</span> auth = getAuth();
onAuthStateChanged(auth, <span class="hljs-function">(<span class="hljs-params">user</span>) =&gt;</span> {
 <span class="hljs-comment">// deal with authentication changes </span>
});
</code></pre>
<p>The introduction of modular packages in turn results in the introduction of a more functional programming style when working with the Firebase Web SDK.</p>
<h1 id="heading-functional-programming-style">Functional programming style</h1>
<p>If you have ever worked with functional programming languages or libraries, you will be familiar with the advantages that functional programming grants you as a developer. Programs that adhere to the functional programming style often have the advantages of being very intuitive and incredibly test-friendly. Although the old Firebase Web SDK was hardly difficult to comprehend, the new Firebase Web SDK is no less intuitive or beginner-friendly.</p>
<p>To demonstrate the functional programming style introduced by the new modular Firebase packages, let’s look at an example of updating a document in Firestore.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { getStorage, ref, uploadBytes } <span class="hljs-keyword">from</span> <span class="hljs-string">"firebase/storage"</span>;


<span class="hljs-comment">// first, get a reference to the storage bucket for our app</span>
<span class="hljs-keyword">const</span> storage = getStorage();

<span class="hljs-comment">// then, make a reference to the file</span>
<span class="hljs-keyword">const</span> usersCollection = ref(storage, <span class="hljs-string">"files/example.png"</span>);

<span class="hljs-comment">// finally, upload the file to the reference</span>
uploadBytes(usersCollection, file);
</code></pre>
<p>As you can see, there is a lot of function nesting present in this code example—the result of one function is passed as the argument to <em>another</em> function, whose result is passed to the argument of <em>another</em> function, and so on. This is in stark contrast to the method chaining approach used by the old Firebase Web SDK.</p>
<p>To summarize, the code used with the new Firebase SDK functional languages like F# or Scala (or functional libraries like Ramda and RxJS), whereas the code used with the old Firebase Web SDK resembles that of Java or C++.</p>
<h1 id="heading-firestore-lite">Firestore Lite</h1>
<p>Firestore is an incredibly powerful and useful database service. It provides a lot of features, many of which aren’t actually utilized in all web apps that use Firestore. Many developers simply use Firestore as an easy-to-implement NoSQL database that handles many of the complexities of operating a database on both the client- and server-side. To that extent, a lot of web apps don’t need the realtime updates capability of Firestore; they just need access to one-time document and collection queries.</p>
<p>The Firebase team recognizes this valid use case, and has addressed it with the introduction of a new library: Firestore Lite. The Firestore Lite library is up to 80% lighter than the old Firestore v8 library. All of the features of Firestore that you have grown to love and take full advantage of, minus realtime updates, are available in the Firestore Lite library. This is a big win for the Firebase Web community because your apps can now be more performant and less bloated with unused code!</p>
<h1 id="heading-compatibility">Compatibility</h1>
<p>The new Firebase Web v9 SDK makes it easy to progressively upgrade from the v8 SDK. The <code>firebase</code> package provides a <code>compat</code> library to make migrating from v8 to v9 easy and incremental. For all of the places in your codebase where you aren't ready to make the full switch to Firebase Web v9, you can take advantage of the <code>compat</code> library and incrementally upgrade parts of your codebase until you no longer need to use the <code>compat</code> library functionality.</p>
<p>The main drawback for this is that you won’t experience all of the bloat and load time reducing features of the new v9 SDK when using the <code>compat</code> library. The <code>compat</code> library still relies on side-effect imports, so you will have to deal with those like you would with the Firebase Web v8 SDK.</p>
<hr />
<h1 id="heading-conclusion">Conclusion</h1>
<p>If you have ever worked with Firebase on the web before, the future of Firebase should really excite you. The introduction of this new modular Firebase Web v9 SDK changes everything in terms of developing with Firebase on the web. From making your apps less bloated to improving the experiences of both the developer and the end-user, the new Firebase Web v9 modular SDK removes one of the biggest downsides to using Firebase, and will revolutionize the future of Firebase-powered web apps!</p>
]]></content:encoded></item><item><title><![CDATA[The Ultimate TypeScript Development Configuration]]></title><description><![CDATA[TypeScript is quickly becoming one of the most-used, most-loved, and most-popular languages.
However, setting up a productive workspace for TS development can be a hassle. This guide demystifies the one of the most frustrating yet most essential part...]]></description><link>https://blog.colegaw.in/the-ultimate-typescript-development-configuration</link><guid isPermaLink="true">https://blog.colegaw.in/the-ultimate-typescript-development-configuration</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[Programming Tips]]></category><dc:creator><![CDATA[Cole Gawin]]></dc:creator><pubDate>Wed, 31 Jul 2019 01:17:21 GMT</pubDate><content:encoded><![CDATA[<p><a target="_blank" href="https://www.typescriptlang.org/">TypeScript</a> is quickly becoming one of the most-used, most-loved, and <a target="_blank" href="https://redmonk.com/sogrady/2019/07/18/language-rankings-6-19/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=language-rankings-6-19">most-popular</a> languages.</p>
<p>However, setting up a productive workspace for TS development can be a hassle. This guide demystifies the one of the most frustrating yet most essential parts of modern programming with TypeScript.</p>
<p>All code used in this guide is available on <a target="_blank" href="https://gitlab.com/colegawin/ts-ultimate">GitLab</a>.</p>
<p>In this guide, I’ll be using <a target="_blank" href="https://yarnpkg.com/">Yarn</a>. It’s my package manager of choice due to its versatility, speed, and performance <em>compared to</em> <a target="_blank" href="https://www.npmjs.com/"><em>npm</em></a><em>.</em></p>
<p>I use <a target="_blank" href="https://code.visualstudio.com/">Visual Studio Code</a> as my IDE, so I will first set up VS Code for TypeScript development. Feel free to skip this section and continue with part 2.</p>
<hr />
<h1 id="heading-part-1-vs-code-setup">Part 1: VS Code Setup</h1>
<p>The are the extensions I use for TypeScript development in VS Code:</p>
<p>Let me know if you think I’m missing anything, or have any extensions to “add on” 😃.</p>
<p>Some personal choices that I use in VS Code:</p>
<ul>
<li><p><a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=monokai.theme-monokai-pro-vscode">Monokai Pro</a>: “Professional theme and matching icons, from the author of the original Monokai color scheme.” (The best color theme imo.)</p>
</li>
<li><p><a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=vscode-icons-team.vscode-icons">vscode-icons</a>: Icons for Visual Studio Code. (The best set of icons for VS Code imo.)</p>
</li>
<li><p><a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=natqe.reload">Reload</a>: Add a reload button to the status bar’s bottom right. (For when VS Code decides it’s time to get funky.)</p>
</li>
</ul>
<p>Add these lines to your VS Code <code>settings.json</code>:</p>
<pre><code class="lang-json"><span class="hljs-string">"eslint.autoFixOnSave"</span>:  <span class="hljs-literal">true</span>,
<span class="hljs-string">"eslint.validate"</span>:  [
    <span class="hljs-string">"javascript"</span>,
    {
        <span class="hljs-attr">"language"</span>: <span class="hljs-string">"typescript"</span>,
        <span class="hljs-attr">"autoFix"</span>: <span class="hljs-literal">true</span>
    },
],
<span class="hljs-string">"editor.formatOnSave"</span>: <span class="hljs-literal">true</span>,
<span class="hljs-string">"[javascript]"</span>: {
    <span class="hljs-attr">"editor.formatOnSave"</span>:  <span class="hljs-literal">false</span>,
},
<span class="hljs-string">"[typescript]"</span>: {
    <span class="hljs-attr">"editor.formatOnSave"</span>: <span class="hljs-literal">false</span>,
}
</code></pre>
<p>This enables <a target="_blank" href="https://eslint.org/">ESLint</a> auto-fixing and <a target="_blank" href="https://prettier.io/">Prettier</a> auto-formatting.</p>
<hr />
<h1 id="heading-part-2-project-configuration">Part 2: Project Configuration</h1>
<p>Let’s first create a directory. For this, we’ll call it <code>ts-ultimate</code>.</p>
<pre><code class="lang-bash">mkdir ts-ultimate
</code></pre>
<p>While we’re at it, run <code>npm init</code>.</p>
<p>Inside that directory, create a <code>src</code> folder. This is where your TS/JS files will go. Create an <code>index.ts</code> file inside as well.</p>
<h2 id="heading-install-typescript-and-ts-node-dev">Install TypeScript and ts-node-dev</h2>
<p>Next, we will install TypeScript (of course) and <a target="_blank" href="https://github.com/TypeStrong/ts-node">ts-node</a>.</p>
<pre><code class="lang-bash">yarn add typescript ts-node-dev —-dev
</code></pre>
<p><code>ts-node-dev</code> enables REPL for TypeScript, with auto-restarting, which enables us to see our TypeScript code work in real-time, sans compilation (think <a target="_blank" href="https://www.npmjs.com/package/nodemon">nodemon</a> or <a target="_blank" href="https://www.npmjs.com/package/node-dev">node-dev</a>, but for TypeScript).</p>
<p>Save these packages to <code>devDependencies</code> as they will only be required for development purposes.</p>
<h2 id="heading-eslint-extensions-and-plugins">ESLint, extensions, and plugins</h2>
<p>We’ll also need ESLint and a few extensions and plugins</p>
<pre><code class="lang-bash">yarn add eslint @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-config-airbnb eslint-config-prettier eslint-plugin-prettier prettier —-dev
</code></pre>
<p>Add these to a <code>.eslintrc.js</code> file, like so:</p>
<p><strong>barebones example, please take notice</strong></p>
<h2 id="heading-configure-prettier">Configure Prettier</h2>
<p>This is how I personally configure Prettier:</p>
<pre><code class="lang-tsx">module.exports = {
  semi: true,
  trailingComma: 'all',
  singleQuote: true,
  printWidth: 120,
  tabWidth: 2,
};
</code></pre>
<p>Don’t try to be fancy. Please, just use semicolons.</p>
<p>Now, go ahead and open up your original <code>package.json</code>.</p>
<p>Add a <code>script</code> called <code>lint</code>. We will use this script to lint our TS/JS files.</p>
<pre><code class="lang-json"><span class="hljs-string">"lint"</span>: <span class="hljs-string">"eslint --fix ./src/*"</span>
</code></pre>
<p>This tells the ESLint CLI to lint all files located in the <code>src</code> directory and fix any problems it is able to. Note that <em>most</em>problems you will have to fix yourself.</p>
<p>Run <code>yarn lint</code> in your <code>ts-ultimate</code> directory and no errors should be found!</p>
<p>However, if you have no files in the <code>src</code> directory, you may get an error that says: “No files matching the pattern”. That is expected behavior, so you can ignore this error.</p>
<p>If you are using VS Code, this is automatically integrated into the ESLint and Prettier plugins, so most errors will show up in your workspace while editing.</p>
<hr />
<h1 id="heading-part-3-live-updates-without-compiling">Part 3: Live Updates Without Compiling</h1>
<p>To accomplish this not-so-daunting task, we will use ts-node-dev.</p>
<p>According to its npm README, ts-node-dev “restarts the target node process when any of the required files change, but shares the TypeScript compilation process between restarts.”</p>
<p>Basically, this means that we will be able to auto-reload our TypeScript file without waiting for <code>tsc</code> to compile it.</p>
<p>Let’s create a dev script that uses nodemon:</p>
<pre><code class="lang-json"><span class="hljs-string">"dev"</span>: <span class="hljs-string">"ts-node-dev --respawn --transpileOnly src/index.ts"</span>
</code></pre>
<p>…and a <code>dev:debug</code> script:</p>
<pre><code class="lang-json"><span class="hljs-string">"dev:debug"</span>: <span class="hljs-string">"ts-node-dev --inspect=4321 --respawn --transpileOnly src/index.ts"</span>
</code></pre>
<p>The <code>inspect</code> flag informs ts-node-dev to attach a <em>debugger</em> to the process at port <code>4321</code> (this number doesn’t matter) to which VS Code will listen.</p>
<hr />
<h1 id="heading-part-4-visual-studio-code-debugging">Part 4: Visual Studio Code Debugging</h1>
<p>Using VS Code, we can add breakpoints to our TypeScript code to inspect and debug our code at these points.</p>
<p>Go to your <code>ts-ultimate</code> directory and create a file called <code>launch.json</code> inside.</p>
<pre><code class="lang-bash">mkdir .vscode
<span class="hljs-built_in">cd</span> .vscode
touch launch.json
</code></pre>
<p>Paste the following into your <code>launch.json</code>:</p>
<p>This creates a debug config that will attach itself to the <code>dev:debug</code> script when running.</p>
<p>To do so, first run the <code>dev:debug</code> script.</p>
<pre><code class="lang-bash">yarn dev:debug
</code></pre>
<p>Once you have this script running, open the debug tab, and, in the top navbar, click the play button next to <em>Node: Nodemon</em>.</p>
<p>VS Code should now be able to debug your script when you add breakpoints.</p>
<hr />
<h1 id="heading-conclusion">Conclusion</h1>
<p>Congrats on making it this far! You now have a super-powerful TypeScript development environment with ESLint with Airbnb style guide and Prettier integration, live reloading, and on-the-fly VS Code debugging.</p>
]]></content:encoded></item></channel></rss>