<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Ted Ngeene</title>
    <description>The latest articles on Forem by Ted Ngeene (@tngeene).</description>
    <link>https://forem.com/tngeene</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F208322%2F1df4df3d-c506-44de-8342-fe1fcc0a5ada.jpg</url>
      <title>Forem: Ted Ngeene</title>
      <link>https://forem.com/tngeene</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/tngeene"/>
    <language>en</language>
    <item>
      <title>How to bypass captcha with 2captcha and Selenium using Python</title>
      <dc:creator>Ted Ngeene</dc:creator>
      <pubDate>Fri, 28 Jan 2022 20:02:54 +0000</pubDate>
      <link>https://forem.com/tngeene/how-to-bypass-captcha-with-2captcha-and-selenium-using-python-1p9c</link>
      <guid>https://forem.com/tngeene/how-to-bypass-captcha-with-2captcha-and-selenium-using-python-1p9c</guid>
      <description>&lt;p&gt;When you go to any modern website, they ask you to solve a challenge before submitting a form, such as identifying all firehoses, a slider to solve a puzzle or typing out some texts. This is called CAPTCHA(short for Completely Automated Public Turing tests to tell Computers and Humans Apart). They usually add a layer of security to prevent bots and malicious accounts from accessing a website's resources.&lt;/p&gt;

&lt;p&gt;The squiggly lines and words of the past are less common nowadays and have been replaced by Google's version 2 of CAPTCHA, known as reCAPTCHA or key puzzles from keyCAPTCHA. However, bots are becoming more advanced and can bypass almost any captcha. In this tutorial, we'll make use of the captcha bypass software, &lt;a href="https://2captcha.com/?from=12437369"&gt;2captcha&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uftA2SQq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0cxjwqgogblodoijg1fv.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uftA2SQq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0cxjwqgogblodoijg1fv.jpeg" alt="cover image tngeene captcha" width="260" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why are we doing this?
&lt;/h2&gt;

&lt;p&gt;Okay, I know some of you might be wondering, why would we need to bypass captcha? and most importantly; is this even legal? No, it is not illegal to bypass 2captcha.&lt;br&gt;
This article is intended for;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Curious about how you can bypass captcha&lt;/li&gt;
&lt;li&gt;Building web scrapers that would need to bypass a captcha&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;*Disclosure: I only recommend products I would use myself and all opinions expressed here are my own. This post may contain affiliate links that at no additional cost to you, I may earn a small commission.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  What is 2captcha?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://2captcha.com/?from=12437369"&gt;2Captcha.com&lt;/a&gt; is a captcha solving service that automates the captcha solving process. They have an API and several packages that wrap around different programming languages. All you need is to register on their website, get an API key and make requests to their API.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: They charge a small rate for every captcha solved, however, the fee is quite minimal at $0.05 to $ 1.00 for every 1000 captchas solved.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Signup Process and supported languages
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yS5aGT0Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--JgfXuOWJ--/c_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vdobzdwc4thex7mwocho.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yS5aGT0Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--JgfXuOWJ--/c_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vdobzdwc4thex7mwocho.png" alt="https://res.cloudinary.com/practicaldev/image/fetch/s--JgfXuOWJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vdobzdwc4thex7mwocho.png" width="880" height="393"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_dyaDAZk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--OEXnGqQA--/c_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/szwihqwt8xxrtpnz64fn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_dyaDAZk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--OEXnGqQA--/c_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/szwihqwt8xxrtpnz64fn.png" alt="https://res.cloudinary.com/practicaldev/image/fetch/s--OEXnGqQA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/szwihqwt8xxrtpnz64fn.png" width="880" height="354"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Supported captchas the software solves include;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6BNKXNhn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--px3AuiEX--/c_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/11ybxotkegschanhgq9i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6BNKXNhn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--px3AuiEX--/c_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/11ybxotkegschanhgq9i.png" alt="https://res.cloudinary.com/practicaldev/image/fetch/s--px3AuiEX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/11ybxotkegschanhgq9i.png" width="880" height="201"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Requirements and Setup
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;For this tutorial, we'll utilize the &lt;a href="https://2captcha.com/2captcha-api"&gt;2captcha API&lt;/a&gt;. You'll need to have a developer account to use. You can head over to &lt;a href="https://2captcha.com/?from=12437369"&gt;this link and sign up&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The source code we'll be using can be found &lt;a href="https://github.com/tngeene/2-captcha-solver-python"&gt;here&lt;/a&gt;. Setup instructions have been linked in the project README file. Feel free to clone the repository and tinker around.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's dive in.&lt;/p&gt;
&lt;h2&gt;
  
  
  Automating with Selenium
&lt;/h2&gt;

&lt;p&gt;Before we get carried away by CAPTCHA, we need to understand the process our program will follow. I'm going to use &lt;a href="https://www.selenium.dev/documentation/"&gt;selenium&lt;/a&gt; and python. Selenium is a browser automation service that provides extensions that allow programs to emulate browser interactions. It supports an array of browsers; from chrome, firefox, safari, and so forth, by integrating with their web drivers.&lt;/p&gt;
&lt;h2&gt;
  
  
  Captcha Solving Pseudocode
&lt;/h2&gt;

&lt;p&gt;Now that we have our account setup, we'll do the actual capture bypassing, which brings us to the next part.&lt;/p&gt;

&lt;p&gt;For this tutorial, i'll be bypassing the capture on &lt;a href="https://phppot.com/demo/php-contact-form-with-google-recaptcha/"&gt;this sample form&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kn9Pn1PP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--F4Bz13C_--/c_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qkgdk3qldwr178k6rtb0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kn9Pn1PP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--F4Bz13C_--/c_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qkgdk3qldwr178k6rtb0.png" alt="https://res.cloudinary.com/practicaldev/image/fetch/s--F4Bz13C_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qkgdk3qldwr178k6rtb0.png" width="880" height="950"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In order to achieve our objective, we'll need our program to follow these steps.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Launch a browser tab.&lt;/li&gt;
&lt;li&gt;Fill in the required fields in the form.&lt;/li&gt;
&lt;li&gt;Solve the captcha.&lt;/li&gt;
&lt;li&gt;Send the contact message with the solved captcha.&lt;/li&gt;
&lt;li&gt;Close the browser window.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All this will be achieved automatically!! How cool is that?&lt;/p&gt;

&lt;p&gt;For this tutorial, I'll be using chrome driver since chrome is my default browser. To setup selenium, chromedriver, and all project dependencies run the following command&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install -r requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Always ensure you've activated a virtual environment to prevent the packages from being installed globally in your machine.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are several packages that you'll see in the &lt;code&gt;main.py&lt;/code&gt; file. I'll go over them in brief&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://pypi.org/project/2captcha-python/"&gt;2captcha-python&lt;/a&gt; - the 2captcha python SDK. It does most of the heavy lifting of interacting with the 2captcha API&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://selenium-python.readthedocs.io/"&gt;Selenium with python&lt;/a&gt; - contains selenium python bindings.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pypi.org/project/webdriver-manager/"&gt;webdriver-manager&lt;/a&gt; - manages our chromedriver installation rather than manually setting path configurations. It's main idea is to simplify management of binary drivers for different browsers.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pypi.org/project/python-decouple/"&gt;python-decouple&lt;/a&gt; - enables storing of variables in &lt;code&gt;.env&lt;/code&gt; or &lt;code&gt;.ini&lt;/code&gt; files. This comes in handy when we have sensitive information we might not want exposed to the unauthorized parties, such as API keys.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Project Code
&lt;/h2&gt;

&lt;p&gt;Open an &lt;code&gt;main.py&lt;/code&gt; file at the root of your project directory and paste the following code. We'll go over the code in-depth in the next section.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;random&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;randint&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sleep&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;decouple&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;selenium&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;selenium.common.exceptions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TimeoutException&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;selenium.webdriver.chrome.service&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Service&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;selenium.webdriver.common.by&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;By&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;twocaptcha&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TwoCaptcha&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;twocaptcha.api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ApiException&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NetworkException&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;twocaptcha.solver&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ValidationException&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;webdriver_manager.chrome&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChromeDriverManager&lt;/span&gt;

&lt;span class="n"&gt;two_captcha_api_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'CAPTCHA_API_KEY'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;website_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'WEBSITE_URL'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;site_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'GOOGLE_CAPTCHA_KEY'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;driver_options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChromeOptions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;driver_options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'--start_maximized'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# * use 2captcha python client, read docs in https://pypi.org/project/2captcha-python/
&lt;/span&gt;&lt;span class="n"&gt;solver_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;'apiKey'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;two_captcha_api_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;'defaultTimeout'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;'recaptchaTimeout'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;'pollingInterval'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;solver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TwoCaptcha&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;solver_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SolveCaptcha&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;launch_selenium&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Captcha Solved! Launching Browser...'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="c1"&gt;# initiate chrome webdriver
&lt;/span&gt;            &lt;span class="c1"&gt;# * check webdrivers for firefox and others in https://selenium-python.readthedocs.io/installation.html#drivers
&lt;/span&gt;            &lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chrome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;ChromeDriverManager&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;install&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
                                      &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;driver_options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="c1"&gt;# open browser window and navigate to Url
&lt;/span&gt;            &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;website_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;By&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;send_keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Ted'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;By&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'phone'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;send_keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'000000000'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;By&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="s"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;send_keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'tngeene@captcha.com'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;By&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="s"&gt;'comment-content'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;send_keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'test comment'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="n"&gt;google_captcha_response_input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;By&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'g-recaptcha-response'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="c1"&gt;# make input visible
&lt;/span&gt;            &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute_script&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s"&gt;"arguments[0].setAttribute('style','type: text; visibility:visible;');"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;google_captcha_response_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="c1"&gt;# input the code received from 2captcha API
&lt;/span&gt;            &lt;span class="n"&gt;google_captcha_response_input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'code'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="c1"&gt;# hide the captch input
&lt;/span&gt;            &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute_script&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s"&gt;"arguments[0].setAttribute('style', 'display:none;');"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;google_captcha_response_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="c1"&gt;# send text
&lt;/span&gt;            &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;By&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'send-message'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

            &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

            &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="c1"&gt;# show 2captcha Balance.
&lt;/span&gt;            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;'Your 2captcha balance is $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'No response.'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initiate_captcha_solver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Solving captcha...'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recaptcha&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sitekey&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;site_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;website_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="c1"&gt;# launch browser window upon successful
&lt;/span&gt;            &lt;span class="c1"&gt;# completion of captcha solving.
&lt;/span&gt;            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;launch_selenium&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;ValidationException&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# invalid parameters passed
&lt;/span&gt;            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;NetworkException&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# network error occurred
&lt;/span&gt;            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;ApiException&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# api respond with error
&lt;/span&gt;            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;TimeoutException&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# captcha is not solved so far
&lt;/span&gt;            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;


&lt;span class="n"&gt;SolveCaptcha&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;initiate_captcha_solver&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Importing Packages
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;random&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;randint&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sleep&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;decouple&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;selenium&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;selenium.common.exceptions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TimeoutException&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;selenium.webdriver.chrome.service&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Service&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;selenium.webdriver.common.by&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;By&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;twocaptcha&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TwoCaptcha&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;twocaptcha.api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ApiException&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NetworkException&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;twocaptcha.solver&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ValidationException&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;webdriver_manager.chrome&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChromeDriverManager&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;These lines are essentially referencing the packages we'll be using. We're telling the program that we'll require to use; decouple, the twocaptcha, and selenium packages.&lt;br&gt;
I've also imported the &lt;code&gt;randint&lt;/code&gt; and &lt;code&gt;sleep&lt;/code&gt; modules that come as part of the standard python package.&lt;br&gt;
We'll be using these two as well.&lt;/p&gt;
&lt;h3&gt;
  
  
  Interacting with imported packages
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;two_captcha_api_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'CAPTCHA_API_KEY'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;website_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'WEBSITE_URL'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;site_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'GOOGLE_CAPTCHA_KEY'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The first package we need to use is the &lt;code&gt;python-decouple&lt;/code&gt; package. Here, we define the environment variables we'll be using for the program. You can obtain the 2captcha api key from the website's dashboard.&lt;/p&gt;
&lt;h3&gt;
  
  
  Environment variable.
&lt;/h3&gt;


&lt;div class="ltag__wikipedia--container"&gt;
  &lt;div class="ltag__wikipedia--header"&gt;
    &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Sew3uq9H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/wikipedia-logo-0a3e76624c7b1c3ccdeb9493ea4add6ef5bd82d7e88d102d5ddfd7c981efa2e7.svg" class="ltag__wikipedia--logo" alt="Wikipedia Logo" width="128" height="128"&gt;
    &lt;a href="https://en.wikipedia.org/wiki/Environment_variable" rel="noopener noreferrer"&gt;Environment variable&lt;/a&gt;
  &lt;/div&gt;
  &lt;div class="ltag__wikipedia--extract"&gt;&lt;p&gt;An &lt;b&gt;environment variable&lt;/b&gt; is a dynamic-named value that can affect the way running processes will behave on a computer. They are part of the environment in which a process runs. For example, a running process can query the value of the TEMP environment variable to discover a suitable location to store temporary files, or the HOME or USERPROFILE variable to find the directory structure owned by the user running the process.&lt;/p&gt;&lt;/div&gt;
  &lt;div class="ltag__wikipedia--btn--container"&gt;
    
      &lt;a href="https://en.wikipedia.org/wiki/Environment_variable" rel="noopener noreferrer"&gt;View on Wikipedia&lt;/a&gt;
    
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;It's advisable to use environment variables to store sensitive information that we wouldn't want getting into unauthorized hands, for example, our 2Captcha API key. Create a &lt;code&gt;env&lt;/code&gt; file at the root of your project folder. Modify it to this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; CAPTCHA_API_KEY=&amp;lt;api_key_from_2_captcha.com&amp;gt;
 WEBSITE_URL=&amp;lt;website_we'll_be_accessing_captcha&amp;gt;
 GOOGLE_CAPTCHA_KEY=&amp;lt;google_captcha_site_key&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste the api key value from &lt;a href="https://2captcha.com/?from=12437369"&gt;2captcha&lt;/a&gt; dashboard on the &lt;code&gt;CAPTCHA_API_KEY&lt;/code&gt; field.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;GOOGLE_CAPTCHA_KEY&lt;/code&gt; is a special identifier found in all web forms having captcha, we can retrieve it by also opening dev tools, and searching the &lt;code&gt;data-sitekey&lt;/code&gt; keyword.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--r3mHI9Id--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--EWcZ2FeR--/c_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lp1rxds2suvwbtgrktdn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--r3mHI9Id--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--EWcZ2FeR--/c_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lp1rxds2suvwbtgrktdn.png" alt="https://res.cloudinary.com/practicaldev/image/fetch/s--EWcZ2FeR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lp1rxds2suvwbtgrktdn.png" width="880" height="611"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Retrieve the value and paste it in the &lt;code&gt;.env&lt;/code&gt;, GOOGLE_CAPTCHA_KEY value. &lt;code&gt;python-2captcha&lt;/code&gt; sends this key under the hood to the 2captcha API, which then returns an API response of the solved captcha. We'll be seeing this further ahead.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;WEBSITE_URL&lt;/code&gt; is the webpage of our captcha form. Fill it in the &lt;code&gt;.env&lt;/code&gt; file as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;solver_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;'apiKey'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;two_captcha_api_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;'defaultTimeout'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;'recaptchaTimeout'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;'pollingInterval'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;solver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TwoCaptcha&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;solver_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next we initialize the twocaptcha instance using the TwoCaptcha constructor. The constructor takes in a few arguments which we have defined inside the &lt;code&gt;solver_config&lt;/code&gt; dictionary.&lt;br&gt;
We then pass the configs into the constructor by desctructuring the dictionary.&lt;/p&gt;

&lt;p&gt;These arguments are;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;apikey&lt;/code&gt;: The API key we obtain from 2Captcha.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;defaultTimeout&lt;/code&gt;: Time (seconds) to wait before giving up on waiting for a captcha solution.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;recaptchaTimeout&lt;/code&gt;: Polling timeout for ReCaptcha in seconds. Defines how long the module tries to get the answer from &lt;code&gt;res.php&lt;/code&gt; API endpoint&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pollingInterval&lt;/code&gt;: Time (seconds) between polls to 2captcha server. 2Captcha documentation suggests this time to be at least 5 seconds, or you might get blocked.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Retrieving Dom elements
&lt;/h2&gt;

&lt;p&gt;As you can tell from the form, we will need selenium to access the DOM form elements as it automatically fills the inputs. These are; the name, email, phone, comment, and finally there's a hidden google captcha field that takes the solved captcha as an input.&lt;/p&gt;

&lt;p&gt;For the visible form fields, all we need to do is open up the dev tools and retrieve the individual ids of the form fields.&lt;/p&gt;

&lt;p&gt;This section does just that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;launch_selenium&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Captcha Solved! Launching Browser...'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="c1"&gt;# initiate chrome webdriver
&lt;/span&gt;            &lt;span class="c1"&gt;# * check webdrivers for firefox and others in https://selenium-python.readthedocs.io/installation.html#drivers
&lt;/span&gt;            &lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chrome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;ChromeDriverManager&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;install&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
                                      &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;driver_options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="c1"&gt;# open browser window and navigate to Url
&lt;/span&gt;            &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;website_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;By&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;send_keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Ted'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;By&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'phone'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;send_keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'000000000'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;By&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="s"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;send_keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'tngeene@captcha.com'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;By&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="s"&gt;'comment-content'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;send_keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'test comment'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What we're telling selenium is, to launch chrome browser, and visit the specified url. Once it does so, find the DOM elements that matches the provided ids. Since these are form inputs, auto-populate those field with the data inside the &lt;code&gt;send_keys&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;All 2captchas have a hidden text-area that autofills with the solved captcha code once you click the &lt;code&gt;i'm not a robot&lt;/code&gt; check. Usually, this has the id set as &lt;code&gt;g-recaptcha-response&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--N1YSAwgW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--DRV3vb-q--/c_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6v3jdf99wtv974jfhffy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--N1YSAwgW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--DRV3vb-q--/c_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6v3jdf99wtv974jfhffy.png" alt="https://res.cloudinary.com/practicaldev/image/fetch/s--DRV3vb-q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6v3jdf99wtv974jfhffy.png" width="880" height="333"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since selenium simulates browser human inputs, it'll be required to make the field visible. We achieve this with this code snippet&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;  &lt;span class="n"&gt;google_captcha_response_input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;By&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'g-recaptcha-response'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="c1"&gt;# make input visible
&lt;/span&gt;            &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute_script&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s"&gt;"arguments[0].setAttribute('style','type: text; visibility:visible;');"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;google_captcha_response_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="c1"&gt;# input the code received from 2captcha API
&lt;/span&gt;            &lt;span class="n"&gt;google_captcha_response_input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'code'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="c1"&gt;# hide the captcha input
&lt;/span&gt;            &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute_script&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s"&gt;"arguments[0].setAttribute('style', 'display:none;');"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;google_captcha_response_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="c1"&gt;# send text
&lt;/span&gt;            &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;By&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'send-message'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This section typically makes the field visible, auto-populates the field with the solved captcha, hides the field again, and finally, the button click is simulated to send the comment with the solved captcha.&lt;/p&gt;

