<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Adyaksa W</title>
    <description>The latest articles on DEV Community by Adyaksa W (@adyaksa_w).</description>
    <link>https://dev.to/adyaksa_w</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%2F775396%2Fd846acc1-41d8-478a-b88c-fc3ef65ef57b.png</url>
      <title>DEV Community: Adyaksa W</title>
      <link>https://dev.to/adyaksa_w</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/adyaksa_w"/>
    <language>en</language>
    <item>
      <title>Easy Manual Setup HTTPS In Nginx</title>
      <dc:creator>Adyaksa W</dc:creator>
      <pubDate>Wed, 12 Jan 2022 13:59:34 +0000</pubDate>
      <link>https://dev.to/adyaksa_w/easy-manual-setup-https-in-nginx-2g85</link>
      <guid>https://dev.to/adyaksa_w/easy-manual-setup-https-in-nginx-2g85</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_9_NQd68--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3840/1%2A49glYe-285UPvYW9xp7JFw.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_9_NQd68--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3840/1%2A49glYe-285UPvYW9xp7JFw.jpeg" alt="Image by [Skylarvision on Pixabay](https://pixabay.com/id/illustrations/https-situs-web-internet-keamanan-3344700/)" width="880" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’re lazy like me, you would use certbot to create certificates for HTTPS. This is my preferred method because it’s simple, fast, and can automatically renew a certificate when it expired. Unfortunately, the world ain’t all sunshine and rainbows. There are some occasions where certbot can’t immediately create a certificate, such as:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The server is behind a VPN. Usually, there is a proxy that is already set up. If this is not the case for you, then I hope you know the list of things where the proxy should be set up.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The web server is behind a secured TLD, such as .app. This means you can’t access it on HTTP protocol and certbot can’t create a certification. This is because, on default, certbot uses the &lt;a href="https://letsencrypt.org/docs/challenge-types/#http-01-challenge"&gt;HTTP-01 challenge&lt;/a&gt;, which means this challenge would fail on the webserver.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you’re here because of the first reason, then I recommend configuring the proxy for the certbot. Because sooner or later you will need to configure it for automatic certificate renewal. Your future self will thank you if you do this hard work now.&lt;/p&gt;

&lt;p&gt;In my case, I have some websites that use the .app domain. Because of this, I need to learn how to set up a certificate manually. For the explanation, I will skip over the certificate creation because it’s already generated in my domain provider. If you want to refresh your understanding of HTTPS, you can check this helpful comic from &lt;a href="https://howhttps.works/"&gt;HowHTTPSWorks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So the first step is getting your certificate. In my case, I used porkbun as my provider. There will 4 files in the given zip: domain.cert.pem, intermediate.cert.pem, private.key.pem, public.key.pem. If you’re used to certbot file naming like me, this mapping will helps you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;domain.cert.pem (Porkbun) -&amp;gt; fullchain.pem (certbot) -&amp;gt; ssl_certificate (nginx)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;intermediate.cert.pem (Porkbun) -&amp;gt; chain.pem (certbot) -&amp;gt;ssl_trusted_certificate(nginx)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;private.key.pem (Porkbun) -&amp;gt; privkey.pem (certbot) -&amp;gt; ssl_certificate_key&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;public.key.pem (Porkbun) -&amp;gt; cert.pem (certbot)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to understand these files more, you can check these StackOverflow discussions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://stackoverflow.com/q/63359785"&gt;How to use Porkbun SSL Certificate Files with Nginx?&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://stackoverflow.com/q/50389883"&gt;Generate CRT &amp;amp; KEY SSL files from Let’s Encrypt from scratch&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5cNdVD0p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2936/1%2AD3MWFRaYj2LbywNWdL5T4A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5cNdVD0p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2936/1%2AD3MWFRaYj2LbywNWdL5T4A.png" alt="" width="880" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you need to move this file to your server. We can just use SCP to move it like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;scp artilearn.app-ssl-bundle host@ipaddress:~/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The SSL certificate is successfully copied to our remote server. Now we need to put our website on the server. For this tutorial purpose, we will just use a dummy HTML file just to test our SSL.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
The last step is to configure the Nginx config file. We only need to configure 4 things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The port where our website will be (Line 2)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;SSL Certificate for the website (Line 3–5)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Root folder of our website (Line 6)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Domain name of our website (Line 7)&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