&lt;p&gt;Finally, we'll close the browser tab a few seconds after the captcha has been solved. We're using the &lt;code&gt;randint&lt;/code&gt; function to close the browser anytime between 5 and 10 seconds&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;            &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

            &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="c1"&gt;# show 2captcha Balance.
&lt;/span&gt;            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;'Your 2captcha balance is $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last section queries your 2captcha balance and returns the account balance of your account so as to give you a small report on the cash balance. As mentioned before, each captcha solved attracts a small fee.&lt;/p&gt;

&lt;p&gt;All the fore-mentioned functionality resides in the &lt;code&gt;launch_selenium()&lt;/code&gt; function. We need to tie it all together with the 2captcha service.&lt;/p&gt;

&lt;p&gt;From the &lt;code&gt;main.py&lt;/code&gt; file, you can see we have a &lt;code&gt;initiate_captcha_solver()&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initiate_captcha_solver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Solving captcha...'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recaptcha&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sitekey&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;site_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;website_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="c1"&gt;# launch browser window upon successful
&lt;/span&gt;            &lt;span class="c1"&gt;# completion of captcha solving.
&lt;/span&gt;            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;launch_selenium&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;ValidationException&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# invalid parameters passed
&lt;/span&gt;            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;NetworkException&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# network error occurred
&lt;/span&gt;            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;ApiException&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# api respond with error
&lt;/span&gt;            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;TimeoutException&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# captcha is not solved so far
&lt;/span&gt;            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are calling the &lt;code&gt;python_two_captcha&lt;/code&gt; sdk we'd initialized before, by&lt;br&gt;
   result = solver.recaptcha(sitekey=site_key, &lt;br&gt;
          url=website_url)&lt;/p&gt;

&lt;p&gt;We're also catching some exceptions that may be returned by the 2Captcha API.&lt;/p&gt;

&lt;p&gt;This result will be used as an argument in the &lt;code&gt;launch_selenium&lt;/code&gt; function as we'll need the output of the response as an input to our form.&lt;/p&gt;

&lt;p&gt;Selenium will only be launched upon a successful captcha solve, which usually takes a few seconds. For Recaptcha version 2, the ETA is usually anytime from 15 seconds to 45seconds. Recaptcha version 3 takes a shorter time.If a request timed out, we log the api response.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Okay, now your application is set up! It may feel like a lot 😅 but we did a lot of installation. We will now test our application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9cC7K-iw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://i.imgur.com/Ma9Llnc.gif%255B/img%255D" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9cC7K-iw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://i.imgur.com/Ma9Llnc.gif%255B/img%255D" alt="tngeene python 2captcha demo gif" width="600" height="341"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;It’s always a dilemma, should websites have a better experience and have simple to bypass the CAPTCHA or should websites aggressively protect themselves from bots and have a bad user experience. The war between websites and bots is never over. Whatever verification method websites pull out, it’s just a matter of time when someone figures out how to bypass it. ~ Filip Vitas&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this guide, we were introduced to 2captcha API, selenium, and a few concepts in 2captcha using python. By the end of it, I hope you can apply the knowledge gained to build your own captcha bypass service. I mean, if bots can do it, then so should we! A few next steps would be to add a User interface to input our values. You can also look into using the 2captcha API using your preferred programming language and other tools such as &lt;a href="https://pptr.dev/"&gt;puppeteer&lt;/a&gt;, i've also previously written an article on the same using javascript. You can find this by visiting this &lt;a href="https://dev.to/tngeene/how-to-bypass-captcha-with-2captcha-api-and-selenium-4p5m"&gt;link&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, if you liked the content and would like to use 2captcha, &lt;a href="https://2captcha.com/?from=12437369"&gt;sign up with this link.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you have any questions, you can always leave a comment below, or reach out on these channels;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://tngeene.com/"&gt;personal website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/Ngeene_kihiu"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Source code of demo project can be accessed &lt;a href="https://github.com/tngeene/2-captcha-solver-python"&gt;here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use 2captcha responsibly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sponsors
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.scraperapi.com?via=teddy44"&gt;Scraper API&lt;/a&gt; is a startup specializing in strategies that'll ease the worry of your IP address from being blocked while web scraping. They utilize IP rotation so you can avoid detection. Boasting over 20 million IP addresses and unlimited bandwidth. Using Scraper API and a tool like &lt;a href="https://2captcha.com?from=12437369"&gt;2captcha&lt;/a&gt; will give you an edge over other developers. The two can be used together to automate processes. Sign up on Scraper API and use &lt;a href="https://www.scraperapi.com?via=teddy44"&gt;this link&lt;/a&gt; to get a 10% discount on your first purchase.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Do you need a place to host your website or app, &lt;a href="//DigitalOcean%20%E2%80%93%20The%20developer%20cloudwww.digitalocean.com"&gt;Digital ocean&lt;/a&gt;&lt;br&gt;
is just the solution you need, sign up on digital ocean using this &lt;a href="https://m.do.co/c/eaa803fe4d99"&gt;link&lt;/a&gt; and experience the best cloud service provider.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The journey to becoming a developer can be long and tormentous, luckily &lt;a href="http://referral.pluralsight.com/mQh0Nxp"&gt;Pluralsight&lt;/a&gt; makes it easier to learn. They offer a wide range of courses, with top quality trainers, whom I can personally vouch for. Sign up using &lt;a href="http://referral.pluralsight.com/mQh0Nxp"&gt;this link&lt;/a&gt; and get a 50% discount on your first course.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>automation</category>
      <category>selenium</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Automating Django Deployment workflow with Github Actions</title>
      <dc:creator>Ted Ngeene</dc:creator>
      <pubDate>Thu, 13 Jan 2022 11:53:08 +0000</pubDate>
      <link>https://forem.com/tngeene/automating-django-deployment-workflow-with-github-actions-2i1o</link>
      <guid>https://forem.com/tngeene/automating-django-deployment-workflow-with-github-actions-2i1o</guid>
      <description>&lt;p&gt;If you’ve worked on a project that has constant changes, tests, and needs to be deployed to a server every time the changes occur, then you’re aware how the process of logging in to the server via ssh, redeploying, and testing is quite repetitive and frankly annoying sometimes. Luckily, with the advent of Github actions, this can be automated, leading to a happy developer.&lt;/p&gt;

&lt;p&gt;The purpose of this article is to get you introduced to the concept of Github actions for a typical Django app deployment, but it can easily apply to any other framework. So...let’s see some Github actions in action.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction to Github actions
&lt;/h2&gt;

&lt;p&gt;According to the official docs, the definition of GitHub actions is;&lt;/p&gt;

&lt;p&gt;GitHub Actions is a continuous integration and continuous delivery (CI/CD) platform that allows you to automate your build, test, and deployment pipeline. You can create workflows that build and test every pull request to your repository or deploy merged pull requests to production.&lt;/p&gt;

&lt;p&gt;GitHub Actions goes beyond just DevOps and lets you run workflows when other events happen in your repository.&lt;/p&gt;

&lt;p&gt;GitHub provides Linux, Windows, and macOS virtual machines to run your workflows, or you can host your own self-hosted runners in your own data center or cloud infrastructure.&lt;/p&gt;

&lt;p&gt;What this basically means is that they provide a &lt;a href="https://www.redhat.com/en/topics/devops/what-is-ci-cd" rel="noopener noreferrer"&gt;Continuous Integration and Continuous Delivery pipeline&lt;/a&gt;, all in one platform. This is a powerful tool for dev teams and Individual Contributors and is usually a key step in the software development life cycle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Terms and Concepts In Github Actions
&lt;/h2&gt;

&lt;p&gt;Before we begin, let’s familiarize ourselves with a few terms and concepts that you’ll be seeing along with the post.&lt;/p&gt;

&lt;h3&gt;
  
  
  Workflow
&lt;/h3&gt;

&lt;p&gt;A &lt;em&gt;workflow&lt;/em&gt; is a configurable automated process that will run one or more jobs. Workflows are defined by a YAML file in your repository and will run when triggered by an &lt;code&gt;event&lt;/code&gt; in your repository, or they can be triggered manually, or at a defined schedule.&lt;/p&gt;

&lt;p&gt;Your repository can have multiple workflows each of which can perform a different set of steps. For example, you can have one workflow to build and test pull requests, another workflow to deploy your application every time a release is created, and still another workflow that adds a label every time someone opens a new issue.&lt;/p&gt;

&lt;p&gt;Workflows are contained in the &lt;code&gt;.github/workflows&lt;/code&gt; directory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Events
&lt;/h3&gt;

&lt;p&gt;An event is a specific activity in a repository that triggers a workflow run. For example, an event can be a pull request, push, comment, and so on. For more explanation on this, see &lt;a href="https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows" rel="noopener noreferrer"&gt;events that trigger workflows&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Jobs
&lt;/h3&gt;

&lt;p&gt;A &lt;em&gt;job&lt;/em&gt; is a set of steps that execute on your workflow in the same &lt;code&gt;runner&lt;/code&gt;. An example of a job would be running unit tests. A step can either be a shell script that will be executed or an action to be run. Steps are executed in order and are dependent on each other. Since each step is executed on the same runner, you can share data from one step to another. For example, you can have a step that builds your application followed by a step that tests the application that was built. which is what we’re going to be doing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Actions
&lt;/h3&gt;

&lt;p&gt;An &lt;em&gt;action&lt;/em&gt; is a custom application for the GitHub Actions platform that performs a complex but frequently repeated task. Actions help reduce the amount of repetitive code that you write in your workflow files. An action can pull your git repository from GitHub, set up the correct toolchain for your build environment, or set up the authentication to your cloud provider.&lt;/p&gt;

&lt;p&gt;You can write your own actions, or you can find actions to use in your workflows in the &lt;a href="https://github.com/marketplace?type=" rel="noopener noreferrer"&gt;GitHub Marketplace.&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Runners
&lt;/h3&gt;

&lt;p&gt;A &lt;em&gt;runner&lt;/em&gt; is a server that runs your workflows when they're triggered. Each runner can run a single job at a time. GitHub provides Ubuntu Linux, Microsoft Windows, and macOS runners to run your workflows; each workflow run executes in a fresh, newly-provisioned virtual machine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Secrets
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Secrets&lt;/em&gt; are environment variables that your jobs depend on. These are usually sensitive information that you wouldn’t want to be exposed to the public. Such secrets include; secret keys, API keys, access tokens, and passwords.&lt;/p&gt;

&lt;h3&gt;
  
  
  What you need for this config
&lt;/h3&gt;

&lt;p&gt;Before we set up our first workflow, you’ll need a few things.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A Github repository - this is the repo you want to set up your actions. For this demo, you can reference &lt;a href="https://github.com/tngeene/weather-stats-api" rel="noopener noreferrer"&gt;this repo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/nektos/act" rel="noopener noreferrer"&gt;Nektos/act package&lt;/a&gt; - it’s always advisable that you test your actions locally before pushing them to Github. This package enables us to do so.&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://en.wikipedia.org/wiki/Virtual_private_server" rel="noopener noreferrer"&gt;virtual private server&lt;/a&gt; - where we’ll be deploying our application. I recommend using digital ocean. They provide the best servers at affordable tiers. Sign up using my &lt;a href="https://m.do.co/c/eaa803fe4d99" rel="noopener noreferrer"&gt;affiliate link&lt;/a&gt; to get $100 credits for 60 days.

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://www.docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; and docker-compose- the nektos/act package runs on docker since we’ll be virtualizing our runners. Install them both on your machine and the server.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;h2&gt;
  
  
  Setting Our first workflow
&lt;/h2&gt;

&lt;p&gt;For this tutorial, we need to first understand what our objective is before we write any workflow files.&lt;/p&gt;

&lt;p&gt;We want our workflow to;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run our tests every time a pull request is made to the &lt;code&gt;main&lt;/code&gt; branch.&lt;/li&gt;
&lt;li&gt;Deploy to a server every time a push is made to the main branch.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For this setup, we’ll need two workflow files, i.e, &lt;code&gt;tests.yml&lt;/code&gt;, and &lt;code&gt;deploy.yml&lt;/code&gt; files.&lt;/p&gt;

&lt;p&gt;Let’s get started.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running Tests
&lt;/h3&gt;

&lt;p&gt;On your repo, switch to a new branch, in my case, I have a &lt;code&gt;workflows&lt;/code&gt; branch.&lt;/p&gt;

&lt;p&gt;Run&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mkdir .github &amp;amp;&amp;amp; mkdir ./github/workflow &amp;amp;&amp;amp; touch ./github/workflows/tests.yml&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Open the tests.yml file and paste the following code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Django Tests CI&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-18.04&lt;/span&gt;
    &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;max-parallel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;
      &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;python-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;3.8&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;3.9&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
      &lt;span class="c1"&gt;# this fixes local act bug of python setup&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local act python setup fix&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;# Hack to get setup-python to work on act&lt;/span&gt;
          &lt;span class="s"&gt;# (see https://github.com/nektos/act/issues/251)&lt;/span&gt;
          &lt;span class="s"&gt;if [ ! -f "/etc/lsb-release" ] ; then&lt;/span&gt;
            &lt;span class="s"&gt;echo "DISTRIB_RELEASE=18.04" &amp;gt; /etc/lsb-release&lt;/span&gt;
          &lt;span class="s"&gt;fi&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Python ${{ matrix.python-version }}&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-python@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;python-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ matrix.python-version }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Dependencies&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;python -m pip install --upgrade pip&lt;/span&gt;
          &lt;span class="s"&gt;pip install -r requirements.txt&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Tests&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;DEBUG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DEBUG }}&lt;/span&gt;
          &lt;span class="na"&gt;SECRET_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SECRET_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;DB_ENGINE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DB_ENGINE }}&lt;/span&gt;
          &lt;span class="na"&gt;DB_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DB_NAME }}&lt;/span&gt;
          &lt;span class="na"&gt;BASE_WEATHER_API_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.BASE_WEATHER_API_URL }}&lt;/span&gt;
          &lt;span class="na"&gt;WEATHER_API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.WEATHER_API_KEY }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;python manage.py test core.tests&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Starting from the top, we specify our workflow name&lt;/p&gt;

&lt;p&gt;name: Django Tests CI&lt;/p&gt;

&lt;p&gt;This can be whatever name you want but always use a name that’s easy to remember and descriptive of what the file does.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;on&lt;/code&gt; keyword is the entrypoint for our events, in this file, we’d like the action to run every time a push and a pull request is made to the main branch. This way, tests will always run if these scenarios are triggered.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jobs:
  test:
    runs-on: ubuntu-18.04
    strategy:
      max-parallel: 4
      matrix:
        python-version: [3.8, 3.9]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This bit has a few configurations.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We create a job called test.&lt;/li&gt;
&lt;li&gt;we specify that the job will run on ubuntu 18.04 since my server is running on that OS.&lt;/li&gt;
&lt;li&gt;Inside the test job, we specify a &lt;code&gt;strategy&lt;/code&gt; matrix. This allows running our tests on a few different python variations. In this case, we want the tests to be run in python 3.8 and python 3.9 environments.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;max-parallel&lt;/code&gt; The maximum number of jobs that can run simultaneously when using a &lt;code&gt;matrix&lt;/code&gt; job strategy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;steps&lt;/code&gt; - Next, we define the series of steps the job will follow. Steps are usually sequential, that is, they’re executed one after the other.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt; &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
      &lt;span class="s"&gt;# this fixes local act bug of python setup&lt;/span&gt;
      &lt;span class="s"&gt;- name&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local act python setup fix&lt;/span&gt;
        &lt;span class="s"&gt;run&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;# Hack to get setup-python to work on act&lt;/span&gt;
          &lt;span class="s"&gt;# (see https://github.com/nektos/act/issues/251)&lt;/span&gt;
          &lt;span class="s"&gt;if [ ! -f "/etc/lsb-release" ] ; then&lt;/span&gt;
            &lt;span class="s"&gt;echo "DISTRIB_RELEASE=18.04" &amp;gt; /etc/lsb-release&lt;/span&gt;
          &lt;span class="s"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first step is a caveat to note when working with act in a local environment (for mac users). It’ll likely throw an error that there’s a problem setting up python. The command is a bash script that resolves this issue. You can ignore it if you won’t face such a headwind.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt; &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Python ${{ matrix.python-version }}&lt;/span&gt;
        &lt;span class="s"&gt;uses&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-python@v2&lt;/span&gt;
        &lt;span class="s"&gt;with&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;python-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ matrix.python-version }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Dependencies&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;python -m pip install --upgrade pip&lt;/span&gt;
          &lt;span class="s"&gt;pip install -r requirements.txt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This next step sets up the python versions specified in the &lt;code&gt;matrix&lt;/code&gt;.  We install the dependencies and update &lt;code&gt;pip&lt;/code&gt; in the bash script. (defined in the &lt;code&gt;run&lt;/code&gt; keyword)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Tests&lt;/span&gt;
        &lt;span class="s"&gt;env&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;DEBUG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DEBUG }}&lt;/span&gt;
          &lt;span class="na"&gt;SECRET_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SECRET_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;DB_ENGINE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DB_ENGINE }}&lt;/span&gt;
          &lt;span class="na"&gt;DB_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DB_NAME }}&lt;/span&gt;
          &lt;span class="na"&gt;BASE_WEATHER_API_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.BASE_WEATHER_API_URL }}&lt;/span&gt;
          &lt;span class="na"&gt;WEATHER_API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.WEATHER_API_KEY }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;python manage.py test core.tests&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we run our tests.&lt;/p&gt;

&lt;p&gt;You have noticed the environmental variables in this action. To create environmental variables in your github repo go to your &lt;code&gt;repository &amp;gt; settings &amp;gt; left-sidebar &amp;gt; secrets&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Add whatever secrets you might require.&lt;/p&gt;

&lt;p&gt;Since we’ll need to test our actions locally first, create a &lt;code&gt;.secrets&lt;/code&gt; file in your project root. I decided to name mine ‘act.secrets’.&lt;/p&gt;

&lt;p&gt;For a more detailed explanation of how secrets work, check out the &lt;a href="https://docs.github.com/en/actions/security-guides/encrypted-secrets" rel="noopener noreferrer"&gt;official documentation.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, let’s run the tests workflow by using;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;act —secret-file act.secrets&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Drumrolls 🥁🥁.... if you set up everything correctly, the output should be like this;&lt;/p&gt;

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

&lt;p&gt;Congratulations, you’ve made your first GitHub action! Next, push the code to GitHub, make a PR to the main branch and check if it’s been triggered. Finally, merge the PR to the main branch. This should also trigger the workflow.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Setting Up AutoDeploy Pipeline
&lt;/h2&gt;

&lt;p&gt;Now that we’ve set up our tests action, we’ll need to pass deploy to our server when we merge into the main branch.&lt;/p&gt;

&lt;p&gt;For this, make a &lt;code&gt;deploy.yml&lt;/code&gt; file under the workflows folder and paste the following snippet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Django Deploy CD&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-18.04&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to server&lt;/span&gt;
      &lt;span class="c1"&gt;# don't run locally&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ !env.ACT }}&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;appleboy/ssh-action@master&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.LIVE_SERVER_IP }}&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SERVER_USERNAME }}&lt;/span&gt;
          &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SERVER_USER_PASSWORD }}&lt;/span&gt;
          &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DEPLOY_PORT }}&lt;/span&gt;
          &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;cd ${{ secrets.PROJECT_PATH }}&lt;/span&gt;
            &lt;span class="s"&gt;git pull ${{secrets.REPO_URL}}&lt;/span&gt;
            &lt;span class="s"&gt;docker-compose down &lt;/span&gt;
            &lt;span class="s"&gt;docker-compose up --build -d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Like the first file, this folder follows the same structure, just different commands. I will cover the new parts.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;if: ${{ !env.ACT }}&lt;/code&gt; - this command instructs the nektos/act package to ignore this job. This is because, in most cases, we don’t want to deploy to our server from our local environment.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;uses: appleboy/ssh-action@master&lt;/code&gt; - this instructs our job to use the publicly available GitHub action, “appleboy/ssh-action@master” which is an ssh client. Check out their &lt;a href="https://opensourcelibs.com/lib/ssh-action" rel="noopener noreferrer"&gt;official docs&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;   &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.LIVE_SERVER_IP }}&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SERVER_USERNAME }}&lt;/span&gt;
          &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SERVER_USER_PASSWORD }}&lt;/span&gt;
          &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DEPLOY_PORT }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This action requires us to define a few secrets. The final expected GitHub secrets for this job are;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;LIVE_SERVER_IP - server ip address&lt;/span&gt;
&lt;span class="s"&gt;SERVER_USERNAME - user logging into the server&lt;/span&gt;
&lt;span class="s"&gt;DEPLOY_PORT - ssh port, default is &lt;/span&gt;&lt;span class="m"&gt;22&lt;/span&gt;
&lt;span class="s"&gt;SERVER_USER_PASSWORD - password of user, can using public key as well in place of this&lt;/span&gt;
&lt;span class="s"&gt;USERNAME= github username&lt;/span&gt;
&lt;span class="s"&gt;PROJECT_PATH - path the project is located on server&lt;/span&gt;
&lt;span class="s"&gt;REPO_URL=https://{USERS_GITHUB_TOKEN}@github.com/{USERNAME}/{repo-name}.git&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we run a series of commands once the action has managed to ssh into the server. Since I’m using docker and docker-compose,  my pipeline is as follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;   &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;cd ${{ secrets.PROJECT_PATH }}&lt;/span&gt;
            &lt;span class="s"&gt;git pull ${{secrets.REPO_URL}}&lt;/span&gt;
            &lt;span class="s"&gt;docker-compose down &lt;/span&gt;
            &lt;span class="s"&gt;docker-compose up --build -d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the final piece, push the code to Github, make a PR to the main branch(remember this will trigger the test workflow). Once the tests have run, merge the PR, and if you followed along fine, the tests and deployment actions will be set in motion.&lt;/p&gt;

&lt;p&gt;If you configured everything well, the output should be similar to this.&lt;/p&gt;

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

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

&lt;p&gt;Deploy Action&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdcfbhgw5nr6mnaexy8yp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdcfbhgw5nr6mnaexy8yp.png" alt="deploy action tngeene"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;p&gt;The content of this post can be nerve-wracking and seem a lot to bite, however, if you feel you need further reading on some of the concepts explained, do check out these curated resources  I used while doing my research.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;a href="https://docs.github.com/en/actions" rel="noopener noreferrer"&gt;official Github actions documentation&lt;/a&gt; is a treasure trove on all things Github actions.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.freecodecamp.org/news/what-are-github-actions-and-how-can-you-automate-tests-and-slack-notifications/" rel="noopener noreferrer"&gt;Colby Fayock’s&lt;/a&gt; article on Freecodecamp.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=X3F3El_yvFg" rel="noopener noreferrer"&gt;This tutorial&lt;/a&gt; on Brad Traversy’s Youtube channel.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Github actions offer a way for DevOps teams to quickly set up a CI/CD pipeline. In this post, we set up a Django application, with some automated tests. We also set up two GitHub actions; one that automatically runs our tests when a pull request is made to the main branch, and another that automatically deploys the application to a virtual private server.&lt;/p&gt;

&lt;p&gt;You can read further on working with different environments, setting up restrictions on certain actions and deploying your own actions to the Github marketplace.&lt;/p&gt;

&lt;p&gt;Thanks for following along this far and I hope this goes a long way to aid in saving your precious time by avoiding manually doing this.&lt;/p&gt;

&lt;p&gt;If you have any thoughts, extra pointers, or just a comment, feel free to reach out by leaving a comment, or shooting up a dm on &lt;a href="https://twitter.com/Ngeene_kihiu" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sponsors
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.scraperapi.com/?via=teddy44" rel="noopener noreferrer"&gt;Scraper API&lt;/a&gt; is a startup specializing in strategies that'll ease the worry of your IP address from being blocked while web scraping. They utilize IP rotation so you can avoid detection. Boasting over 20 million IP addresses and unlimited bandwidth. Using Scraper API and a tool like &lt;a href="https://2captcha.com/?from=12437369" rel="noopener noreferrer"&gt;2captcha&lt;/a&gt; will give you an edge over other developers. The two can be used together to automate processes. Sign up on Scraper API and use &lt;a href="https://www.scraperapi.com/?via=teddy44" rel="noopener noreferrer"&gt;this link&lt;/a&gt; to get a 10% discount on your first purchase.&lt;/li&gt;
&lt;li&gt;Do you need a place to host your website or app, &lt;a href="http://cloudwww.digitalocean.com/" rel="noopener noreferrer"&gt;Digital ocean&lt;/a&gt;
is just the solution you need, sign up on digital ocean using this &lt;a href="https://m.do.co/c/eaa803fe4d99" rel="noopener noreferrer"&gt;link&lt;/a&gt; and experience the best cloud service, provider.&lt;/li&gt;
&lt;li&gt;The journey to becoming a developer can be long and tormentous, luckily &lt;a href="http://referral.pluralsight.com/mQh0Nxp" rel="noopener noreferrer"&gt;Pluralsight&lt;/a&gt; makes it easier to learn. They offer a wide range of courses, with top quality trainers, whom I can personally vouch for. Sign up using &lt;a href="http://referral.pluralsight.com/mQh0Nxp" rel="noopener noreferrer"&gt;this link&lt;/a&gt; and get a 50% discount on your first course.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devops</category>
      <category>django</category>
      <category>github</category>
      <category>python</category>
    </item>
    <item>
      <title>When are microservices appropriate?</title>
      <dc:creator>Ted Ngeene</dc:creator>
      <pubDate>Tue, 30 Nov 2021 23:52:25 +0000</pubDate>
      <link>https://forem.com/tngeene/when-are-microservices-appropriate-g2n</link>
      <guid>https://forem.com/tngeene/when-are-microservices-appropriate-g2n</guid>
      <description>&lt;p&gt;Lately, I've been tending to lean more towards employing a monolithic approach in system architecture. On paper, using micro-services seems like an ideal solution. The fact that you can decouple a project into smaller manageable bits is good. However, from experience and a couple of blog posts and videos I've watched, they do introduce a lot of complexities. Just to mention a few would be; giving database access to a microservice poses major security risks if not properly implemented, error handling, and especially if the said microservice is down would essentially make the system fail, connection error, read timeout, resource not found...you have to deal with many error scenarios.&lt;/p&gt;

&lt;p&gt;I'm not completely against them, for example I believe a microservice should do one thing and do it really well. A good example would be sending sms notifications. This wouldn't really need to interact with any database. Just accept a list of phone numbers and send out the messages.&lt;/p&gt;

&lt;p&gt;So my question is; when are my microservices appropriate and do we really need them as much as folks preach?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>distributedsystems</category>
      <category>watercooler</category>
      <category>codequality</category>
    </item>
    <item>
      <title>AdonisJs - Introduction to database migrations</title>
      <dc:creator>Ted Ngeene</dc:creator>
      <pubDate>Mon, 15 Nov 2021 06:48:08 +0000</pubDate>
      <link>https://forem.com/tngeene/adonisjs-introduction-to-database-migrations-i03</link>
      <guid>https://forem.com/tngeene/adonisjs-introduction-to-database-migrations-i03</guid>
      <description>&lt;h1&gt;
  
  
  Introduction to Database Migrations
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;This article will introduce you to the concept of migrations in AdonisJs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the previous articles, we learned 2 approaches to working with events in AdonisJs. In this sixth installment of the &lt;a href="https://dev.to/tngeene/series/14084"&gt;everything you need to know about adonisJs series&lt;/a&gt;, we will familiarize ourselves with the concept of migrations. The aim is to have a broader understanding of how our database state will look like.&lt;/p&gt;

&lt;p&gt;Source code for this tutorial can be &lt;a href="https://github.com/tngeene/adonis-tutorial-fitit" rel="noopener noreferrer"&gt;found here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I will link the resources at the end of the article.&lt;/p&gt;

&lt;p&gt;Let's get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our Database Structure
&lt;/h2&gt;

&lt;p&gt;Just to remind you how our database should look like, we'll reference this diagram.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--e9nyVRxq--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_880%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpzn7uhf6fh7om3u017g5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--e9nyVRxq--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_880%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpzn7uhf6fh7om3u017g5.png" alt="https://res.cloudinary.com/practicaldev/image/fetch/s--e9nyVRxq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pzn7uhf6fh7om3u017g5.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining Model Relationships
&lt;/h2&gt;

&lt;p&gt;Database migrations and modeling can sometimes be a pain point for many devs to fully understand. It's important to plan your database in the most optimal way that meets business requirements. Therefore, before we start coding, it's crucial to understand know just how our models will be related to each other. I'll attempt to explain this from a high-level view, then head over to the implementation.&lt;/p&gt;

&lt;p&gt;We'll use the diagram above as a guide to modeling our database.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A user can own many stores and a store can only be owned by one user, hence this would be a &lt;code&gt;many-to-one&lt;/code&gt; relationship.&lt;/li&gt;
&lt;li&gt;A store can be followed by many users and a user can follow many stores. So this would be represented by a &lt;code&gt;many-to-many relationship&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;An item(product) can only belong to a single store, but a store can have many items, hence this is a &lt;code&gt;one-to-many&lt;/code&gt; relationship.&lt;/li&gt;
&lt;li&gt;An item can only be uploaded by one user, and a user can upload many items. This will also be represented as a &lt;code&gt;one-to-many&lt;/code&gt; relationship.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;many-to-one and one-to-many are the same things.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Migrations
&lt;/h3&gt;

&lt;p&gt;Migration files allow us to create schema definitions using code. They are a way to alter a database programmatically. Adonis allows us to do so in a typescript-safe environment.&lt;br&gt;
In most cases, an application will have a migration file for each table it needs. Later on, in the application's lifespan, you may have migrations for any alteration(s) you need to perform in a given table.&lt;/p&gt;
&lt;h3&gt;
  
  
  The flow of migrations
&lt;/h3&gt;

&lt;p&gt;Migrations have two directions, &lt;code&gt;up&lt;/code&gt; and &lt;code&gt;down&lt;/code&gt;. We run migrations to go up when we have changes we'd like to persist in our database and down when we want to roll back (undoing changes) when the changes were done.&lt;/p&gt;

&lt;p&gt;This flow is good for several reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We can easily roll back changes that aren't in production to make alterations as needed, during development.&lt;/li&gt;
&lt;li&gt;We can maintain a history of the changes made to our application database over time.&lt;/li&gt;
&lt;li&gt;Alterations made to our migrations are enforced at the database level, hence when we deploy to production, the changes are always synced with our database state. This means that the database is always updated with the latest changes.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;It's worth noting that rolling back migrations in production should be done with caution. This is because it poses the risk of losing data. Always have a backup of the database if you wish to roll back in production.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Migration commands and setting up our first migration file
&lt;/h3&gt;

&lt;p&gt;The Adonis CLI ships with several commands that we can run in regards to migrations&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;make:migration&lt;/code&gt; - allows us to create a migration&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;migration:run&lt;/code&gt; - allows us to run all migrations that haven't been previously run, i.e, sync them with the database&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;migration:rollback&lt;/code&gt; - This allows us to roll back previously run migrations. (Return to a previous state)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;migration:status&lt;/code&gt; - allows us to view the run status on our migrations, i.e, whether the migrations altered the database state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Migration folders are contained in the &lt;code&gt;database/migrations&lt;/code&gt; directory.&lt;/p&gt;
&lt;h3&gt;
  
  
  Setting up our store Migration
&lt;/h3&gt;

&lt;p&gt;For this tutorial, I'll set up the store migration. All other migration folders will follow the same flow. You can recreate them following similar steps.&lt;br&gt;
If you run into any errors, feel free to check out the &lt;a href="https://github.com/tngeene/adonis-tutorial-fitit" rel="noopener noreferrer"&gt;source code&lt;/a&gt; or leave a comment.&lt;/p&gt;

&lt;p&gt;We'll run the &lt;code&gt;make migration&lt;/code&gt; command to set up our store schema.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  node ace make:migration stores

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a migration file in the migrations directory. Paste the missing values in the folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;BaseSchema&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ioc:Adonis/Lucid/Schema&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Stores&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BaseSchema&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nx"&gt;tableName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stores&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;up&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tableName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;notNullable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;owner&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;references&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users.id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;onDelete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CASCADE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;description&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;notNullable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cover_photo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;nullable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;slug&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;notNullable&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;notNullable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;phone_number&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;is_active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;defaultTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="cm"&gt;/**
       * Uses timestamptz for PostgreSQL and DATETIME2 for MSSQL
       */&lt;/span&gt;
      &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;created_at&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;useTz&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;updated_at&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;useTz&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;down&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dropTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tableName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you will notice, the migration filename is prefixed with some numeric value. We add the current timestamp to the filename so that the migration files are sorted in the order created.&lt;br&gt;
By default, migration includes an id and timestamps column.&lt;br&gt;
The rest of the fields are defined by the developer. If you look at the migration folder above, you can see that it's self-descriptive,&lt;/p&gt;

&lt;h2&gt;
  
  
  Up
&lt;/h2&gt;

&lt;p&gt;The up method defines the record initialization logic. The first line instructs our migration file to create a table called &lt;code&gt;stores&lt;/code&gt; within our database. The following are then methods that enable us to define the column types or attributes of our records.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;table&lt;/code&gt; -&amp;gt; means we're initializing a database column&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;table.string('description')&lt;/code&gt; -&amp;gt; the column is to be of type &lt;code&gt;string&lt;/code&gt; with a name, 'description`. There are other column types, including; boolean, enum, integer, and so on.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;notNullable/nullable&lt;/code&gt; -&amp;gt; defines whether the field is allowed to have &lt;code&gt;NULL&lt;/code&gt; values.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;primary&lt;/code&gt; -&amp;gt; defines the column as the primary key of that table&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;unique&lt;/code&gt; -&amp;gt; ensures that the value of the database columns is all unique. We will implement this functionality in a later article.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Down
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;down&lt;/code&gt; method is used to roll back the actions executed by the up method. For example, if the up method creates a table, the down method should drop the same table.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`tsx&lt;/p&gt;

&lt;p&gt;public async down() {&lt;br&gt;
    this.schema.dropTable(this.tableName)&lt;br&gt;
   }&lt;/p&gt;

&lt;p&gt;`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Relationships and foreign keys
&lt;/h2&gt;

&lt;p&gt;As you may have noticed from our migration and the database schema diagram, our stores will be owned by a user. So how do we go about this in a migration file?&lt;/p&gt;

&lt;p&gt;We can use a couple of approaches.&lt;/p&gt;

&lt;p&gt;One is exactly as I've defined in my migration file.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`tsx&lt;/p&gt;

&lt;p&gt;table.integer('owner').references('users.id').onDelete('CASCADE')&lt;/p&gt;

&lt;p&gt;`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here we are telling adonis that the column named, "owner" is an integer that references the user's table, specifically the id column, which we know is an integer. the &lt;code&gt;onDelete()&lt;/code&gt; method enforces certain rules for when a user is deleted and they have an existing store. In our case, we go with &lt;code&gt;CASCADE&lt;/code&gt;, meaning that when a user is deleted, then recursively delete all their stores. Other options include; &lt;code&gt;PROTECT&lt;/code&gt; and &lt;code&gt;SET_NULL&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Another approach would be;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`tsx&lt;/p&gt;

&lt;p&gt;table.integer('owner').index()&lt;br&gt;
table.foreign('owner').references('users.id').onDelete('CASCADE')&lt;/p&gt;

&lt;p&gt;`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`tsx&lt;/p&gt;

&lt;p&gt;table.integer('owner').references('id').inTable('users')&lt;/p&gt;