Now restart the Nginx service with sudo systemctl restart nginx. When you check &lt;a href="https://artilearn.app/dummy.html"&gt;https://artilearn.app/dummy.html&lt;/a&gt;, our HTML file will successfully appear with the correct certificate!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1nj1rEvY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2ABM9IyHxES-KFJpNJu8L6KQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1nj1rEvY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2ABM9IyHxES-KFJpNJu8L6KQ.png" alt="" width="372" height="70"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now our certificate has been successfully set up. But don’t forget this certificate is manually set up, and we haven’t configured any automatic renewal. But how should we do it? Well, we can go to the first sentence in this article: Being lazy and using certbot. We can do this now because our website can now be accessed from the HTTPS URL, which HTTP-01 challenge approves this method. Now just go to the &lt;a href="https://certbot.eff.org/"&gt;certbot site&lt;/a&gt; and follow the simple step there!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Hello, I’m Adyaksa, and I write about software development and my language learning experience. I’m planning to release a weekly blog about something that I find interesting while working on my side projects. If you’re interested, you can follow me to keep updated about it!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article is crossposted from &lt;a href="https://medium.com/@adyaksa.w"&gt;my Medium account&lt;/a&gt;. You can follow it to get my latest article earlier.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Automate Your Flask Deployment to VPS Using Github Actions</title>
      <dc:creator>Adyaksa W</dc:creator>
      <pubDate>Mon, 03 Jan 2022 02:46:39 +0000</pubDate>
      <link>https://dev.to/adyaksa_w/automate-your-flask-deployment-to-vps-using-github-actions-1d5g</link>
      <guid>https://dev.to/adyaksa_w/automate-your-flask-deployment-to-vps-using-github-actions-1d5g</guid>
      <description>&lt;p&gt;&lt;em&gt;(Cover Image by &lt;a href="https://unsplash.com/@sortino?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Joshua Sortino&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/automation?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In the project &lt;a href="https://lnprice.com/" rel="noopener noreferrer"&gt;lnprice&lt;/a&gt; that I have mentioned in &lt;a href="https://dev.to/adyaksa_w/what-i-learn-from-trying-selenium-on-vps-5a56"&gt;my previous article&lt;/a&gt;, the backend deployment is done manually like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Push code to Github&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pull code in VPS&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Redeploy application&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After dozens of deployments, &lt;strong&gt;I have enough&lt;/strong&gt;. I decide to learn how to automate this. For some context, I already have experience in using Github Actions to automate my deployment. The reason that I’m not doing it in this project is that I have never done it in a Flask environment. I just want to prioritize the application first before any automation. So in this article, I will describe the step that I have done to deploy my backend on the VPS automatically. All the code can be accessed in my &lt;a href="https://github.com/adyaksaw/tutorial/tree/master/1-flask-deployment" rel="noopener noreferrer"&gt;Github repository&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%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2AliDJRez82TNvbhaBJn0B1Q.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%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2AliDJRez82TNvbhaBJn0B1Q.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;1. Setup Pipenv &amp;amp; Dependency&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;To have an easier time automating deployment, we need to have a simple dependency management system. So we need to install Pipenv in our project to handle our dependency and then install our dependency. We can do it like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install pipenv
pipenv install flask
pipenv shell
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;By running pipenv shell , we initiate a virtual environment in this project that has the Flask framework.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. &lt;strong&gt;Create Flask Application&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The next step is creating a simple Flask application. We can use the sample on the Flask homepage. But in my case, I changed it a bit to use a different port so we don’t accidentally use an already used port.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
Now run flask run and go to localhost:5001 , there should be a ‘Hello World’ on that page.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Create Docker File
&lt;/h2&gt;

&lt;p&gt;In my opinion, the easiest and painless way to deploy our application is to use Docker. We can definitely not use it and still be able to automate our deployment. But because there is so much possible pain that could appear when not using Docker, so I would really suggest using it.&lt;/p&gt;

&lt;p&gt;Now, before touching Docker, we need to talk about how to deploy Flask to production. Like Django, Flask needs a WSGI server to deploy the application. There are 2 common WSGI server: UWSGI and Gunicorn. I won’t go into detail what is the difference between them. This article will use Gunicorn as the WSGI server.&lt;br&gt;
&lt;a href="https://github.com/tiangolo/meinheld-gunicorn-flask-docker" rel="noopener noreferrer"&gt;&lt;strong&gt;GitHub - tiangolo/meinheld-gunicorn-flask-docker: Docker image with Meinheld and Gunicorn for Flask…&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, someone already created a docker image for the &lt;a href="https://github.com/tiangolo/meinheld-gunicorn-flask-docker" rel="noopener noreferrer"&gt;Gunicorn-Flask&lt;/a&gt; combination. But in the context of this tutorial, that image needs modification so we are better off creating it from scratch. Here is the created Dockerfile.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
Why do we use requirements.txt instead of using Pipfile directly? Here, I just want to treat Pipenv as some ‘dependency organizer’. I don’t want to use Pipenv as a virtual environment tool. You should be able to use the dependency from Pipfile directly and use Pipenv to access the dependency with some modification to the code.

&lt;p&gt;After creating the Dockerfile, we can build and run the Dockerfile by running the following commands:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker build -t 1-flask-deployment .
docker run -dp 5001:5001 1-flask-deployment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Try to run docker container ls , the 1-flask-deployment container should be up and running and can be accessed from localhost:5001 .&lt;/p&gt;
&lt;h2&gt;
  
  
  4. Setup Github Runner
&lt;/h2&gt;

&lt;p&gt;Before creating the Github Actions file, we need to configure our own Github Runner. We need to place the Runner on the same server as the server where we want to place our production code. For this step, we can just follow the steps that are outlined in the official &lt;a href="https://github.com/adyaksaw/tutorial/settings/actions/runners/new?arch=x64&amp;amp;os=linux" rel="noopener noreferrer"&gt;Github Runner Documentation&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  5. Create Github Actions
&lt;/h2&gt;

&lt;p&gt;Time to pay off the hard part of creating the Dockerfile! Because we already know the step to run the container, now we just need to repeat that step in the Github Actions.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
In this code, I configured the Github Actions so it will run only when the code is pushed to master (Line 3–6) and running in my own Virtual Machine (Line 10).&lt;/p&gt;

&lt;p&gt;After pushing the deploy.yml, try to call curl localhost:5001 from inside the VPS. The ‘Hello World’ text should appear like when we call it in our local system. This means that the Github Runner is working, and the deployment automation is working!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Hello, I’m Adyaksa, and I write about software development and my language learning experience. I’m planning to release a weekly blog about something that I find interesting while working on my side projects. If you’re interested, you can follow me to keep updated about it!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;References:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Pipenv Basic: &lt;a href="https://pipenv.pypa.io/en/latest/basics/" rel="noopener noreferrer"&gt;https://pipenv.pypa.io/en/latest/basics/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Flask Quick Start: &lt;a href="https://flask.palletsprojects.com/en/2.0.x/quickstart/" rel="noopener noreferrer"&gt;https://flask.palletsprojects.com/en/2.0.x/quickstart/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pipenv docker: &lt;a href="https://pythonspeed.com/articles/pipenv-docker/" rel="noopener noreferrer"&gt;https://pythonspeed.com/articles/pipenv-docker/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Gunicorn Flask: &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-serve-flask-applications-with-gunicorn-and-nginx-on-ubuntu-18-04" rel="noopener noreferrer"&gt;https://www.digitalocean.com/community/tutorials/how-to-serve-flask-applications-with-gunicorn-and-nginx-on-ubuntu-18-04&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>docker</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Why You Should Use VueJS</title>
      <dc:creator>Adyaksa W</dc:creator>
      <pubDate>Mon, 27 Dec 2021 06:04:14 +0000</pubDate>
      <link>https://dev.to/adyaksa_w/why-you-should-use-vuejs-49il</link>
      <guid>https://dev.to/adyaksa_w/why-you-should-use-vuejs-49il</guid>
      <description>&lt;p&gt;In the current framework trend for frontend, there is 3 mainstream that we commonly know: React, Vue, and Angular. In my recent projects, when there is a need to write frontend applications, I always used Vue. I just love using Vue.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rR_-_Kkz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fb8hwzozeobgivvvqu98.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rR_-_Kkz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fb8hwzozeobgivvvqu98.png" alt="VueJS Logo" width="400" height="400"&gt;&lt;/a&gt;&lt;br&gt;
From &lt;a href="https://docs.vuejs.id/"&gt;VueJS Official Site&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why? Well, first of all, I'm not hardcore enough to learn many things just for a simple project. I want simplicity. So for this reason, I excluded Angular. Now it comes down to React and Vue. Here comes my second reason: &lt;strong&gt;I love Vue syntax&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;First of all, the file structure is quite simple yet separated beautifully. If you never touched Vue before, here is a snippet of basic Vue syntax&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;**&amp;lt;template&amp;gt;**
  &amp;lt;h1&amp;gt;Hello {{name}}&amp;lt;/h1&amp;gt;
**&amp;lt;/template&amp;gt;
&amp;lt;script&amp;gt;**
export default {
  data() {
    return {
      name: 'Adyaksa',
    }
  }
}
**&amp;lt;/script&amp;gt;
&amp;lt;style&amp;gt;**
h1 {
  color: red;
}
**&amp;lt;/style&amp;gt;**
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So Vue file structure is divided into 3 sections: &lt;strong&gt;template&lt;/strong&gt;, &lt;strong&gt;script&lt;/strong&gt;, and &lt;strong&gt;style&lt;/strong&gt;. The combination will form a &lt;strong&gt;Vue Component&lt;/strong&gt;. The template is where the HTML structure is described. All heavy lifting is placed in the script section, where we can put all normal frontend scripts here in addition to Vue-specific scripts such as &lt;a href="https://docs.vuejs.id/"&gt;component lifecycle&lt;/a&gt;. And then the last section is where we put our CSS for the code.&lt;/p&gt;

&lt;p&gt;One thing that I have experienced when using React is that when your team doesn't have a clear formatting guideline, it's harder to find specific code that you need. Moreover, when you have many components with their own specific styling, you will have an enormous number of files that you have. But when we're using Vue, all the HTML, CSS, and JS are combined in 1 class with a specific order that is already defined. Because of this, we know where each section is located in the file and we have an easier time finding what we need. This is also described in Vue docs: "&lt;a href="https://vuejs.org/v2/guide/single-file-components.html#What-About-Separation-of-Concerns"&gt;What About Separation of Concerns?&lt;/a&gt;"&lt;/p&gt;

&lt;p&gt;And then the second one is what makes creating HTML in Vue fun: Directives. Imagine that you want to create a list based on value from array &lt;code&gt;arrayList &lt;/code&gt;. You can easily do it by adding &lt;code&gt;v-for&lt;/code&gt; directives like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;li **v-for="item in arrayList"**&amp;gt; {{ item }} &amp;lt;/li&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hey, what's so fun about it? Well, imagine that you want to create something more complex such as displaying the ranking of an item with its attributes. By using this, we can just add &lt;code&gt;v-for&lt;/code&gt; directives to easily access all the attributes of the item. And there are many more neat directives such as &lt;code&gt;v-if&lt;/code&gt;, &lt;code&gt;v-show&lt;/code&gt;, &lt;code&gt;v-model&lt;/code&gt; etc.&lt;/p&gt;

&lt;p&gt;But it's not all fun and game. Like all languages, VueJS readability would suffer at a more complex project. Its code structure also doesn't help, with every bit of code stuffed in a file. But still, I think this is a small price to use this fun language.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Hello, I'm Adyaksa, and I write about software development and my language learning experience. I'm planning to release a weekly blog about something that I find interesting while working on my side projects. If you're interested, you can follow me to keep updated about it!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>vue</category>
      <category>react</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>What I Learn From Trying Selenium on VPS</title>
      <dc:creator>Adyaksa W</dc:creator>
      <pubDate>Mon, 20 Dec 2021 08:39:51 +0000</pubDate>
      <link>https://dev.to/adyaksa_w/what-i-learn-from-trying-selenium-on-vps-5a56</link>
      <guid>https://dev.to/adyaksa_w/what-i-learn-from-trying-selenium-on-vps-5a56</guid>
      <description>&lt;p&gt;Currently, I'm exploring various ideas for side projects. One of them needs some automated scraping that runs as a CRON job. Because I'm used to scrapping with Python using Selenium, I immediately choose them and connect them with Flask. There is 1 little problem: My previous scraping experience is only on a local machine. So I need to learn how to do scraping on the server  and add a CRON job on top of that. Because it's quite a common use case, there should be many tutorials about it and make it easier when I need to troubleshoot, right?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Oh god, how wrong I was.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Okay, so before I started building this, I already researched a bit about 3 common libraries for scraping in Python with their advantage and disadvantage. To recap it a bit from what I learned:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Selenium:&lt;/strong&gt; Can handle javascript-heavy websites, pass more scrapper detection than other alternatives, but so much slower than other libraries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BeautifulSoup + requests:&lt;/strong&gt; Simplest solution (I used requests on my previous project, so I only need to learn how to parse HTML using BeautifulSoup, which is quite easy too).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scrapy:&lt;/strong&gt; So much functionality and faster than Selenium, but quite inflexible than other solutions.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Because some websites that I need to scrape have some scrapper detection and are quite javascript-heavy, I don't see the need to use another library. I also don't need to update this CRON job too often, so slow performance doesn't matter to me.&lt;/p&gt;

&lt;p&gt;In total, it took me 1 day to finish the prototype for this side project. I tested the scrapper locally and it works. It's slow, but expected. Satisfied with the result, I try to start the scraping on the VPS.&lt;/p&gt;

&lt;p&gt;It broke. Okay no worry, I can fix this.&lt;/p&gt;

&lt;p&gt;The error keeps changing, and it's getting more obscure.&lt;/p&gt;

&lt;p&gt;I spent a few days after that to debug it, and my final solution is "&lt;strong&gt;Restart and hope for the best&lt;/strong&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%2F48vv98d42g6d8ys6xrt4.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%2F48vv98d42g6d8ys6xrt4.png" alt="Image description"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;By &lt;a href="https://unsplash.com/@lucabravo" rel="noopener noreferrer"&gt;Luca Bravo&lt;/a&gt; from &lt;a href="https://unsplash.com/photos/XJXWbfSo2f0" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There is so much hardship that comes with scraping on the VPS.&lt;/p&gt;

&lt;p&gt;First, VPS doesn't have GUI, so we need to configure the server or the Selenium so it can still run without GUI. I found 2 solutions regarding this, using &lt;a href="https://stackoverflow.com/questions/6183276/how-do-i-run-selenium-in-xvfb" rel="noopener noreferrer"&gt;virtual display&lt;/a&gt; or running Selenium with a &lt;a href="https://stackoverflow.com/questions/55544648/how-to-run-selenium-script-on-server" rel="noopener noreferrer"&gt;headless&lt;/a&gt;browser.&lt;/p&gt;

&lt;p&gt;I tried the virtual display approach, but the error that came up is still the same. When I changed to the headless solution, the error changes, so I choose this solution.&lt;/p&gt;

&lt;p&gt;Second, &lt;strong&gt;the program frequently crashes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To handle this, I used &lt;a href="https://selenium-python.readthedocs.io/waits.html" rel="noopener noreferrer"&gt;WebDriverWait&lt;/a&gt; so the driver can wait until the browser finishes loading. I also added various try except in risky lines that have a high chance to create an error.&lt;/p&gt;

&lt;p&gt;Lastly, &lt;strong&gt;the driver frequently disconnects&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is the hardest problem for me. What I found out is that ChromeDriver is very unstable compared to GeckoDriver. It successfully reduces the disconnection, but still not zero. So I need to run the scrapper in batch and restart every time the driver disconnected.&lt;/p&gt;

&lt;p&gt;As a result, I changed my approach. I used the second scraping method (BeautifulSoup + requests) for website that can handle it. For the rest of the website, I used Selenium and wait patiently.&lt;br&gt;
From this project, I learned that scheduled scraping on VPS is hard and time-consuming. Moreover, the problem that I encountered doesn't include when the target website blocks your scraping attempt. To handle this, there is a need to configure my scrapper so it doesn't look like it came from a scraping program. But because I'm just doing this side project for fun, I just change a few basic configurations. If in the future I need to have another scraping on VPS, I probably will invest a bit on a few proxies to change the requester's IP or even use web scraping services such as &lt;a href="https://www.scraperapi.com/?fp_ref=adyaksaw" rel="noopener noreferrer"&gt;ScraperAPI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But if not really needed, I don't want to do scraping on VPS again.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you're curious about the side project that I'm building, you can check it out on &lt;a href="https://www.lnprice.com/" rel="noopener noreferrer"&gt;this page&lt;/a&gt;. This is a simple price aggregation website for &lt;a href="https://en.wikipedia.org/wiki/Light_novel" rel="noopener noreferrer"&gt;light novels&lt;/a&gt;. I'm planning to release a weekly blog about something that I find interesting while working on my side projects, and this article is one of them.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>python</category>
      <category>webdev</category>
      <category>cloud</category>
    </item>
  </channel>
</rss>