&lt;p&gt;`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;As you can tell, there's more than one to achieve an objective in Adonis. The approach you choose is entirely up to you.&lt;/p&gt;

&lt;p&gt;For many-to-many relationships, we'll look into it once we start working with models in another article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running and Rolling Back migrations
&lt;/h2&gt;

&lt;p&gt;Now that we have our up and down methods defined, we can go ahead and run our migrations by running the command on our terminal.&lt;/p&gt;

&lt;p&gt;node ace migration:run&lt;/p&gt;

&lt;p&gt;This command executes the &lt;code&gt;up&lt;/code&gt; method in all migration files.&lt;/p&gt;

&lt;p&gt;SQL statements for every migration file are wrapped inside a transaction. So if one statement fails, all other statements within the same file will rollback.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Also, in case of failure, the subsequent migrations will be aborted. However, the migrations before the failed migration stay in the completed state.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the migration was successful, the command line GUI should not throw any error.&lt;/p&gt;

&lt;p&gt;That's it! We have other migration operations which I will not cover in-depth as they have been comprehensively covered in the official docs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;Some of the references that I used to cover this article were acquired from the following sources.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;a href="https://docs.adonisjs.com/guides/database/migrations" rel="noopener noreferrer"&gt;official AdonisJS documentation&lt;/a&gt; on schema migrations.&lt;/li&gt;
&lt;li&gt;This awesome article from &lt;a href="https://jagr.co/lessons/lets-learn-adonis-5-migrations" rel="noopener noreferrer"&gt;Jagr&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Sponsors
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;*Disclosure: I only recommend products I would use myself and all recommendations here are my own. This post may contain affiliate links that at no additional cost to you, I may earn a small commission.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.scraperapi.com?via=teddy44" rel="noopener noreferrer"&gt;Scraper API&lt;/a&gt; is a startup specializing in strategies that'll ease the worry of your IP address from being blocked while web scraping. They utilize IP rotation so you can avoid detection. Boasting over 20 million IP addresses and unlimited bandwidth. Using Scraper API and a tool like &lt;a href="https://2captcha.com?from=12437369" rel="noopener noreferrer"&gt;2captcha&lt;/a&gt; will give you an edge over other developers. The two can be used together to automate processes. Sign up on Scraper API and use &lt;a href="https://www.scraperapi.com?via=teddy44" rel="noopener noreferrer"&gt;this link&lt;/a&gt; to get a 10% discount on your first purchase.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Do you need a place to host your website or app, &lt;a href="//DigitalOcean%20%E2%80%93%20The%20developer%20cloudwww.digitalocean.com"&gt;Digital ocean&lt;/a&gt;&lt;br&gt;
is just the solution you need, sign up on digital ocean using this &lt;a href="https://m.do.co/c/eaa803fe4d99" rel="noopener noreferrer"&gt;link&lt;/a&gt; and experience the best cloud service provider.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The journey to becoming a developer can be long and tormentous, luckily &lt;a href="http://referral.pluralsight.com/mQh0Nxp" rel="noopener noreferrer"&gt;Pluralsight&lt;/a&gt; makes it easier to learn. They offer a wide range of courses, with top quality trainers, whom I can personally vouch for. Sign up using &lt;a href="http://referral.pluralsight.com/mQh0Nxp" rel="noopener noreferrer"&gt;this link&lt;/a&gt; and get a 50% discount on your first course.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope you've had a better understanding of database migrations.&lt;br&gt;
If you have any questions on this topic, feel free to leave a comment or get in touch directly on &lt;a href="https://twitter.com/Ngeene_kihiu" rel="noopener noreferrer"&gt;twitter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>adonisjs</category>
      <category>node</category>
      <category>typescript</category>
      <category>database</category>
    </item>
    <item>
      <title>Adonis Js - Events and Mailing Part 2</title>
      <dc:creator>Ted Ngeene</dc:creator>
      <pubDate>Tue, 05 Oct 2021 17:00:14 +0000</pubDate>
      <link>https://forem.com/tngeene/adonis-js-events-and-mailing-part-2-4f17</link>
      <guid>https://forem.com/tngeene/adonis-js-events-and-mailing-part-2-4f17</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Curiosity keeps leading us down new paths. ~ Walt Disney&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is often said that in programming there's always more than one way of doing things. The maxim is that to grow we must be open to new ideas...new ways of doing things. &lt;/p&gt;

&lt;p&gt;As developers, we always try to explore different ways of doing things.&lt;/p&gt;

&lt;p&gt;In this second piece of implementing events and mailing in Adonis, I'll demonstrate another technique we can use to capture different events in our applications.&lt;/p&gt;

&lt;p&gt;A reminder that the entire source code for this project can be found &lt;a href="https://github.com/tngeene/adonis-tutorial-fitit" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;br&gt;
Let's start.&lt;/p&gt;
&lt;h2&gt;
  
  
  Using the built-in Adonis event emitter module
&lt;/h2&gt;

&lt;p&gt;For this guide, we'll use the built-in Event emitter module that comes in AdonisJs. You can peruse the &lt;a href="https://docs.adonisjs.com/guides/events" rel="noopener noreferrer"&gt;official event documentation&lt;/a&gt; to have a better understanding of this concept.&lt;/p&gt;

&lt;p&gt;We'll implement the same flow we did in our &lt;a href="https://dev.to/tngeene/events-and-mailing-part-1-2oi1"&gt;previous article&lt;/a&gt;, where the user receives a notification email upon registration to activate their account, so be sure to check it out!&lt;/p&gt;
&lt;h2&gt;
  
  
  What is the AdonisJs event module?
&lt;/h2&gt;

&lt;p&gt;According to the adonis documentation, "The AdonisJS event emitter module is built on top of emittery".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/sindresorhus/emittery" rel="noopener noreferrer"&gt;Emittery&lt;/a&gt; is a modern async event emitter for node.js.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Some basic knowledge of the node.js event loop would help you better understand it but is not necessary.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node ace make:prldfile events
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;This command creates a new events.ts file in the contracts directory. This file is the entry point for all events in our application. Select all options prompted by the CLI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;App/Models/User&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kr"&gt;module&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ioc:Adonis/Core/Event&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;EventsList&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;new:user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;newUser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Event.on&lt;/code&gt; method registers a new event listener. It accepts the name of the event, in our case &lt;code&gt;new:user&lt;/code&gt;, followed by a method to handle the events as the arguments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Listener Classes
&lt;/h2&gt;

&lt;p&gt;Listener classes define the logic for our events. This is similar to the model function we defined in the last post.&lt;br&gt;
Conventionally event listeners are stored inside the app/Listeners directory. However, you can customize the namespace inside the &lt;code&gt;.adonisrc.json&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;To make a new event listener class that will handle the emailing, run the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node ace make:listener User

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A new &lt;code&gt;User.ts&lt;/code&gt; file will be created under the &lt;code&gt;app/Listeners/&lt;/code&gt; directory. Open the newly created file and paste this code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Mail&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ioc:Adonis/Addons/Mail&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Env&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ioc:Adonis/Core/Env&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;EventsList&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ioc:Adonis/Core/Event&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ioc:Adonis/Core/Route&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;onNewUser&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;newUser&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;EventsList&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;new:user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appDomain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;APP_URL&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;APP_NAME&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;defaultFromEmail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DEFAULT_FROM_EMAIL&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currentYear&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getFullYear&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;params&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prefixUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;appDomain&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;makeSigned&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;verifyEmail&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;expiresIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;24hours&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Mail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;message&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;defaultFromEmail&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Please verify your email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;htmlView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;emails/auth/verify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;appName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;appDomain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currentYear&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the code above is very similar to what we'd defined earlier as the &lt;code&gt;sendVerificationEmail()&lt;/code&gt; function. For a more detailed explanation, head over to &lt;a href="https://dev.to/tngeene/events-and-mailing-part-1-2oi1"&gt;that article&lt;/a&gt; and check out the description.&lt;/p&gt;

&lt;p&gt;However, just as a recap, we're defining the mail sending capability and building a URL that will encode our user token. The token expires in 24 hours and is tied down to a named URL, &lt;code&gt;verifyEmail&lt;/code&gt;. Now, onto the new stuff.&lt;/p&gt;

&lt;p&gt;public async onNewUser({ newUser }: EventsList['new:user'])&lt;/p&gt;

&lt;p&gt;We're defining an async function named, &lt;code&gt;onNewUser&lt;/code&gt; inside the default User class that takes the &lt;code&gt;newUser&lt;/code&gt; as an argument. The newUser argument is tied to the event we just defined before. There, it'll always ensure that the parameters passed will match the ones defined in the event declaration.&lt;/p&gt;

&lt;p&gt;If you wish to pass more than one argument, you can always define them in the &lt;code&gt;events.ts&lt;/code&gt; file by separating them with semicolons.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
 &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;new:user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;newUser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;arg2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;modelName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;arg3&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;modelName&lt;/span&gt;&lt;span class="p"&gt;;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then calling the same arguments on the function declaration&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;onNewUser&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="nx"&gt;newUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="nx"&gt;arg2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="nx"&gt;arg3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;....&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we can emit our event on the authController.&lt;/p&gt;

&lt;p&gt;Import the events module&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Event from '@ioc:Adonis/Core/Event'

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then right below the validation in the register function&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;validations&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;new:user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;newUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're calling the &lt;code&gt;new:user&lt;/code&gt; event we defined in the &lt;code&gt;events.ts&lt;/code&gt; file as this is the event name, the second parameter is the arguments to take.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that the argument names must be named similarly to the arguments we passed to the event declaration. By this I mean that you have to name the variable newUser just as it's declared in the event declaration. The same follows when having multiple arguments.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, we'll test the event.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Remember to turn on the development server by running, &lt;code&gt;node ace serve --watch&lt;/code&gt;`&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Registration
&lt;/h3&gt;

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

&lt;h3&gt;
  
  
  Email
&lt;/h3&gt;

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

&lt;h3&gt;
  
  
  Account activated
&lt;/h3&gt;

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

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;The complete source code can be accessed on my &lt;a href="https://github.com/tngeene/adonis-tutorial-fitit" rel="noopener noreferrer"&gt;Github profile, here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Frontend code for the email template can be found in this &lt;a href="https://www.notion.so/78a312be1151c46a994b21c1729cf3d5" rel="noopener noreferrer"&gt;GitHub gist&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.adonisjs.com/guides/events" rel="noopener noreferrer"&gt;official adonis events documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://blog.logrocket.com/a-complete-guide-to-the-node-js-event-loop/" rel="noopener noreferrer"&gt;Node.js event loop&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;In this article, we've learned another way of working with events in AdonisJs. It's entirely up to you to decide which method best works for you. That being said, always employ the most optimal approach.&lt;br&gt;
If you have any queries, comment and insight, don't hesitate to reach out on my &lt;a href="https://twitter.com/Ngeene_kihiu" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, &lt;a href="https://tngeeene.com/" rel="noopener noreferrer"&gt;personal website&lt;/a&gt; or simply by leaving a comment below.&lt;/p&gt;

&lt;p&gt;I'll be going over models, migrations, and relationships in my next article. Till then...cheers!&lt;/p&gt;

</description>
      <category>adonis</category>
      <category>beginners</category>
      <category>javascript</category>
      <category>node</category>
    </item>
    <item>
      <title>AdonisJs - Events and Mailing Part 1</title>
      <dc:creator>Ted Ngeene</dc:creator>
      <pubDate>Fri, 24 Sep 2021 20:21:11 +0000</pubDate>
      <link>https://forem.com/tngeene/events-and-mailing-part-1-2oi1</link>
      <guid>https://forem.com/tngeene/events-and-mailing-part-1-2oi1</guid>
      <description>&lt;p&gt;Welcome back to the fourth installment of the AdonisJs series!&lt;/p&gt;

&lt;p&gt;A web framework's ability to handle events and mailing greatly add to its appeal. Luckily, Adonis provides these in an easily configurable way that will set you on your path to building robust APIs and web apps. &lt;/p&gt;

&lt;p&gt;In this article, I'll show just how we go about these two crucial concepts in modern software development. In order to achieve our goal, we'll build upon the &lt;a href="https://dev.to/tngeene/adonisjs-understanding-user-registration-and-authentication-2ojl"&gt;previous article&lt;/a&gt;, where we talked about user registration and login.&lt;/p&gt;

&lt;p&gt;If you're not familiar with the concept of events in software development, worry not.&lt;/p&gt;

&lt;p&gt;Events can be described as actions that are triggered by another action in a system, for example, you'd want your users to be emailed upon successful registration. In this case, the email sending is an event triggered by successful user registration.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we'll be working on
&lt;/h2&gt;

&lt;p&gt;For this tutorial, we're going to implement an event that emails our users an activation link for their accounts. The purpose of this is to enforce security in our application.&lt;br&gt;
We definitely wouldn't have bots and fictitious accounts registering in our application.&lt;/p&gt;

&lt;p&gt;We'll look at two approaches we can employ to achieve this, but before we do that, let's set up the mailer.&lt;/p&gt;
&lt;h2&gt;
  
  
  Installing Adonis Mailing package.
&lt;/h2&gt;

&lt;p&gt;Since we'll need users to receive emails, we need a way to make our system be able to send them. Luckily, Adonis also has a &lt;a href="https://docs.adonisjs.com/guides/mailer#document" rel="noopener noreferrer"&gt;mailer package&lt;/a&gt; for this. To install, run &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; npm i @adonisjs/mail
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;As usual, we also need to configure the package preferences,&lt;/p&gt;

&lt;p&gt;node ace invoke @adonisjs/mail&lt;/p&gt;

&lt;p&gt;For my configuration, I'll be using SMTP&lt;/p&gt;

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

&lt;p&gt;Open up the &lt;code&gt;env.ts&lt;/code&gt; file and paste the following values.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SMTP_HOST: Env.schema.string({ format: 'host' }),
SMTP_PORT: Env.schema.number(),
SMTP_USERNAME: Env.schema.string(),
SMTP_PASSWORD: Env.schema.string(),
DEFAULT_FROM_EMAIL: Env.schema.string(),
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;These values are the environment variables for our SMTP configuration. I recommend validating them in the &lt;code&gt;env.ts&lt;/code&gt; file. &lt;br&gt;
The &lt;code&gt;DEFAULT_FROM_EMAIL&lt;/code&gt; is the email that will appear as the sender from our application.&lt;br&gt;
The main environment variables will reside in the &lt;code&gt;.env&lt;/code&gt; file. We'll get there in a minute.&lt;/p&gt;
&lt;h3&gt;
  
  
  Setting up our SMTP service
&lt;/h3&gt;

&lt;p&gt;The Simple Mail Transfer Protocol(SMTP)  is an internet standard communication protocol for electronic mail transmission. Mail servers and other message transfer agents use SMTP to send and receive mail messages. &lt;/p&gt;

&lt;p&gt;There are a number of SMTP providers; including, google, mailgun, SendGrid...and so on. However, since we are not building a production-ready application yet, I'll be using &lt;a href="https://mailtrap.io/" rel="noopener noreferrer"&gt;mailtrap.&lt;/a&gt; &lt;/p&gt;

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

&lt;p&gt;Mailtrap is an email testing tool that Captures SMTP traffic from staging and dev environments. Simply put, it mocks real-world emails in a sandbox environment. Head over to their &lt;a href="https://mailtrap.io/" rel="noopener noreferrer"&gt;website&lt;/a&gt;, create an account, and navigate to the demo inbox section.&lt;/p&gt;

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

&lt;p&gt;Next, open the .env file located in your project's root directory and paste these constants.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  SMTP_HOST=smtp.mailtrap.io
  SMTP_PORT=2525
  SMTP_USERNAME=&amp;lt;your_mailtrap_username&amp;gt;
  SMTP_PASSWORD=&amp;lt;your_mailtrap_password&amp;gt;
  DEFAULT_FROM_EMAIL=admin@fitit.com #varies
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The username and password values are found on the mailtrap dashboard, under the &lt;code&gt;integrations&lt;/code&gt; section.&lt;/p&gt;

&lt;p&gt;Now that we have all that setup, we'll get straight right into actual coding. &lt;/p&gt;
&lt;h2&gt;
  
  
  Approach 1 - Using a function in a model
&lt;/h2&gt;

&lt;p&gt;This approach will involve us having a function inside our user model that will handle email sending. We'll call this function in our authController, right after a successful registration.&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;app/Models/User.ts&lt;/code&gt;. We'll import several packages at the top,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Mail&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ioc:Adonis/Addons/Mail&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Env&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ioc:Adonis/Core/Env&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ioc:Adonis/Core/Route&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, after all the model column definitions, we'll write a &lt;code&gt;sendVerificationEmail&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;sendVerificationEmail&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appDomain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;APP_URL&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;APP_NAME&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currentYear&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getFullYear&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;params&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prefixUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;appDomain&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;makeSigned&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;verifyEmail&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;expiresIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;24hours&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nx"&gt;Mail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;message&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DEFAULT_FROM_EMAIL&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Please verify your email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;htmlView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;emails/auth/verify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;appName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;appDomain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currentYear&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code above is contains several building blocks that helps to achieve the email sending functionality.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appDomain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;APP_URL&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;APP_NAME&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are values which reside in our &lt;code&gt;.env&lt;/code&gt; file. If you don't have them already, you can check out the &lt;code&gt;env.example&lt;/code&gt; file of this &lt;a href="https://github.com/tngeene/adonis-tutorial-fitit" rel="noopener noreferrer"&gt;project repo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My current values are &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  APP_NAME=Fitit
  APP_URL=http://127.0.0.1:3333
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currentYear&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getFullYear&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we get the current year. We'll inject this as a variable in our html template for our email&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;params&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prefixUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;appDomain&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;makeSigned&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;verifyEmail&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;expiresIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;24hours&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're using the Route package to encode our user data into an activation token. The &lt;code&gt;makeSigned()&lt;/code&gt; function is tied to a &lt;code&gt;verifyEmail&lt;/code&gt; route that we'll build in the next part. Finally, we set an expiry period for the token. For this case, I set the period to 24 hours. That way, if a user fails to verify their account within that duration, their account won't be activated.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Mail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;message&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DEFAULT_FROM_EMAIL&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Please verify your email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;htmlView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;emails/auth/verify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;appName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;appDomain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currentYear&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This section makes use of the adonis mailer package. We're giving instructions to the program to send an email to the created user's email. The email will appear as received from the value &lt;code&gt;DEFAULT_FROM_EMAIL&lt;/code&gt; in our &lt;code&gt;.env&lt;/code&gt; file.&lt;br&gt;
The subject line will be, "Please verify your email".&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;htmlView&lt;/code&gt; method contains the template our email will read from, that is, the html code that styles our verification email.&lt;br&gt;
For this, create a folder named &lt;code&gt;resources&lt;/code&gt; in the project root directory. The entry point for all Adonis html templates must be located within this folder. &lt;br&gt;
Since we initialized this project as an API only, we'll install the package that enables us to have &lt;code&gt;.edge&lt;/code&gt; templates&lt;/p&gt;

&lt;p&gt;npm i @adonisjs/view&lt;/p&gt;

&lt;p&gt;The first argument the function takes is the html template to read from.&lt;br&gt;
Create a &lt;code&gt;emails\auth\verify.edge&lt;/code&gt; file and paste the code from this &lt;a href="https://gist.github.com/tngeene/78a312be1151c46a994b21c1729cf3d5" rel="noopener noreferrer"&gt;github gist&lt;/a&gt;. The next argument it takes are the variables to be read in the html template. In our case, &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{ user: this, url, appName, appDomain, currentYear }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Writing the Verify Email controller
&lt;/h3&gt;

&lt;p&gt;We're going to write an email verification controller that will be responsible for validating and activating a user's account.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node ace make:controller users/EmailVerificationsController
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Open the created file and copy this snippet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;HttpContextContract&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ioc:Adonis/Core/HttpContext&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;App/Models/User&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DateTime&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;luxon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EmailVerificationsController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;confirm&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;HttpContextContract&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasValidSignature&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findByOrFail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isActivated&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email_verified_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;local&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isActivated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;202&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Account verified and activated&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;409&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Account was already verified&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, copy this into the users.ts file under the routes directory&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/verify-email/:email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users/EmailVerificationsController.confirm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;verifyEmail&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're mapping the controller to a route. In adonis, routes can be given custom names, by defining the named route to a function, &lt;code&gt;as()&lt;/code&gt;. In our case, the route is called &lt;code&gt;verifyEmail&lt;/code&gt;. If you're keen enough, you'll notice that we passed this named route as a parameter in the model function we defined in the User model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;makeSigned&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;verifyEmail&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;expiresIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;24hours&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From the email verification controller code, we can see we have the &lt;code&gt;confirm()&lt;/code&gt; function.&lt;br&gt;
This block contains the user activation logic, that is, immediately the link is hit, we verify the user and activate his account. It also contains constraints that check whether the URL pattern matches the valid the signature, hence the &lt;code&gt;makeSigned()&lt;/code&gt; and &lt;code&gt;hasValidSignatureSections()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Finally, we modify the AuthController to send the email after a successful registration. Right after we save our user in the database,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// send verification email&lt;/span&gt;
    &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;sendVerificationEmail&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Registration successful, check your email inbox for a verification email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Testing
&lt;/h3&gt;

&lt;p&gt;For testing open up your postman and repeat the registration steps we used in the &lt;a href="https://dev.to/tngeene/adonisjs-understanding-user-registration-and-authentication-2ojl"&gt;previous article&lt;/a&gt;. Create a new user, and head over to your mailtrap demo inbox.&lt;/p&gt;

&lt;p&gt;If everything worked well, then the following images should be what you see.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fognw8ikao4dvag1x3ax8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fognw8ikao4dvag1x3ax8.png" alt="tngeene events adonis"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0c92k8qrwisc6ckmo4g1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0c92k8qrwisc6ckmo4g1.png" alt="tngeene events adonis"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuo5wzhaa4jrd95jcsi5s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuo5wzhaa4jrd95jcsi5s.png" alt="tngeene events adonis"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's a lot of content to cover in events. The method covered in this piece is just one way to go about it. To keep the article short, I've decided to cover the next approach in the next piece. &lt;/p&gt;

&lt;p&gt;If you have any comments, queries, don't hesitate to leave a comment or email. &lt;br&gt;
Until then, stay Adonis. It is the way! &lt;/p&gt;

</description>
      <category>adonisjs</category>
      <category>javascript</category>
      <category>node</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How to bypass captcha with 2captcha API and Selenium using Javascript</title>
      <dc:creator>Ted Ngeene</dc:creator>
      <pubDate>Sun, 12 Sep 2021 08:44:51 +0000</pubDate>
      <link>https://forem.com/tngeene/how-to-bypass-captcha-with-2captcha-api-and-selenium-4p5m</link>
      <guid>https://forem.com/tngeene/how-to-bypass-captcha-with-2captcha-api-and-selenium-4p5m</guid>
      <description>&lt;p&gt;Spam is a big nightmare for website owners. With the dawn of bots, this challenge has never been more prominent. Completely Automated Public Turing tests to tell Computers and Humans Apart(or CAPTCHA as they are commonly known) were introduced to tackle this issue.&lt;/p&gt;

&lt;p&gt;The squiggly lines and words of the past are less common nowadays and have been replaced by Google's version 2 of CAPTCHA, known as reCAPTCHA. However, bots are becoming more advanced and can bypass almost any captcha. With a little spare time and, a few resources, we can make a program that bypasses the all too annoying CAPTCHA. In this tutorial, we'll make use of the captcha bypass software, &lt;a href="https://2captcha.com/?from=12437369" rel="noopener noreferrer"&gt;2captcha&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--TImx3P4Z--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_880%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh2oqd92dh8bo0ute3qvn.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--TImx3P4Z--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_880%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh2oqd92dh8bo0ute3qvn.jpeg" alt="https://res.cloudinary.com/practicaldev/image/fetch/s--TImx3P4Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h2oqd92dh8bo0ute3qvn.jpeg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why are we doing this?
&lt;/h2&gt;

&lt;p&gt;Okay, I know some of you might be wondering, why would we need to bypass captcha? and most importantly; is this even legal? No, it is not illegal to bypass 2captcha. Whatever we'll be building is within the confines of the law. Secondly, this article is intended for anyone;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Curious about how you can bypass captcha&lt;/li&gt;
&lt;li&gt;Building web scrapers that would need to bypass a captcha&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;*Disclosure: I only recommend products I would use myself and all opinions expressed here are my own. This post may contain affiliate links that at no additional cost to you, I may earn a small commission.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What is 2captcha API and how does it work?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://2captcha.com/?from=12437369" rel="noopener noreferrer"&gt;2Captcha.com&lt;/a&gt; is a captcha solving service that automates the captcha solving process. They have an API and several packages that wrap around different programming languages. All you need is to register on their website, get an API key and make requests to their API.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: They charge a small rate for every captcha solved, however, the fee is quite minimal.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Signup Process and supported languages
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--JgfXuOWJ--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_880%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvdobzdwc4thex7mwocho.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--JgfXuOWJ--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_880%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvdobzdwc4thex7mwocho.png" alt="https://res.cloudinary.com/practicaldev/image/fetch/s--JgfXuOWJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vdobzdwc4thex7mwocho.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--OEXnGqQA--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_880%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fszwihqwt8xxrtpnz64fn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--OEXnGqQA--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_880%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fszwihqwt8xxrtpnz64fn.png" alt="https://res.cloudinary.com/practicaldev/image/fetch/s--OEXnGqQA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/szwihqwt8xxrtpnz64fn.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Supported captchas the software solves include;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--px3AuiEX--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_880%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F11ybxotkegschanhgq9i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--px3AuiEX--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_880%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F11ybxotkegschanhgq9i.png" alt="https://res.cloudinary.com/practicaldev/image/fetch/s--px3AuiEX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/11ybxotkegschanhgq9i.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements and Setup
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;For this tutorial, we'll utilize the &lt;a href="https://2captcha.com/2captcha-api" rel="noopener noreferrer"&gt;2captcha API&lt;/a&gt;. You'll need to have a developer account to use. You can head over to &lt;a href="https://2captcha.com/?from=12437369" rel="noopener noreferrer"&gt;this link and sign up&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The source code we'll be using can be found &lt;a href="https://github.com/tngeene/two-captcha-solver" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Setup instructions have been linked in the project README file. Feel free to clone the repository and tinker around.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's dive in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automating with Selenium
&lt;/h2&gt;

&lt;p&gt;Before we get carried away by CAPTCHA, we need to understand the process our program will follow. I'm going to use &lt;a href="https://www.selenium.dev/documentation/" rel="noopener noreferrer"&gt;selenium&lt;/a&gt; and node.js. Selenium is a browser automation service that provides extensions that allow programs to emulate browser interactions. It supports an array of browsers; from chrome, firefox, safari, and so forth, by integrating with their web drivers.&lt;/p&gt;

&lt;p&gt;For this tutorial, I'll be using chrome driver since chrome is my default browser. To setup selenium and chromedriver, run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; npm i selenium-webdriver
 npm i chromedriver

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This will write to the package.json file initialized in your node application.&lt;/p&gt;
&lt;h2&gt;
  
  
  Installing 2Captcha Dependencies
&lt;/h2&gt;

&lt;p&gt;Next, we'll need to install 2captcha's node.js package&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i @infosimples/node_two_captcha

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This package will do the heavy lifting in terms of interacting with 2Captcha API.&lt;/p&gt;

&lt;p&gt;Finally, we'll install &lt;code&gt;dotenv&lt;/code&gt; since we'll be having sensitive keys that we need stored in an environment variables file.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i dotenv

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Project Code
&lt;/h2&gt;

&lt;p&gt;Open an &lt;code&gt;index.js&lt;/code&gt; file at the root of your project directory and paste the following code. We'll go over the code in-depth in the next section.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chromedriver&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@infosimples/node_two_captcha&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;By&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;until&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;selenium-webdriver&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CAPTCHA_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;polling&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;throwErrors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initiateCaptchaRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;solving captcha...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;client&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decodeRecaptchaV2&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;googlekey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GOOGLE_CAPTCHA_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;pageurl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WEBSITE_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//   if captcha is solved, launch selenium driver.&lt;/span&gt;
        &lt;span class="nf"&gt;launchSelenium&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//   do something&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;launchSelenium&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Captcha Solved! Launching Browser instance...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;forBrowser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chrome&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// Navigate to Url&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WEBSITE_URL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;By&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;sendKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Ted&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;By&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;phone&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;sendKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;000000000&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;By&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;sendKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tngeene@captcha.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;By&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;comment-content&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;sendKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test comment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;gCaptchResponseInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;By&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;g-recaptcha-response&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;executeScript&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;arguments[0].setAttribute('style','type: text; visibility:visible;');&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;gCaptchResponseInput&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;gCaptchResponseInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;executeScript&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;arguments[0].setAttribute('style','display:none;');&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;gCaptchResponseInput&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;By&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;send-message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// wait 8 seconds and close browser window&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;quit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//   if no text return request time out message&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Request timed out.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;initiateCaptchaRequest&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Importing Packages
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chromedriver&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@infosimples/node_two_captcha&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;By&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;until&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;selenium-webdriver&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;These lines are essentially referencing the packages we'll be using. We're telling the program that we'll require to use chrome driver, dotenv, the node_two_captcha, and selenium packages.&lt;/p&gt;
&lt;h3&gt;
  
  
  Interacting with imported packages
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CAPTCHA_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;polling&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;throwErrors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The first package we need to use is the &lt;code&gt;node_two_captcha&lt;/code&gt; package. The first parameter of the TwoCaptchaClient constructor is your API key from 2Captcha. In our case above, we referenced an environment variable (CAPTCHA_API_KEY). More on this below. The other parameters are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;timeout&lt;/code&gt;: Time (milliseconds) to wait before giving up on waiting for a captcha solution.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;polling&lt;/code&gt;: Time (milliseconds) between polls to 2captcha server. 2Captcha documentation suggests this time to be at least 5 seconds, or you might get blocked.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;throwErrors&lt;/code&gt; : Whether the client should throw errors or just log the errors.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Environment variable.
&lt;/h3&gt;


&lt;div class="ltag__wikipedia--container"&gt;
  &lt;div class="ltag__wikipedia--header"&gt;
    &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fwikipedia-logo-0a3e76624c7b1c3ccdeb9493ea4add6ef5bd82d7e88d102d5ddfd7c981efa2e7.svg" class="ltag__wikipedia--logo" alt="Wikipedia Logo"&gt;
    &lt;a href="https://en.wikipedia.org/wiki/Environment_variable" rel="noopener noreferrer"&gt;Environment variable&lt;/a&gt;
  &lt;/div&gt;
  &lt;div class="ltag__wikipedia--extract"&gt;&lt;p&gt;An &lt;b&gt;environment variable&lt;/b&gt; is a user-definable value that can affect the way running processes will behave on a computer. Environment variables are part of the environment in which a process runs. For example, a running process can query the value of the TEMP environment variable to discover a suitable location to store temporary files, or the HOME or USERPROFILE variable to find the directory structure owned by the user running the process.&lt;/p&gt;&lt;/div&gt;
  &lt;div class="ltag__wikipedia--btn--container"&gt;
      &lt;a class="ltag__wikipedia--btn" href="https://en.wikipedia.org/wiki/Environment_variable" rel="noopener noreferrer"&gt;View on Wikipedia&lt;/a&gt;&amp;gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;It's advisable to use environment variables to store sensitive information that we wouldn't want getting into unauthorized hands, for example, our 2Captcha API key. Create a &lt;code&gt;env&lt;/code&gt; file at the root of your project folder. Modify it to this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; CAPTCHA_API_KEY=&amp;lt;api_key_from_2_captcha.com&amp;gt;
 WEBSITE_URL=&amp;lt;website_we'll_be_accessing_captcha&amp;gt;
 GOOGLE_CAPTCHA_KEY=&amp;lt;google_captcha_site_key&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste the api key value from &lt;a href="https://2captcha.com/?from=12437369" rel="noopener noreferrer"&gt;2captcha&lt;/a&gt; dashboard on the &lt;code&gt;CAPTCHA_API_KEY&lt;/code&gt; field.&lt;/p&gt;

&lt;h2&gt;
  
  
  Captcha Solving Pseudocode
&lt;/h2&gt;

&lt;p&gt;Now that we have our account setup, we'll do the actual capture bypassing, which brings us to the next part.&lt;/p&gt;

&lt;p&gt;For this tutorial, i'll be bypassing the capture on &lt;a href="https://phppot.com/demo/php-contact-form-with-google-recaptcha/" rel="noopener noreferrer"&gt;this sample form&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--F4Bz13C_--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_880%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqkgdk3qldwr178k6rtb0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--F4Bz13C_--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_880%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqkgdk3qldwr178k6rtb0.png" alt="https://res.cloudinary.com/practicaldev/image/fetch/s--F4Bz13C_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qkgdk3qldwr178k6rtb0.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In order to achieve our objective, we'll need our program to follow these steps.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Launch a browser tab&lt;/li&gt;
&lt;li&gt;Fill in the required fields in the form&lt;/li&gt;
&lt;li&gt;Solve the captcha&lt;/li&gt;
&lt;li&gt;Send the contact message with the solved captcha.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All this will be achieved automatically!! How cool is that?&lt;/p&gt;

&lt;h2&gt;
  
  
  Retrieving Dom elements
&lt;/h2&gt;

&lt;p&gt;As you can tell from the form, we will need selenium to access the DOM form elements as it automatically fills the inputs. These are; the name, email, phone, comment, and finally there's a hidden google captcha field that takes the solved captcha as an input.&lt;/p&gt;

&lt;p&gt;For the visible form fields, all we need to do is open up the dev tools and retrieve the individual ids of the form fields.&lt;/p&gt;

&lt;p&gt;This section does just that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;   &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;
    &lt;span class="nc"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;forBrowser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chrome&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WEBSITE_URL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;By&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;sendKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Ted&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;By&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;phone&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;sendKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;000000000&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;By&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;sendKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tngeene@captcha.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;By&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;comment-content&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;sendKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test comment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What we're telling selenium is, to launch chrome browser, and visit the specified url. Once it does so, find the DOM elements that matches the provided ids. Since these are form inputs, auto-populate those field with the data inside the &lt;code&gt;sendKeys&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;All 2captchas have a hidden text-area that autofills with the solved captcha code once you click the &lt;code&gt;i'm not a robot&lt;/code&gt; check. Usually, this has the id set as &lt;code&gt;g-recaptcha-response&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--DRV3vb-q--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_880%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6v3jdf99wtv974jfhffy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--DRV3vb-q--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_880%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6v3jdf99wtv974jfhffy.png" alt="https://res.cloudinary.com/practicaldev/image/fetch/s--DRV3vb-q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6v3jdf99wtv974jfhffy.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since selenium simulates browser human inputs, it'll be required to make the field visible. We achieve this with this code snippet&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;gCaptchResponseInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;By&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;g-recaptcha-response&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;executeScript&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;arguments[0].setAttribute('style','type: text; visibility:visible;');&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;gCaptchResponseInput&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;gCaptchResponseInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;executeScript&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;arguments[0].setAttribute('style','display:none;');&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;gCaptchResponseInput&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;By&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;send-message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This section typically makes the field visible, auto-populates the field with the solved captcha, hides the field again, and finally, the button click is simulated to send the comment with the solved captcha.&lt;/p&gt;

&lt;p&gt;Finally, we'll close the browser tab 8 seconds after the captcha has been solved.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// wait 8 seconds and close browser window&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;quit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All the fore-mentioned functionality resides in the &lt;code&gt;launchSelenium()&lt;/code&gt; function. We need to tie it all together with the 2captcha service.&lt;/p&gt;

&lt;p&gt;From the index.js file, you can see we have a &lt;code&gt;initiateCaptchaRequest()&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initiateCaptchaRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;solving captcha...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;client&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decodeRecaptchaV2&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;googlekey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GOOGLE_CAPTCHA_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;pageurl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WEBSITE_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//   if captcha is solved, launch selenium driver.&lt;/span&gt;
        &lt;span class="nf"&gt;launchSelenium&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//   do something&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are calling the &lt;code&gt;node_two_captcha&lt;/code&gt; client we'd initialized before.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;WEBSITE_URL&lt;/code&gt; is the webpage of our captcha form. Fill it in the &lt;code&gt;.env&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;GOOGLE_CAPTCHA_KEY&lt;/code&gt; is a special identifier found in all web forms having captcha, we can retrieve it by also opening dev tools, and searching the &lt;code&gt;data-sitekey&lt;/code&gt; keyword.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--EWcZ2FeR--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_880%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flp1rxds2suvwbtgrktdn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--EWcZ2FeR--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_880%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flp1rxds2suvwbtgrktdn.png" alt="https://res.cloudinary.com/practicaldev/image/fetch/s--EWcZ2FeR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lp1rxds2suvwbtgrktdn.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Retrieve the value and paste it in the &lt;code&gt;.env&lt;/code&gt;, GOOGLE_CAPTCHA_KEY value. &lt;code&gt;node_two_captcha&lt;/code&gt; sends this key under the hood to 2capthca API, which then returns an API response of the solved captcha.&lt;/p&gt;

&lt;p&gt;Selenium will only be launched upon a successful captcha solve, which usually takes a few seconds. For Recaptcha version 2, the ETA is usually anytime from 15 seconds to 45seconds. Recaptcha version 3 takes a shorter time.If a request timed out, we log the api response.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Okay, now your application is set up! It may feel like a lot 😅 but we did a lot of installation. We will now test our application.&lt;/p&gt;

&lt;p&gt;To do this, run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm index.js

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--wo6M1CbL--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_66%252Cw_880%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl2rzq2jqq3zbyu8z86pd.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--wo6M1CbL--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_66%252Cw_880%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl2rzq2jqq3zbyu8z86pd.gif" alt="https://res.cloudinary.com/practicaldev/image/fetch/s--wo6M1CbL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l2rzq2jqq3zbyu8z86pd.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;It’s always a dilemma, should websites have a better experience and have simple to bypass the CAPTCHA or should websites aggressively protect themselves from bots and have a bad user experience. The war between websites and bots is never over. Whatever verification method websites pull out, it’s just a matter of time when someone figures out how to bypass it. ~ Filip Vitas&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this guide, we were introduced to 2captcha API, selenium, and a few concepts in 2captcha. By the end of it, I hope you can apply the knowledge gained to build your own captcha bypass service. I mean, if bots can do it, then so should we! A few next steps would be to add a User interface to input our values. You can also look into using the 2captcha API using your preferred programming language and other tools such as &lt;a href="https://pptr.dev/" rel="noopener noreferrer"&gt;puppeteer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, if you liked the content and would like to use 2captcha, &lt;a href="https://2captcha.com/?from=12437369" rel="noopener noreferrer"&gt;sign up with this link.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you have any questions, you can always leave a comment below, or reach out on these channels;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://tngeene.com/" rel="noopener noreferrer"&gt;personal website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/Ngeene_kihiu" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Source code of demo project can be accessed &lt;a href="https://github.com/tngeene/two-captcha-solver" rel="noopener noreferrer"&gt;here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use 2captcha responsibly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sponsors
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.scraperapi.com?via=teddy44" rel="noopener noreferrer"&gt;Scraper API&lt;/a&gt; is a startup specializing in strategies that'll ease the worry of your IP address from being blocked while web scraping. They utilize IP rotation so you can avoid detection. Boasting over 20 million IP addresses and unlimited bandwidth. Using Scraper API and a tool like &lt;a href="https://2captcha.com?from=12437369" rel="noopener noreferrer"&gt;2captcha&lt;/a&gt; will give you an edge over other developers. The two can be used together to automate processes. Sign up on Scraper API and use &lt;a href="https://www.scraperapi.com?via=teddy44" rel="noopener noreferrer"&gt;this link&lt;/a&gt; to get a 10% discount on your first purchase.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Do you need a place to host your website or app, &lt;a href="//DigitalOcean%20%E2%80%93%20The%20developer%20cloudwww.digitalocean.com"&gt;Digital ocean&lt;/a&gt;&lt;br&gt;
is just the solution you need, sign up on digital ocean using this &lt;a href="https://m.do.co/c/eaa803fe4d99" rel="noopener noreferrer"&gt;link&lt;/a&gt; and experience the best cloud service provider.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The journey to becoming a developer can be long and tormentous, luckily &lt;a href="http://referral.pluralsight.com/mQh0Nxp" rel="noopener noreferrer"&gt;Pluralsight&lt;/a&gt; makes it easier to learn. They offer a wide range of courses, with top quality trainers, whom I can personally vouch for. Sign up using &lt;a href="http://referral.pluralsight.com/mQh0Nxp" rel="noopener noreferrer"&gt;this link&lt;/a&gt; and get a 50% discount on your first course.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>automation</category>
      <category>captcha</category>
      <category>node</category>
    </item>
    <item>
      <title>AdonisJs - Understanding User Registration and Authentication</title>
      <dc:creator>Ted Ngeene</dc:creator>
      <pubDate>Sun, 05 Sep 2021 19:47:01 +0000</pubDate>
      <link>https://forem.com/tngeene/adonisjs-understanding-user-registration-and-authentication-2ojl</link>
      <guid>https://forem.com/tngeene/adonisjs-understanding-user-registration-and-authentication-2ojl</guid>
      <description>&lt;p&gt;In this third installment of the &lt;a href="https://dev.to/tngeene/series/14084"&gt;Everything, you need to know about AdonisJs series&lt;/a&gt;. we'll go over the basic setup of database models, using the user model. We'll also configure our registration and login controllers(authentication). Finally, I'll show you how to handle routing for endpoints.&lt;br&gt;
This article will also briefly introduce you to basic lifecycle hooks in Adonis. Let's dive in.&lt;/p&gt;
&lt;h2&gt;
  
  
  Definitions
&lt;/h2&gt;

&lt;p&gt;Authentication is the process of verifying who a user is, for example by making them enter a password.&lt;/p&gt;

&lt;p&gt;If you're not familiar with the concept of database models, the following description succinctly defines it.&lt;/p&gt;


&lt;div class="ltag__wikipedia--container"&gt;
  &lt;div class="ltag__wikipedia--header"&gt;
    &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fwikipedia-logo-0a3e76624c7b1c3ccdeb9493ea4add6ef5bd82d7e88d102d5ddfd7c981efa2e7.svg" class="ltag__wikipedia--logo" alt="Wikipedia Logo"&gt;
    &lt;a href="https://en.wikipedia.org/wiki/Database_model" rel="noopener noreferrer"&gt;Database model&lt;/a&gt;
  &lt;/div&gt;
  &lt;div class="ltag__wikipedia--extract"&gt;&lt;p&gt;A &lt;b&gt;database model&lt;/b&gt; is a type of data model that determines the logical structure of a database. It fundamentally determines in which manner data can be stored, organized and manipulated. The most popular example of a database model is the relational model, which uses a table-based format.&lt;/p&gt;&lt;/div&gt;
  &lt;div class="ltag__wikipedia--btn--container"&gt;
      &lt;a class="ltag__wikipedia--btn" href="https://en.wikipedia.org/wiki/Database_model" rel="noopener noreferrer"&gt;View on Wikipedia&lt;/a&gt;&amp;gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The model is essentially the data that will be manipulated in the system, it has attributes and relationships with other models.&lt;/p&gt;

&lt;p&gt;Routes allow us to make HTTP requests to our application. The entry point for all Adonis routes is located in the &lt;code&gt;start/routes.ts&lt;/code&gt; file. You can define all routes in this file or other files and import them into this file as we will do. For more detailed info on Adonis routes, head over to their &lt;a href="https://docs.adonisjs.com/guides/routing#default-routes-file" rel="noopener noreferrer"&gt;official documentation.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  HTTP methods
&lt;/h2&gt;

&lt;p&gt;In most cases, you'll need your application to perform some business logic. This is where HTTP methods come in, these allow us to perform some actions on our models. Common HTTP methods include.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GET - Used to fetch data from a specified resource.&lt;/li&gt;
&lt;li&gt;POST - Used to store new data or dispatch data to the server.&lt;/li&gt;
&lt;li&gt;PUT/PATCH - Used to update existing data.&lt;/li&gt;
&lt;li&gt;DELETE - Used to delete existing data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, controllers are files that all logic on the program that will be performed. A controller determines what response to send back to a user when a user makes a browser request. For example, we can have an &lt;code&gt;authController&lt;/code&gt; that will handle all authentication logic.&lt;/p&gt;

&lt;p&gt;Routes are tied down to controller functions. They are URL patterns that are tied down to a handler function, in this case, a function in a controller. Using the example above, we could have a &lt;code&gt;login&lt;/code&gt; route mapping to a function in the auth controller.&lt;/p&gt;

&lt;p&gt;From the above definitions, it's pretty clear that we're covering the &lt;code&gt;MC&lt;/code&gt; in the &lt;code&gt;MVC&lt;/code&gt; pattern, that is, the model and the controller.&lt;/p&gt;

&lt;p&gt;Now we can actually get our hands dirty in setting up the user model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up the user model
&lt;/h2&gt;

&lt;p&gt;A cool thing with Adonis is that it has a neat package called &lt;code&gt;adonisjs/auth&lt;/code&gt; which handles authentication. It leverages the fully-fledged in-built authentication system of Adonis.&lt;br&gt;
We'll begin by installing the package; &lt;code&gt;npm i @adonisjs/auth&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;After successful installation, as earlier mentioned on the configuration of packages, we configure the package settings by running &lt;code&gt;node ace configure @adonis/auth&lt;/code&gt;&lt;br&gt;
This will lead the cli to prompt some questions. For my configuration, I followed the steps below.&lt;/p&gt;

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

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

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

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

&lt;p&gt;If the configuration was successful, you'll notice that some new files will be added to the file tree of your application.&lt;/p&gt;

&lt;p&gt;These are the user migrations and user model files. The package creates a basic user model which we can modify depending on the use case.&lt;br&gt;
You'll also notice that for this particular configuration, since I decided to use API token guard, then a separate migration file for API tokens has also been created.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;The configuration for the auth package is stored inside the config/auth.ts file. Inside this file, you can define one or more guards to authenticate users. If you'd like to use a combination of API tokens and sessions, for example, all you'd need to invoke the node ace configure @adonisjs/auth command and add the guard you'd like to use. This will then append to the config/auth.ts file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Modifying the user migration
&lt;/h3&gt;

&lt;p&gt;Every application's user model is different. The basic model provided makes a general assumption of the common user attributes of most systems, however, to modify it to our use-case, we need to open the &lt;code&gt;database/migrations/....users.ts&lt;/code&gt; file. Don't mind the digits.&lt;/p&gt;

&lt;p&gt;For our application, the user table will need to look like this&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;


&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;BaseSchema&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ioc:Adonis/Lucid/Schema&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UsersSchema&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BaseSchema&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nx"&gt;tableName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;up&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tableName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;notNullable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;username&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;notNullable&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;avatar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;nullable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dateTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email_verified_at&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;nullable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;is_activated&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;notNullable&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;defaultTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;notNullable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;remember_me_token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;nullable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

      &lt;span class="cm"&gt;/**
       * Uses timestampz for PostgreSQL and DATETIME2 for MSSQL
       */&lt;/span&gt;
      &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;created_at&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;useTz&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;notNullable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;updated_at&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;useTz&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;notNullable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

      &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;username&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;down&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dropTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tableName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;From the configuration above, we can see which fields we'll need our users to have. Over and above their inputs, we'll require that users verify their accounts. This will prevent bots from using our system. The implementation for this will be covered in the next section.&lt;/p&gt;

&lt;p&gt;We'll also need to index some fields, which adonis provides. All we have to do is indicate which fields we'd like to be indexed.&lt;br&gt;
For those of you not familiar with the concept of database indexing, head over to &lt;a href="https://www.guru99.com/indexing-in-database.html" rel="noopener noreferrer"&gt;this definition.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, it's time to migrate the data&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

node ace migration:run



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If you got a successful migration, you'll see this on the command line.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;It's worth noting that you might experience an error in migration related to phc-argon not being installed as a dependency. If you come across this, install this library by running npm i phc-argon2, then try running the migration again.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Modify the user model
&lt;/h3&gt;

&lt;p&gt;In most cases, we'll have separate model files for each table in our database. These model files describe the columns to lucid. They also contain relationship definitions, lifecycle hooks, computed properties, serialization behavior, and query scopes. We'll dig into this at a later time.&lt;/p&gt;

&lt;p&gt;Under the &lt;code&gt;app/models&lt;/code&gt; directory, open the &lt;code&gt;User.ts&lt;/code&gt;. We'll adjust it to this format.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;


&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DateTime&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;luxon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Hash&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ioc:Adonis/Core/Hash&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;column&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;beforeSave&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;BaseModel&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ioc:Adonis/Lucid/Orm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BaseModel&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;column&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;isPrimary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;column&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;column&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;column&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;column&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;isActivated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;column&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dateTime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;email_verified_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DateTime&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;column&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;serializeAs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;column&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dateTime&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;autoCreate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DateTime&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;column&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dateTime&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;autoCreate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;autoUpdate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;updatedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DateTime&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;beforeSave&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;hashPassword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$dirty&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The code above is quite self-explanatory; it defines all the fields that we'd need our user model to have. However, at this point, I'd like to mention on the last bit&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;beforeSave&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kr"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;hashPassword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$dirty&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This is a brief introduction to adonis lifecycle hooks. What this hook does is essentially encrypt user passwords using a hashing algorithm. This operation is performed right before a user is saved into the database, hence the &lt;code&gt;beforeSave()&lt;/code&gt; function. We wouldn't want to store user passwords as raw texts. You can perform other lifecycle operations using any of these hooks in adonis&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

beforeSave(), beforeCreate(), beforeUpdate(), beforeDestroy(), beforeFind(), afterFind(),beforeFetch(), afterFetch(), beforePaginate(), afterPaginate()



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Creating our Auth Controller
&lt;/h2&gt;

&lt;p&gt;For the next step, we will make a controller that will handle all user authentication. We do this by running&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

node ace make:controller Users/AuthController



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You'll notice that a new directory has been created under the &lt;code&gt;app/Controllers/Http&lt;/code&gt;.&lt;br&gt;
Open up the AuthController file and paste the following code.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;


&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;HttpContextContract&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ioc:Adonis/Core/HttpContext&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;App/Models/User&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ioc:Adonis/Core/Validator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AuthController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;HttpContextContract&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// validate email&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;({},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;email&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;column&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})]),&lt;/span&gt;
      &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;({},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;confirmed&lt;/span&gt;&lt;span class="p"&gt;()]),&lt;/span&gt;
      &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;({},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;column&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;username&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})]),&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;validations&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;created&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;//   login function&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;HttpContextContract&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;expiresIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;24hours&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toJSON&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User with provided credentials could not be found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;//   logout function&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;logout&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;HttpContextContract&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logout&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;So what does the code above do?&lt;/p&gt;

&lt;h3&gt;
  
  
  Registration
&lt;/h3&gt;

&lt;p&gt;There are three functions within this controller;&lt;br&gt;
The first one being the &lt;code&gt;registration&lt;/code&gt; of users.&lt;/p&gt;

&lt;p&gt;We have some validators that ensure that the data being input meets certain requirements, in our case, the email and username fields should be unique. The password field should also be entered twice, that is, have a &lt;code&gt;password confirmation&lt;/code&gt; field that matches the password.&lt;br&gt;
If the user input meets the set validations, then the system creates a record of the user in the database.&lt;/p&gt;

&lt;h3&gt;
  
  
  Login
&lt;/h3&gt;

&lt;p&gt;The login functionality of our application will handle the authorization of users. We'll require that users enter an email and password. If the two match against a user in the database, then we return an API token that gives the user access to our system.&lt;br&gt;
This token will validate all requests from the user and will only be valid for 24 hours.&lt;br&gt;
In a case where the user enters the wrong credentials, the system will throw an error with an appropriate response message.&lt;/p&gt;

&lt;h3&gt;
  
  
  Logout
&lt;/h3&gt;

&lt;p&gt;Finally, we will need users to also be able to log out when they need to. The logout() function helps us achieve this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining User Routes
&lt;/h2&gt;

&lt;p&gt;Next, navigate to the start directory and create a new directory called routes, under it make a file named &lt;code&gt;users.ts&lt;/code&gt; . Therefore your start directory should be looking like &lt;code&gt;start/routes/users.ts&lt;/code&gt;. Paste the following;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;


&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ioc:Adonis/Core/Route&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// registration logic&lt;/span&gt;
  &lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;register&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Users/AuthController.register&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;register&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Users/AuthController.login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;logout&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Users/AuthController.logout&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;logout&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api/v1/users/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The above defines the user-related URLs that our application will be having.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;prefix&lt;/code&gt; keyword means that all URLs within the Route group will be prepended with the &lt;code&gt;api/v1/users&lt;/code&gt; pattern.&lt;/p&gt;

&lt;p&gt;For now, all the routes use &lt;code&gt;POST&lt;/code&gt; requests, but not to worry, we'll see how to use other HTTP methods in upcoming articles.&lt;/p&gt;

&lt;p&gt;I'll take a dive into its functionality, but before then we need to inject the user routes into the entry point of all routes for our application. This is the &lt;code&gt;start/routes.ts&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Open the file and modify it such that its contents are like this;&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="n"&gt;import&lt;/span&gt; &lt;span class="no"&gt;HealthCheck&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;'@ioc:Adonis/Core/HealthCheck'&lt;/span&gt;&lt;br&gt;
&lt;span class="n"&gt;import&lt;/span&gt; &lt;span class="no"&gt;Route&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;'@ioc:Adonis/Core/Route'&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="n"&gt;import&lt;/span&gt; &lt;span class="s1"&gt;'./routes/users.ts'&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="sr"&gt;//&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;br&gt;
&lt;span class="no"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'health'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;async&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
  &lt;span class="n"&gt;const&lt;/span&gt; &lt;span class="n"&gt;report&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;await&lt;/span&gt; &lt;span class="no"&gt;HealthCheck&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getReport&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;healthy&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;badRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br&gt;
&lt;span class="p"&gt;})&lt;/span&gt;&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Testing&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;We will be using postman for testing, for my setup, I've made a collection and added a global URL variable called &lt;code&gt;BASE_DEV_API_URL&lt;/code&gt;, which is basically, &lt;code&gt;http://localhost:3333/api&lt;/code&gt;&lt;br&gt;
Next, I've added the three requests we've just created above and tested them out. We'll cover different scenarios, using different inputs.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User registration&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Successful registration
&lt;/h3&gt;

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

&lt;h3&gt;
  
  
  Unique fail for email and username
&lt;/h3&gt;

&lt;p&gt;This error will occur when an email and username fail to meet the uniqueness validator.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Password Confirmation Missing
&lt;/h3&gt;

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

&lt;ol&gt;
&lt;li&gt;Login&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Successful Login
&lt;/h3&gt;

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

&lt;h3&gt;
  
  
  Wrong login credentials
&lt;/h3&gt;

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

&lt;ol&gt;
&lt;li&gt;Logout 
For the logout functionality, copy the bearer token of a logged in user and pass it as an authorization type of &lt;code&gt;Bearer token&lt;/code&gt; under the authorization tab in Postman. 
Hit the &lt;code&gt;http:/localhost:3333/api/v1/users/logout&lt;/code&gt; url. 
The result, if successful should be a status 200.&lt;/li&gt;
&lt;/ol&gt;

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

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

&lt;p&gt;Congratulations! You've made it to the end of the tutorial. I hope you're following along just fine. In this article, we've learnt to setup an authentication scheme in Adonis, gotten introduced to controllers and validators and finally, done some basic HTTP routing. &lt;/p&gt;

&lt;p&gt;In case of any query, feel free to shoot a DM or comment on the post below.&lt;/p&gt;

&lt;p&gt;All the source code of the above application can be found &lt;a href="https://github.com/tngeene/adonis-tutorial-fitit" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the next part of the series, we'll cover Relationships, by setting up more models. See you on the next piece!&lt;/p&gt;

</description>
      <category>adonisjs</category>
      <category>node</category>
      <category>javascript</category>
      <category>database</category>
    </item>
    <item>
      <title>AdonisJs - Installation and Database Setup</title>
      <dc:creator>Ted Ngeene</dc:creator>
      <pubDate>Mon, 16 Aug 2021 08:58:44 +0000</pubDate>
      <link>https://forem.com/tngeene/part-2-installation-and-database-setup-3o0n</link>
      <guid>https://forem.com/tngeene/part-2-installation-and-database-setup-3o0n</guid>
      <description>&lt;p&gt;Now that you have all the requirements we need to be installed, let's set up the project.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you're having any problems setting up or installing the dependencies, feel free to leave a comment or shoot me a DM.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What we'll be covering in this post.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creating a new project&lt;/li&gt;
&lt;li&gt;Adonis Project Structures&lt;/li&gt;
&lt;li&gt;IDE Setup&lt;/li&gt;
&lt;li&gt;Starting the Development Server&lt;/li&gt;
&lt;li&gt;Database Setup&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Create a new project
&lt;/h2&gt;

&lt;p&gt;To initialize a new project, all we need is a simple command on the terminal.&lt;br&gt;
Simply navigate to the directory you'd like the project to be located and type the following;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm init adonis-ts-app fitit&lt;/code&gt; or &lt;code&gt;yarn create adonis-ts-app fitit&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Let's break down this command for context.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;npm init&lt;/code&gt; - this is simply initializing the project. Since we're using npm as the package manager, we use &lt;code&gt;npm&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;adonis-ts-app&lt;/code&gt; - we're initializing the project as a typescript project. If you wish not to use typescript and stick to vanilla JavaScript, you can use the &lt;code&gt;adonis new fitit&lt;/code&gt;. This would involve installing the adonis cli. For more details on this, head over to &lt;a href="https://legacy.adonisjs.com/docs/3.2/adonis-blog-part1#_creating_new_application" rel="noopener noreferrer"&gt;this link.&lt;/a&gt; However, I recommend using the typescript version.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fitit&lt;/code&gt; - this is the name of the application.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In general, the general structure of creating a new Adonis application is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm init adonis-ts-app &amp;lt;project_name&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After you've typed the project initialization command, npm first installs the necessary adonis packages, which then prompts you to choose a preferred project structure.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Adonis Project structures
&lt;/h2&gt;

&lt;p&gt;You can choose between one of the following project structures.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;web project&lt;/code&gt; structure is ideal for creating classic server-rendered applications. We configure the support for sessions and also install the AdonisJS template engine(edge). Also, the support for serving static files like images is provided.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;api project&lt;/code&gt; structure is ideal for creating an API server. This also configures the support for CORS.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;slim project&lt;/code&gt; structure creates the smallest possible AdonisJS application and does not install any additional packages, except the framework core. This sort of allows for flexibility. It's up to the developer to build the application ground-up.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's worth noting that apart from some minor differences, the overall project structure is almost similar.&lt;/p&gt;

&lt;p&gt;For this project, we'll be using the &lt;code&gt;api project&lt;/code&gt; structure. The reason for this is that I'm mostly going to be using Adonis as an API server, which I'll then hook to a vue.js frontend.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you're using vs code, the command prompt will ask for additional configurations. These are prettier and eslint-related. I advise using both formatters.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After the project has been created, you'll get a message like this on the terminal.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Vs Code Setup
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;This section is intended for anyone using vs code as their IDE. If you use any other IDE, you can skip over to the next section.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Some extensions will make your adonis development easier in vs code. These are;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode" rel="noopener noreferrer"&gt;Prettier&lt;/a&gt; and &lt;a href="https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint" rel="noopener noreferrer"&gt;eslint&lt;/a&gt; - As I'd mentioned earlier you need to have prettier and eslint installed. This will be set up during the installation process.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode.vscode-typescript-next" rel="noopener noreferrer"&gt;JavaScript and TypeScript Nightly&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://marketplace.visualstudio.com/items?itemName=hridoy.adonisjs-snippets" rel="noopener noreferrer"&gt;Adonis JS Snippets&lt;/a&gt; - provides insightful snippets.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://marketplace.visualstudio.com/items?itemName=stef-k.adonis-js-goto-controller" rel="noopener noreferrer"&gt;Adonis Js Go to controller&lt;/a&gt; will come in handy when we start working with routes.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Starting the Development server
&lt;/h2&gt;

&lt;p&gt;Navigate to the project directory and open it on your IDE now that the project has been created.&lt;br&gt;
Run the following command to spin up your local development server&lt;/p&gt;

&lt;p&gt;&lt;code&gt;node ace serve --watch&lt;/code&gt;&lt;/p&gt;

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

&lt;p&gt;Open up your browser, and visit &lt;code&gt;localhost:3333&lt;/code&gt;. If you did everything right, then you should see a screen with &lt;code&gt;hello world&lt;/code&gt; text. Congratulations on setting up your first Adonis project! 🎉🎊&lt;/p&gt;

&lt;p&gt;From here, things start to get interesting, we'll set up the database but before we head there, you might be wondering what &lt;code&gt;node ace serve --watch&lt;/code&gt; is.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ace&lt;/code&gt; is a command-line framework that is embedded within your app. It allows you to create project-specific commands and run them using &lt;code&gt;node ace&lt;/code&gt;. We'll be using ace commands throughout the tutorial, each with it's own purpose. For a more detailed explanation on ace commands, head over to this &lt;a href="https://dev.to/amanvirk1/introducing-adonisjs-part-3-65k"&gt;article&lt;/a&gt; by &lt;a class="mentioned-user" href="https://dev.to/amanvirk1"&gt;@amanvirk1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the above command, let's break down what it does.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;serve&lt;/code&gt; command starts the HTTP server and performs an in-memory compilation of TypeScript to JavaScript.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;-watch&lt;/code&gt; flag is meant to watch the file system for changes and restart the server automatically(hot reload).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Database Setup
&lt;/h2&gt;

&lt;p&gt;Before we wind up, let's connect our application to a database.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I'll assume that you have some knowledge of setting up Postgres or SQL databases and already have one on your computer. If this is not the case, you can use sqlite, which will be a file created on your machine once we start defining the database configuration.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;AdonisJS has first class support for SQL databases. The data layer of the framework is powered by Lucid(AdonisJs ORM) and the package must be installed separately.&lt;/p&gt;

&lt;p&gt;Simply run &lt;code&gt;npm i @adonisjs/lucid&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Upon successful installation, we'll configure our database driver and create a database. For this tutorial, I'll be using postgres. So my configs will be postgres-related. However, if you're using SQL, then use relevant configuration.&lt;br&gt;
If you choose sqlite, no configuration will be required.&lt;/p&gt;

&lt;p&gt;Once done, run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node ace configure @adonisjs/lucid

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node ace invoke @adonisjs/lucid

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;The ace configure and invoke commands executes the instructions Javascript file exposed by the package.&lt;/p&gt;

&lt;p&gt;Since I decided to go with postgres, I'll copy the code for validating the environment variables to the env.ts file.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;Environment variables are injected from outside. The env.ts file validates that they type match and that the app is always running with the correct set of configuration values.&lt;br&gt;
Your env.ts file should now look like this.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Env from '@ioc:Adonis/Core/Env'

export default Env.rules({
  HOST: Env.schema.string({ format: 'host' }),
  PORT: Env.schema.number(),
  APP_KEY: Env.schema.string(),
  APP_NAME: Env.schema.string(),
  PG_HOST: Env.schema.string({ format: 'host' }),
  PG_PORT: Env.schema.number(),
  PG_USER: Env.schema.string(),
  PG_PASSWORD: Env.schema.string.optional(),
  PG_DB_NAME: Env.schema.string(),
  NODE_ENV: Env.schema.enum(['development', 'production', 'testing'] as const),
})

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;config/database.ts&lt;/code&gt; file holds all the configuration related to the database. For more details on this, check out the &lt;a href="https://docs.adonisjs.com/guides/database/introduction" rel="noopener noreferrer"&gt;documentation.&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next, we'll configure our database on postgres.&lt;/p&gt;

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

&lt;p&gt;You can name your database whatever name you wish.&lt;/p&gt;

&lt;p&gt;Finally, go to your &lt;code&gt;.env&lt;/code&gt; file and modify these values&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
DB_CONNECTION=pg
PG_HOST=localhost
PG_PORT=5432
PG_USER=postgres
PG_PASSWORD=&amp;lt;your_postgres_password&amp;gt;
PG_DB_NAME=&amp;lt;your_db_name&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing Database connection.
&lt;/h2&gt;

&lt;p&gt;Adonis comes with a neat health checker that checks if the database connection is working.&lt;/p&gt;

&lt;p&gt;Navigate to &lt;code&gt;start/routes.ts&lt;/code&gt; and paste the following code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
import HealthCheck from '@ioc:Adonis/Core/HealthCheck'
import Route from '@ioc:Adonis/Core/Route'

// check db connection
Route.get('health', async ({ response }) =&amp;gt; {
  const report = await HealthCheck.getReport()

  return report.healthy ? response.ok(report) : response.badRequest(report)
})

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With your server still running, open a browser tab and type &lt;code&gt;localhost:3333/health&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If everything worked fine and your database is connected, it should display the following screen.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;Thank you for following along, if you like the content and would like to know more about Adonis Development, bookmark this series, head over to my &lt;a href="https://tngeene.com/" rel="noopener noreferrer"&gt;personal website&lt;/a&gt; or follow me on &lt;a href="https://twitter.com/Ngeene_kihiu" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;. You can also leave a comment in case you need any clarification or would like to point out an addition.&lt;/p&gt;

&lt;p&gt;For the next piece, I'll be covering database models and relationships.&lt;/p&gt;

&lt;p&gt;You can also follow the series on my &lt;a href="https://tngeene.com/blog/series/everything-you-need-to-know-about-adonisjs" rel="noopener noreferrer"&gt;personal website.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Stay tuned!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>adonis</category>
      <category>typescript</category>
      <category>node</category>
    </item>
    <item>
      <title>AdonisJs - Introduction </title>
      <dc:creator>Ted Ngeene</dc:creator>
      <pubDate>Thu, 12 Aug 2021 19:43:32 +0000</pubDate>
      <link>https://forem.com/tngeene/introduction-to-adonisjs-4a1f</link>
      <guid>https://forem.com/tngeene/introduction-to-adonisjs-4a1f</guid>
      <description>&lt;p&gt;When we talk about modern software development, it's hard to overlook the role of JavaScript. With so many frameworks to choose from, choosing the 'perfect one is often a conundrum that many newbie developers and experienced ones face when they want to learn new skills. In this series, I will highlight why you should go with &lt;a href="[https://adonisjs.com/](https://adonisjs.com/)"&gt;AdonisJs&lt;/a&gt;. By the end of the tutorial, I believe you will be able to handle most, if not all of the common tasks involved in any backend application.&lt;/p&gt;

&lt;p&gt;All source code of the application we'll be building can be found &lt;a href="https://github.com/tngeene/adonis-tutorial-fitit" rel="noopener noreferrer"&gt;here.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Table Of Content
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;What is AdonisJs?&lt;/li&gt;
&lt;li&gt;Why Use AdonisJs?&lt;/li&gt;
&lt;li&gt;Series Outline&lt;/li&gt;
&lt;li&gt;Prerequisites&lt;/li&gt;
&lt;li&gt;Communities and Resources in AdonisJs&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What is AdonisJs?
&lt;/h2&gt;

&lt;p&gt;Some of you are wondering, "what in the world is AdonisJs in the first place?". AdonisJs describes itself as, "a backend framework for Node.js." However, you can do both back-end and front-end development using the framework. It follows the &lt;a href="https://www.tutorialspoint.com/mvc_framework/mvc_framework_introduction.htm" rel="noopener noreferrer"&gt;Model View Controller(MVC)&lt;/a&gt; architecture, therefore, you can create fully functional web apps using a single framework. It has its own templating engine called edge. It was created by &lt;a href="https://dev.to/amanvirk1"&gt;Aman Virk&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why AdonisJs
&lt;/h2&gt;

&lt;p&gt;A couple of weeks ago, I started using the framework. Being torn between express and Adonis, I finally swayed the Adonis way. The reason for this is;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It has a robust &lt;a href="https://en.wikipedia.org/wiki/Object%E2%80%93relational_mapping" rel="noopener noreferrer"&gt;Object Relational Mapper(ORM)&lt;/a&gt;, which provides first-class support for SQL databases, Query builder with active records, seeds, migrations, and Redis support. This to me was the biggest factor in choosing it as I didn't have to go through the extra step of manually configuring the ORM.&lt;/li&gt;
&lt;li&gt;Typescript support outside the box🎊&lt;/li&gt;
&lt;li&gt;Well-written and thorough &lt;a href="https://docs.adonisjs.com/guides/introduction" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;. You can build a web app by just using the documentation.&lt;/li&gt;
&lt;li&gt;Multi driver auth support, which lets you choose between JWT, session, and opaque API tokens.&lt;/li&gt;
&lt;li&gt;It is easy to set up and use.&lt;/li&gt;
&lt;li&gt;It follows good design patterns on what should encompass a web framework.&lt;/li&gt;
&lt;li&gt;Strong emphasis on web security&lt;/li&gt;
&lt;li&gt;A growing community. I personally see Adonis cementing itself as a top backend framework in the next couple of years.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I could go on and on about why I like the framework, but for that, I'll link some communities at the end of the post for you to have a look at the discussions around it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Outline
&lt;/h2&gt;

&lt;p&gt;Before we make any development I'll highlight what we'll be building and the requirements you'll need to set up an Adonis project.&lt;/p&gt;

&lt;p&gt;We're going to be making a sports apparel store, called FitIt. The goal of the series is to highlight a couple of concepts that are important in your journey to becoming a top-notch Adonis developer. The store will be multi-tenant, meaning users can own different stores and post their gear for that particular shop.&lt;/p&gt;

&lt;p&gt;The Database model is illustrated below;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--e9nyVRxq--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_880%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpzn7uhf6fh7om3u017g5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fpracticaldev%2Fimage%2Ffetch%2Fs--e9nyVRxq--%2Fc_limit%252Cf_auto%252Cfl_progressive%252Cq_auto%252Cw_880%2Fhttps%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpzn7uhf6fh7om3u017g5.png" alt="https://res.cloudinary.com/practicaldev/image/fetch/s--e9nyVRxq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pzn7uhf6fh7om3u017g5.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Topics
&lt;/h2&gt;

&lt;p&gt;These are the features we'll be going over, which are essential to understand while working on any project.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Installation and setup(using typescript)&lt;/li&gt;
&lt;li&gt;Login and registration

&lt;ul&gt;
&lt;li&gt;Different authentication schemes&lt;/li&gt;
&lt;li&gt;Account activation&lt;/li&gt;
&lt;li&gt;social signup&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Relationships

&lt;ul&gt;
&lt;li&gt;one to many&lt;/li&gt;
&lt;li&gt;many to many&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;CRUD operations

&lt;ul&gt;
&lt;li&gt;Pagination&lt;/li&gt;
&lt;li&gt;Filters&lt;/li&gt;
&lt;li&gt;File uploads&lt;/li&gt;
&lt;li&gt;Validators&lt;/li&gt;
&lt;li&gt;Success and error messages&lt;/li&gt;
&lt;li&gt;Slug system&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Lifecycle hooks&lt;/li&gt;
&lt;li&gt;Events in AdonisJs&lt;/li&gt;
&lt;li&gt;Mailing&lt;/li&gt;
&lt;li&gt;Database Seeders&lt;/li&gt;
&lt;li&gt;Hook up to vue.js frontend.&lt;/li&gt;
&lt;li&gt;Deployment.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://nodejs.org/en/" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; - AdonisJS is a Node.js framework and hence it requires Node.js to be installed on your computer. To be precise, we require at least the latest release of Node.js 14.&lt;/li&gt;
&lt;li&gt;A package manager. I will be using &lt;a href="https://www.npmjs.com/" rel="noopener noreferrer"&gt;npm&lt;/a&gt; However, you can use &lt;a href="https://yarnpkg.com/" rel="noopener noreferrer"&gt;yarn.&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A code editor. My go-to editor is &lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;Visual Studio Code.&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.postman.com/" rel="noopener noreferrer"&gt;Postman&lt;/a&gt; for API testing.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Communities and Resources
&lt;/h2&gt;

&lt;p&gt;If you've made it this far, then I'm sure I've picked your interest in learning this framework. AdonisJs has a growing community, where you'll get assistance in your journey. For more info on the framework, you can check out;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://discord.com/invite/vDcEjq6" rel="noopener noreferrer"&gt;AdonisJS Discord&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/adonisframework?lang=en" rel="noopener noreferrer"&gt;Twitter account&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/adonisjs/core" rel="noopener noreferrer"&gt;Official Github Repo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://adonisjs.com/" rel="noopener noreferrer"&gt;official Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can also follow me on dev as well as check out my &lt;a href="https://tngeene.com/" rel="noopener noreferrer"&gt;personal website&lt;/a&gt; where I'll be writing more on Adonis and full stack development.&lt;/p&gt;

</description>
      <category>adonisjs</category>
      <category>typescript</category>
      <category>javascript</category>
      <category>node</category>
    </item>
    <item>
      <title>Handy Git Commands and practices to improve your workflow</title>
      <dc:creator>Ted Ngeene</dc:creator>
      <pubDate>Sat, 16 Jan 2021 07:57:07 +0000</pubDate>
      <link>https://forem.com/tngeene/handy-git-commands-and-practices-to-improve-your-workflow-4j5p</link>
      <guid>https://forem.com/tngeene/handy-git-commands-and-practices-to-improve-your-workflow-4j5p</guid>
      <description>&lt;p&gt;I have been in active software development for the better part of the last year and a half. For the majority of that period, I've come to appreciate the power of git, especially since the onset of the pandemic, where I've been working remotely 90% of the time, the technology has been very crucial in the development of my career. &lt;br&gt;
The aim of this post is to highlight some of the common git commands that I use on a daily, which I'm sure most of you reading this will apply at some point or do also apply. The post is also meant to deconstruct the notion that git is complicated. Hopefully, you'll gain new insights into it. Let's dive in...&lt;/p&gt;

&lt;p&gt;This article is divided into four parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Definition of terms&lt;/li&gt;
&lt;li&gt;Commands&lt;/li&gt;
&lt;li&gt;Best Practices&lt;/li&gt;
&lt;li&gt;Resources&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;*Disclosure: I only recommend products I would use myself and all opinions expressed here are my own. This post may contain affiliate links that at no additional cost to you, I may earn a small commission. &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;
  
  
  Definition of terms
&lt;/h1&gt;

&lt;p&gt;Before we move to the commands, it's important to familiarise ourselves with common terms used in the git world. &lt;br&gt;
Let's have a look at some of them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://git-scm.com/" rel="noopener noreferrer"&gt;Git&lt;/a&gt; - it is a distributed &lt;a href="https://en.wikipedia.org/wiki/Distributed_version_control" rel="noopener noreferrer"&gt;version control&lt;/a&gt; system for tracking any set of files, for coordination among developers. Basically put, it is like a savings program for your project. It enables tracking and logging the changes you make to your file or file sets over time, a version-control system gives you the power to review or even restore earlier versions by taking snapshots of every revision in your project. You can access these versions to compare and revise them when the need arises.&lt;/li&gt;
&lt;li&gt;Git repository hosting service - these are third-party web applications that wrap and enhance a version control system. An example of popular ones are; Github, Bitbucket, Gitlab. Many people confuse these to be one and the same thing with git. However, they are completely different. So, are they much more closely connected than Java and JavaScript? Yes, git is the underlying technology that platforms such as Github use to manage repositories. For a more detailed explanation on this, check out &lt;a href="https://blog.devmountain.com/git-vs-github-whats-the-difference/#:~:text=Simply%20put%2C%20Git%20is%20a,help%20you%20better%20manage%20them." rel="noopener noreferrer"&gt;this article&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell" rel="noopener noreferrer"&gt;Git branch&lt;/a&gt; - a branch represents an independent line of development on a project. For example, if you're working on a feature that involves creating an admin dashboard, you may choose to create a branch called &lt;code&gt;dashboard&lt;/code&gt; in which you work on all the dashboard logic.&lt;/li&gt;
&lt;li&gt;HEAD - When working with Git, only one branch can be checked out at a time - and this is what's called the "HEAD" branch. Often, this is also referred to as the "active" or "current" branch. Git makes note of this current branch in a file located inside the Git repository, in .git/HEAD. (This is an internal file, so it should not be manually manipulated!). By now you may have noticed that the tree is the symbolic representation in git 😄.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;
  
  
  Commands
&lt;/h1&gt;

&lt;p&gt;-&lt;code&gt;git init&lt;/code&gt; - This command creates a new Git repository. It can be used to convert an existing, unversioned project to a Git repository or initialize a new, empty repository.&lt;br&gt;
-&lt;code&gt;git remote add origin {remote_url}&lt;/code&gt; - connects a repo to a remote git URL. An example is;&lt;br&gt;
 &lt;code&gt;git remote add origin https://github.com/tngeene/Dadjokes-app.git&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git remote -v&lt;/code&gt; - checks the remote URLs set on a particular repo. A repo can have more than one remote, say for example you're pushing the code to two platforms such as Github and Bitbucket&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git remote remove origin&lt;/code&gt; or &lt;code&gt;git remote rm origin&lt;/code&gt; - removes a remote

&lt;ul&gt;
&lt;li&gt;alternatively, you can use &lt;code&gt;git remote set-url origin {new.url.here}&lt;/code&gt; this command automatically removes the old remote and assigns the new one.
In our example above, you might realize you've made a mistake and set the wrong origin, using the second approach of removing an origin, (I won't delve into the first one since it's pretty straightforward), you can run this command &lt;code&gt;git remote set-url origin https://github.com/tngeene/Bakery-template.git&lt;/code&gt; and this will remove the previously specified url and set the new one.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git rm -r --cached .&lt;/code&gt; - this command comes in handy when you want to untrack files, say you realized you would like to add it to the &lt;code&gt;.gitignore&lt;/code&gt; file when it was already tracked.

&lt;ul&gt;
&lt;li&gt;rm : remove command.&lt;/li&gt;
&lt;li&gt;-r : allows recursive removal.&lt;/li&gt;
&lt;li&gt;— cached : will only remove files from the index. Your files will still be there.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.&lt;/code&gt; : untracks all files. You can untrack a specific file by &lt;code&gt;git rm -r --cached {path_to_ file}&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git clone&lt;/code&gt; - useful for getting the contents of a remote repo to your local machine. Usually used when you don't initially have the code. Example usage is &lt;code&gt;git clone {url_of_repo}&lt;/code&gt;, for example 
  &lt;code&gt;git clone https://github.com/tngeene/fibonacci-sequence-react.git&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git fetch&lt;/code&gt; - really only downloads new data from a remote repository - but it doesn't integrate any of this new data into your working files. Fetch is great for getting a fresh view of all the things that happened in a remote repository.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;git pull&lt;/code&gt; - in contrast, is used with a different goal in mind: to update your current &lt;code&gt;HEAD&lt;/code&gt; branch with the latest changes from the remote server. This means that pull not only downloads new data; it also directly integrates it into your current working copy files. This has a couple of consequences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Since &lt;code&gt;git pull&lt;/code&gt; tries to merge remote changes with your local ones, a so-called "merge conflict" can occur.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Sytntax to use
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;git pull {remote_branch_name} {feature_branch_name}&lt;/code&gt; - for example &lt;strong&gt;&lt;em&gt;git pull origin master&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One can use &lt;code&gt;git pull —set-upstream {remote_branch_name} {feature_branch_name}&lt;/code&gt;. This syntax avoids specifying the remote branch to update every time, for example, &lt;code&gt;git pull —set-upstream origin master&lt;/code&gt; will allow you to just type &lt;code&gt;git pull&lt;/code&gt; once you've checked out in the master branch.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git push&lt;/code&gt; - pushing the code that's in your local machine to a remote branch. The syntax is the same as the git pull command highlighted above.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A thing to note about this is that for an initial repo, especially if it was initialized with a README file, it might bring in errors such as the remote having contents you do not have. Another one could be that the repo has unrelated histories. So before you push code make sure to first pull from the remote by specifying &lt;code&gt;git pull origin master —allow-unrelated-histories&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git branch&lt;/code&gt; - allows one to create a new branch. The syntax for this is, &lt;code&gt;git branch -f {branch} {start_point}&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git branch - f updates master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git checkout&lt;/code&gt; The git checkout command lets you navigate between the branches created by the &lt;code&gt;git branch&lt;/code&gt; command. For example, if we would like to navigate to the updates branch created above, we would simply run &lt;code&gt;git checkout updates&lt;/code&gt;.
Something to note when we're checking out is to make sure we've committed the changes we were working on in the branch we're checking out from.

&lt;ul&gt;
&lt;li&gt;Using the &lt;code&gt;git checkout&lt;/code&gt; command, one can also create a new branch and checkout to it immediately, all using one command! So how do we go about this, you might ask. Well, simply type &lt;code&gt;git checkout -b {branch_name}&lt;/code&gt; while in the current working branch. In our case above, &lt;code&gt;git checkout -b updates&lt;/code&gt;. This will automatically merge all the existing changes in your current branch into the newly created branch.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git add&lt;/code&gt; - now that you've finished working on all the cool stuff you've been working on, you'll want to push them to the remote origin we set with the &lt;code&gt;git add remote origin&lt;/code&gt; command. 
The &lt;code&gt;git add&lt;/code&gt; command is part one of that process. This command adds a change in the working directory to the staging area. It tells Git that you want to include updates to a particular file in the next commit. There are two approaches to adding files that you want to push to the remote.

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git add .&lt;/code&gt; This command adds all the changes you've made to be included when you push them to the remote. The &lt;code&gt;.&lt;/code&gt; at the end signifies that you want to stage all the files.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git add {path_to_file_you_want_to_stage}&lt;/code&gt; - this command lets you specify which updates you'd like to be staged for commit. For example one might only want to update the settings file, you would only need to type &lt;code&gt;git add ./settings.py&lt;/code&gt; &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git commit&lt;/code&gt; - finally that you've added all your changes you will want to save them to be included once you decide to push to the remote repo. This command saves the changes to your local machine. 

&lt;ul&gt;
&lt;li&gt;The syntax for this commit involves including a message to describe what changes you've been working on, for example after working on a bug that fixes the login logic on a form, one might choose to commit it using this &lt;code&gt;git commit -m "fixed email validation on login API"&lt;/code&gt; The -m flag denotes message
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   It is important to write informative commit messages so 
   those other people who are working on the branch know 
   exactly what you were working on(just as it's important to 
   use descriptive variable names 😉).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git merge&lt;/code&gt; - after working on all the changes in your newly checked out branch, you might want to merge all the changes to the main branch before pushing them to the remote. After making a commit on the feature branch, run the command &lt;code&gt;git checkout {branch_you_want_to_checkout_to}&lt;/code&gt; and then &lt;code&gt;git merge {feature_branch_name}&lt;/code&gt;. In our example above, if we want to merge changes we've made into the main branch, we would go through these steps
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        git checkout main
        git merge updates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git log&lt;/code&gt; - this command allows you to view information about previous commits. it simply shows the commits that lead up to the current state of the current working branch.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Reverting to a previous commit.
&lt;/h2&gt;

&lt;p&gt;Often times you may realize that you made a mistake and would like to roll back to a more stable version of the project. Luckily, git offers solutions to this problem. We can choose to either use &lt;code&gt;git revert&lt;/code&gt; or &lt;code&gt;git reset&lt;/code&gt; commands. For the purposes of this guide, I'll go with &lt;code&gt;git revert&lt;/code&gt; as I have more experience using it. However, for in-depth analysis and comparison of both commands, you can have a look at this &lt;a href="https://www.atlassian.com/git/tutorials/undoing-changes/git-revert#:~:text=Summary,in%20regards%20to%20losing%20work" rel="noopener noreferrer"&gt;article from atlassian&lt;/a&gt;.&lt;br&gt;
The first step in rolling back is to identify at which point you'd like to go back. For this, we'll use our friend, &lt;code&gt;git log&lt;/code&gt; &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcommunity.codenewbie.org%2Fremoteimages%2Fi%2Frjgtbs0zpewphfc8s92k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcommunity.codenewbie.org%2Fremoteimages%2Fi%2Frjgtbs0zpewphfc8s92k.png" alt="Git log ngeene"&gt;&lt;/a&gt;&lt;br&gt;
Notice the ordering of the commit history, with the "HEAD" being the first one on the list. If we want to go back to the commit with the message "reverted to ssr", we would run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git revert --no-commit 4eab63b2..HEAD
git commit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will revert everything from the HEAD back to the commit hash, meaning it will recreate that commit state in the working tree as if every commit after 4eab63b2 had been walked back. You can then commit the current tree, and it will create a brand new commit essentially equivalent to the commit you "reverted" to. &lt;br&gt;
The &lt;code&gt;--no-commit&lt;/code&gt; flag lets git revert all the commits at once. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that we've used the commit hash of the commit above the one we'd like to roll back to.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There is of course more than one way to revert, but this is what works for me. Feel free to add to the discussion on best practices to revert in the comments.&lt;/p&gt;
&lt;h1&gt;
  
  
  Best Practices
&lt;/h1&gt;

&lt;p&gt;While working with git, it vital to follow some practices, especially working with a team. The practices I'll mention are my personal preference and totally biased towards my own views. &lt;/p&gt;
&lt;h4&gt;
  
  
  Use Descriptive commit messages
&lt;/h4&gt;

&lt;p&gt;It's important to let other developers working on the project on what exactly you've been working on. writing a commit message such as "fixed loader on landing page" instead of "landing page update" goes a long way in informing the code reviewers of what you've worked on.&lt;/p&gt;
&lt;h4&gt;
  
  
  Make Pull Requests
&lt;/h4&gt;

&lt;p&gt;Once you've pushed your changes to Github if on a feature branch, remember to make a pull request(merge request in GitLab). This will notify project owners of a request to integrate your changes into the main branch and they can do a code review.&lt;/p&gt;
&lt;h4&gt;
  
  
  Write an informative pull request message.
&lt;/h4&gt;

&lt;p&gt;Still, on the subject of pull requests, it's important to let code reviewers know why you'd like your code to be merged into the main branch. An example of a pull request message would go along the lines of;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;This commit contains updates on the user registration logic
1. Fixed signup form validation by adding error handling
2. Added vee-validate on login and registration forms
3. Updated redirection on successful signup
4. Integrated social auth as a signup option (using google and Facebook)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Keep Commit Revisions minimal
&lt;/h4&gt;

&lt;p&gt;As a minimalist, I believe that it's best to work on bits of the code and push them instead of making revisions on many files and pushing them at once. My case for this argument is that it provides for good tracking in case things break at some point, we can just roll back to the commit and undo the minor mistake made on a couple of files instead of multiple files, which can prove to be a nightmare. &lt;/p&gt;

&lt;p&gt;That covers the end of this article. There's a ton of information on git, but for now, we'll only dive into the shallow end that is the very deep pool of git. I hope this piece helps someone and feel free to drop your thoughts. You can also check out &lt;a href="https://tngeene.com/" rel="noopener noreferrer"&gt;my website&lt;/a&gt; or follow me on &lt;a href="https://twitter.com/Ngeene_kihiu" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Include a &lt;code&gt;gitignore&lt;/code&gt; file
&lt;/h3&gt;

&lt;p&gt;When working on a project, there are some directories and files you'd prefer git not to track as well as some files which are not recommended to be tracked, an example being the &lt;code&gt;node_modules&lt;/code&gt; files in node.js. This aids in reducing boilerplate code since the files are still going to be recreated in the other developer's machine. I found &lt;a href="https://github.com/github/gitignore" rel="noopener noreferrer"&gt;this interesting GitHub repo&lt;/a&gt; containing a list of popular .gitignore templates for most frameworks and languages.&lt;/p&gt;

&lt;h1&gt;
  
  
  Resources
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;This section highlights some useful resources to get you started with git. It includes videos, blog posts, and podcasts.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;Freecodecamp's &lt;a href="https://www.youtube.com/watch?v=RGOj5yH7evk&amp;amp;ab_channel=freeCodeCamp.org" rel="noopener noreferrer"&gt;Git and GitHub youtube course&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A collection of &lt;a href="https://github.com/github/gitignore" rel="noopener noreferrer"&gt;gitignore templates&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Ladybug podcast episode on &lt;a href="https://podcasts.google.com/feed/aHR0cHM6Ly9waW5lY2FzdC5jb20vZmVlZC9sYWR5YnVnLXBvZGNhc3Q/episode/aHR0cHM6Ly9waW5lY2FzdC5jb20vZ3VpZC80Y2ZlMDg5ZC1jOTQ1LTRjZWEtOWUwNy04ZGE0NGQ1OTkwZGQ" rel="noopener noreferrer"&gt;git and github&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Devmountain's explanation on &lt;a href="https://blog.devmountain.com/git-vs-github-whats-the-difference/" rel="noopener noreferrer"&gt;the difference between git and github&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The official &lt;a href="https://git-scm.com/" rel="noopener noreferrer"&gt;git documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Edureka's &lt;a href="https://www.youtube.com/watch?v=xuB1Id2Wxak&amp;amp;ab_channel=edureka%21" rel="noopener noreferrer"&gt;youtube git course&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Sponsors
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Do you need a place to host your website or app, &lt;a href="//DigitalOcean%20%E2%80%93%20The%20developer%20cloudwww.digitalocean.com"&gt;Digital ocean&lt;/a&gt;
is just the solution you need, sign up on digital ocean using this &lt;a href="https://m.do.co/c/eaa803fe4d99" rel="noopener noreferrer"&gt;link&lt;/a&gt; and experience the best cloud service provider.&lt;/li&gt;
&lt;li&gt;The journey to becoming a developer can be long and tormentuos, luckily &lt;a href="http://referral.pluralsight.com/mQh0Nxp" rel="noopener noreferrer"&gt;Pluralsight&lt;/a&gt; makes it easier to learn. They offer a wide range of courses, with top quality trainers, whom I can personally vouch for. Sign up using &lt;a href="http://referral.pluralsight.com/mQh0Nxp" rel="noopener noreferrer"&gt;this link&lt;/a&gt; and get a 50% discount on your first course.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.scraperapi.com?via=teddy44" rel="noopener noreferrer"&gt;Scraper API&lt;/a&gt; is a startup specializing in strategies that'll ease the worry of your IP address from being blocked while web scraping. They utilize IP rotation so you can avoid detection. Boasting over 20 million IP addresses and unlimited bandwidth. Using Scraper API and a tool like &lt;a href="https://2captcha.com?from=12437369" rel="noopener noreferrer"&gt;2captcha&lt;/a&gt; will give you an edge over other developers. The two can be used together to automate processes. Sign up on Scraper API and use &lt;a href="https://www.scraperapi.com?via=teddy44" rel="noopener noreferrer"&gt;this link&lt;/a&gt; to get a 10% discount on your first purchase.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for going through the article. I hope in one way or another, you have a better understanding of git. Cheers!&lt;/p&gt;

</description>
      <category>git</category>
      <category>cloud</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
    <item>
      <title>What are your thoughts on strapi.js?</title>
      <dc:creator>Ted Ngeene</dc:creator>
      <pubDate>Tue, 15 Dec 2020 04:06:07 +0000</pubDate>
      <link>https://forem.com/tngeene/what-are-your-thoughts-on-strapi-js-4g15</link>
      <guid>https://forem.com/tngeene/what-are-your-thoughts-on-strapi-js-4g15</guid>
      <description>&lt;p&gt;I recently came across &lt;a href="https://strapi.io/"&gt;strapi.js&lt;/a&gt;. They describe themselves as,  "the next-gen headless CMS, open-source, javascript, enabling content-rich experiences to be created, managed and exposed to any digital device."&lt;br&gt;
So I went ahead and bootstrapped a simple blog application. From the looks of things, it seems like a pretty decent resource.&lt;br&gt;
To anyone that has used Strapi before, would you recommend it? which customizations does it offer? features like extending the user models, authentication, scalability, ease of hosting? How does it fair security-wise? What tips would you give to a beginner learning the CMS?&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>discuss</category>
      <category>backend</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
