<?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: Iacovos Constantinou</title>
    <description>The latest articles on DEV Community by Iacovos Constantinou (@iacons).</description>
    <link>https://dev.to/iacons</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%2F5806%2F861bd2be-a338-4d43-9075-d0fccb0f2cb0.jpeg</url>
      <title>DEV Community: Iacovos Constantinou</title>
      <link>https://dev.to/iacons</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/iacons"/>
    <language>en</language>
    <item>
      <title>How to import CSV Files with React</title>
      <dc:creator>Iacovos Constantinou</dc:creator>
      <pubDate>Sat, 11 May 2024 06:48:43 +0000</pubDate>
      <link>https://dev.to/iacons/how-to-import-csv-files-with-react-2f0c</link>
      <guid>https://dev.to/iacons/how-to-import-csv-files-with-react-2f0c</guid>
      <description>&lt;p&gt;In this article we are going to explore how we can use &lt;a href="https://importok.io/?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=react-import" rel="noopener noreferrer"&gt;importOK&lt;/a&gt; to embed an import wizard into a React application. When we are done, we will be able to upload both CSV and Excel files, map the columns, review and fix any validation errors and finally submit the uploaded data to our API.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://importok.io/?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=react-import" rel="noopener noreferrer"&gt;importOK&lt;/a&gt; is a reusable custom web component — you can control where it goes and how the mapping works. There are zero dependencies to any third party services and it can be hooked directly to your API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup your fields
&lt;/h2&gt;

&lt;p&gt;For the sake of simplicity, we will be importing a list of Contacts. Our app will be expecting four fields: first name last name email and phone.&lt;br&gt;
First, we need to install the React component for importOK.&lt;br&gt;
npm install @importok/react&lt;br&gt;
Then to embed the import wizard, we can add the following into our page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import ImportokWizard from '@importok/react';

function App() {
  /**
    * Import fields to be mapped
    * Check https://importok.io/docs/fields.html for more details
    */
  const fields = {
    first_name: {
      label: 'First Name'
    },
    last_name: {
      label: 'Last Name'
    },
    email: {
      label: 'Email'
    },
    phone: {
      label: 'Phone'
    }
  };

  return (
    &amp;lt;div className="App"&amp;gt;
      &amp;lt;ImportokWizard
        title="importOK Example for React"
        fields={fields}
      /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above also &lt;a href="https://importok.io/docs/fields.html?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=react-import" rel="noopener noreferrer"&gt;defines a list of fields&lt;/a&gt; which more or less describe our resource schema. Providing only the labels is sufficient and more than enough to get you started. Give it a go! At this stage you should be able to upload a CSV File and map the columns. Sweet eh?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fsww37798j76oq1ypdczf.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fsww37798j76oq1ypdczf.gif" alt="Uploading CSV Files and mapping columns" width="676" height="358"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Normalize and Cleanup your data
&lt;/h2&gt;

&lt;p&gt;More often than not, the data provided by your end-users is not in perfect shape. Extra trailing spaces, incorrectly formatted phone numbers, and other frequent irregularities that take time and effort to resolve.&lt;/p&gt;

&lt;p&gt;To improve this, we can use &lt;a href="https://importok.io/docs/transformers.html?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=react-import" rel="noopener noreferrer"&gt;transformers&lt;/a&gt; to apply common fixes. The transformation happens right after the file is uploaded and before the validation. importOK provides a handfull of built-in transformers but if necessary you can also add your own.&lt;/p&gt;

&lt;p&gt;Let’s extend our example to trim any spaces, and capitalize both first and last name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import ImportokWizard from '@importok/react';

function App() {
  /**
    * Import fields to be mapped
    * Check https://importok.io/docs/fields.html for more details
    */
  const fields = {
    first_name: {
      label: 'First Name',
      transformers: 'trim|capitalize'
    },
    last_name: {
      label: 'Last Name',
      transformers: 'trim|capitalize'
    },
    email: {
      label: 'Email',
      transformers: 'trim'
    },
    phone: {
      label: 'Phone',
      transformers: 'trim'
    }
  };

  return (
    &amp;lt;div className="App"&amp;gt;
      &amp;lt;ImportokWizard
        title="importOK Example for React"
        fields={fields}
      /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Validate your data
&lt;/h2&gt;

&lt;p&gt;While transformers can help auto-heal your data, and bring consistency, they don’t cover all the cases. What if the email provided is invalid or the last name is blank?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://importok.io/docs/validators.html?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=react-import" rel="noopener noreferrer"&gt;Validators&lt;/a&gt; have been designed to address this problem and provide instead feedback to your end-users, so that they can fix these errors. Similar to transformers, there are some build-in validators and you can add your own as well.&lt;/p&gt;

&lt;p&gt;Let’s improve further our example to make the last name required, and ensure that both email and phone are in the right format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import ImportokWizard from '@importok/react';

function App() {
  /**
    * Import fields to be mapped
    * Check https://importok.io/docs/fields.html for more details
    */
  const fields = {
    first_name: {
      label: 'First Name',
      transformers: 'trim|capitalize'
    },
    last_name: {
      label: 'Last Name',
      transformers: 'trim|capitalize',
      validators: 'required'
    },
    email: {
      label: 'Email',
      transformers: 'trim'
      validators: 'email'
    },
    phone: {
      label: 'Phone',
      transformers: 'trim'
      validators: 'phone'
    }
  };

  return (
    &amp;lt;div className="App"&amp;gt;
      &amp;lt;ImportokWizard
        title="importOK Example for React"
        fields={fields}
      /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is how it looks like at this point. Please note that you can filter by error type and that you can adjust your data without leaving the import wizard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fsnkzrwevu0oo3fb4q0cr.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fsnkzrwevu0oo3fb4q0cr.gif" alt="Reviewing, fixing and importing data" width="676" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Push data to your API
&lt;/h2&gt;

&lt;p&gt;There are a couple of options available to push the normalized and validated data to your API. You can either choose to send individuals records in parallel or send the full data set.&lt;/p&gt;

&lt;p&gt;Let’s use the latter option for our example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import ImportokWizard from '@importok/react';

function App() {
  /**
    * Import fields to be mapped
    * Check https://importok.io/docs/fields.html for more details
    */
  const fields = {
    first_name: {
      label: 'First Name',
      transformers: 'trim|capitalize'
    },
    last_name: {
      label: 'Last Name',
      transformers: 'trim|capitalize',
      validators: 'required'
    },
    email: {
      label: 'Email',
      transformers: 'trim'
      validators: 'email'
    },
    phone: {
      label: 'Phone',
      transformers: 'trim'
      validators: 'phone'
    }
  };

  /**
    * Push the normalized and validated records to the API
    * Check https://importok.io/docs/webhooks.html for more details
    */
  const importRecords = async function (records, meta) {
    return await fetch('https://your-api.com', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(records)
      });
  };

  return (
    &amp;lt;div className="App"&amp;gt;
      &amp;lt;ImportokWizard
        title="importOK Example for React"
        fields={fields}
        onImportReady={importRecords}
      /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Add a CSV importer in your React app
&lt;/h2&gt;

&lt;p&gt;importOK can be used to import data into your application, by embedding a powerful import wizard and leverage your existing API.&lt;/p&gt;

&lt;p&gt;As a guideline, first you need to define the fields, so that your end users can map their data. Then you will need to define some transformers to normalize and clear the data to be imported. Next, you will need to define some validation rules so that you users can get instant feedback when something is wrong. Last, but not least, you will need register your webhooks so that you can actually import the data into your backend system.&lt;/p&gt;

&lt;p&gt;importOK is available as a trial so that you can give it try before purchasing the pro license. At the moment of writing, you can &lt;a href="https://importok.io/pricing.html?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=react-import" rel="noopener noreferrer"&gt;join early&lt;/a&gt; and benefit from a 67% discount, for early adopters.&lt;/p&gt;

&lt;p&gt;Make sure to follow me on &lt;a href="https://dev.to/iacons/"&gt;dev.to&lt;/a&gt;, &lt;a href="https://medium.com/@softius" rel="noopener noreferrer"&gt;Medium&lt;/a&gt; or &lt;a href="https://twitter.com/iacons" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; to read more about importOK or &lt;a href="https://importok.io/index.html?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=react-import" rel="noopener noreferrer"&gt;visit the website&lt;/a&gt; for more details.&lt;/p&gt;

</description>
      <category>react</category>
      <category>import</category>
      <category>csv</category>
    </item>
    <item>
      <title>Create a WeTransfer clone with AWS S3 😎</title>
      <dc:creator>Iacovos Constantinou</dc:creator>
      <pubDate>Sat, 12 Mar 2022 16:08:25 +0000</pubDate>
      <link>https://dev.to/iacons/create-a-clone-of-wetransfer-with-aws-s3-3n48</link>
      <guid>https://dev.to/iacons/create-a-clone-of-wetransfer-with-aws-s3-3n48</guid>
      <description>&lt;p&gt;In this post we are looking at how we can create a clone of WeTransfer, so that we can upload and share our files with others.&lt;/p&gt;

&lt;p&gt;To keep things simple, we are not looking to create the actual user interface. Instead, we are leveraging AWS S3 to upload and share our files via AWS CLI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create S3 Bucket
&lt;/h2&gt;

&lt;p&gt;First, we need to create a new S3 Bucket. To avoid storing the files indefinitely or cleaning up the bucket manually, we can configure a new S3 Lifecycle to automatically cleanup files after 7 days. &lt;/p&gt;

&lt;p&gt;Here is how you can do the above using AWS CLI. Make sure to provide a unique name for your S3 bucket.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws s3api create-bucket \
    --bucket wetransfer-clone \
    --region us-east-1 \
    --create-bucket-configuration LocationConstraint=us-east-1
aws s3api put-bucket-lifecycle \
    --bucket wetransfer-clone \
    --lifecycle-configuration file://lifecycle.json 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The configuration for S3 lifecycle (used in the above commands).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Rules"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Delete files after 7 days"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Prefix"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Enabled"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Expiration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Days"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Upload and share files
&lt;/h2&gt;

&lt;p&gt;Next, we can upload our files and create pre-signed URLs for S3. This will allow to us to share the file without making it public. &lt;/p&gt;

&lt;p&gt;It is a good idea to set the URL expiring before we actually delete the file. In the following example, we expire the URL in two days (172800 seconds). &lt;/p&gt;

&lt;p&gt;If necessary, we can create a new pre-signed URL without re-uploading the file. As mentioned above, the file is automatically deleted in 7 days.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws s3 cp your-large-file.zip s3://wetransfer-clone
aws s3 presign s3://wetransfer-clone/your-large-file.zip \
    --expires-in 172800
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last command will provide the URL that you can share. &lt;/p&gt;

&lt;h2&gt;
  
  
  Large files
&lt;/h2&gt;

&lt;p&gt;Thanks to AWS CLI we don't need to worry about large files.&lt;/p&gt;

&lt;p&gt;When running &lt;code&gt;aws s3 cp&lt;/code&gt;, Amazon S3 automatically performs a multipart upload for large objects. In a multipart upload, a large file is split into multiple parts and uploaded separately to Amazon S3.&lt;/p&gt;

&lt;p&gt;After all the parts are uploaded, AWS S3 combines the parts into a single file. A multipart upload can result in faster uploads and lower chances of failure with large files.&lt;/p&gt;

&lt;p&gt;More details about &lt;a href="https://aws.amazon.com/premiumsupport/knowledge-center/s3-large-file-uploads/" rel="noopener noreferrer"&gt;S3 large file uploads&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;I hope you enjoyed the article and it's something that you can use in real life. &lt;/p&gt;

&lt;p&gt;Make sure to follow me on &lt;a href="https://dev.to/iacons"&gt;dev.to&lt;/a&gt;,  or &lt;a href="https://twitter.com/iacons" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; to read more about web development and how you can automate more stuff!&lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@lunarts?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Volodymyr Hryshchenko&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/copy?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>hacks</category>
    </item>
    <item>
      <title>Automatically generate services and models for your API consumers 🪄</title>
      <dc:creator>Iacovos Constantinou</dc:creator>
      <pubDate>Sun, 16 Jan 2022 13:43:29 +0000</pubDate>
      <link>https://dev.to/iacons/automatically-generate-services-and-models-for-react-2dep</link>
      <guid>https://dev.to/iacons/automatically-generate-services-and-models-for-react-2dep</guid>
      <description>&lt;p&gt;If you have a look at my previous articles here at &lt;a href="https://dev.to/iacons"&gt;dev.to&lt;/a&gt; you would see that I enjoy automating the little things. Especially the boring, tedious stuff that can lead to errors.&lt;/p&gt;

&lt;p&gt;In this article we will be looking on how you can generate code from the OpenAPI spec so that your application is always in sync with the API.&lt;/p&gt;

&lt;p&gt;There are many reasons why you may want to do that, but the main one is that your backend and frontend will adhere to the same schema.&lt;/p&gt;

&lt;p&gt;The article covers mainly frontend applications based on React, Vue or even Angular. However the concept is quite generic and it can be used in backend also regardless using PHP, NodeJS or something else.&lt;/p&gt;

&lt;p&gt;There are a couple of option to achieve this. Before getting started make sure to have a valid OpenAPI spec on your hands.&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenAPI Generator
&lt;/h2&gt;

&lt;p&gt;The first and perhaps the most popular option is to use the &lt;a href="https://openapi-generator.tech/" rel="noopener noreferrer"&gt;OpenAPI generator&lt;/a&gt;.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/OpenAPITools" rel="noopener noreferrer"&gt;
        OpenAPITools
      &lt;/a&gt; / &lt;a href="https://github.com/OpenAPITools/openapi-generator" rel="noopener noreferrer"&gt;
        openapi-generator
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      OpenAPI Generator allows generation of API client libraries (SDK generation), server stubs, documentation and configuration automatically given an OpenAPI Spec (v2, v3)
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Even the solution is based on Java, there is a &lt;a href="https://openapi-generator.tech/docs/installation#npm" rel="noopener noreferrer"&gt;package wrapper&lt;/a&gt; that you can use and install it via npm. Alternatively you can &lt;a href="https://openapi-generator.tech/docs/installation#docker" rel="noopener noreferrer"&gt;run it directly via docker&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are many generators available so depending on your case you can choose to generate code for &lt;a href="https://openapi-generator.tech/docs/generators/typescript-fetch" rel="noopener noreferrer"&gt;Typescript with Fetch&lt;/a&gt;, or &lt;a href="https://openapi-generator.tech/docs/generators/typescript-axios" rel="noopener noreferrer"&gt;Typescript with Axios&lt;/a&gt; or even &lt;a href="https://openapi-generator.tech/docs/generators/javascript/" rel="noopener noreferrer"&gt;plain vanilla javascript&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For each generator, there are different options available that you can use to adjust the generated code to your needs. For example, you can control things like the naming convention for enums and parameters, add a prefix and many more.&lt;/p&gt;

&lt;p&gt;Here is how you can generate the code for a Typescript project with Axios, by using Docker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run --rm \
    -v $PWD:/local openapitools/openapi-generator-cli generate \
    -i /api-specy.aml \
    -g typescript-axios \
    -o /output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  React, Vue and Angular
&lt;/h2&gt;

&lt;p&gt;OpenAPI Generator is a great tool. However, someone might find that it does not fit very well to the NodeJS ecosystem. All of a sudden you have a Java dependency that you need somehow to manage.&lt;/p&gt;

&lt;p&gt;Apart from the OpenAPI generator, there are other solutions available via NPM. Such solutions are based on Javascript of course and they target a particular framework/library.&lt;/p&gt;
&lt;h3&gt;
  
  
  React, Vue &amp;amp; OpenAPI
&lt;/h3&gt;

&lt;p&gt;This package can be used to consume the API directly without pre-generating the services. If necessary though, you can  generate typescript type files (.d.ts) .&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/openapistack" rel="noopener noreferrer"&gt;
        openapistack
      &lt;/a&gt; / &lt;a href="https://github.com/openapistack/openapi-client-axios" rel="noopener noreferrer"&gt;
        openapi-client-axios
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      JavaScript client library for consuming OpenAPI-enabled APIs with axios
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;h3&gt;
  
  
  Angular &amp;amp; OpenAPI
&lt;/h3&gt;

&lt;p&gt;This package generates an Angular module which includes services, configuration and the schema models. It's quite powerful and there are some handy options to adjust the generated code.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/cyclosproject" rel="noopener noreferrer"&gt;
        cyclosproject
      &lt;/a&gt; / &lt;a href="https://github.com/cyclosproject/ng-openapi-gen" rel="noopener noreferrer"&gt;
        ng-openapi-gen
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      An OpenAPI 3.0 codegen for Angular
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Shall we commit the generated code?
&lt;/h2&gt;

&lt;p&gt;While it's possible to commit the generated code, I usually  avoid that. I prefer instead to commit only the OpenAPI spec file and then generate the necessary code right before the build.&lt;/p&gt;

&lt;p&gt;This helps to ensure that the generated code is intact and it has not modified manually. It usually takes a few seconds even for quite large specs so time shouldn't be a problem.&lt;/p&gt;

&lt;p&gt;To do that, you can modify &lt;code&gt;npm run&lt;/code&gt; and &lt;code&gt;npm build&lt;/code&gt; to always execute the code conversion before actually building the app.&lt;/p&gt;

&lt;p&gt;If you would like to commit the code anyway, you could have  Github action that runs, generates the code and then &lt;a href="https://dev.to/iacons/create-a-pr-to-automatically-fix-code-linting-errors-1hf2"&gt;create a PR with all the changes made&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;I hope you enjoyed the article and it's something that you can use to save you some time!&lt;/p&gt;

&lt;p&gt;Make sure to follow me on &lt;a href="https://dev.to/iacons"&gt;dev.to&lt;/a&gt;,  or &lt;a href="https://twitter.com/iacons" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; to read more about web development and how you can automate more stuff!&lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@raimondklavins?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Raimond Klavins&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/generate?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>vue</category>
      <category>angular</category>
      <category>react</category>
    </item>
    <item>
      <title>Synchronize Postman collections with the API</title>
      <dc:creator>Iacovos Constantinou</dc:creator>
      <pubDate>Mon, 10 Jan 2022 15:29:58 +0000</pubDate>
      <link>https://dev.to/iacons/synchronize-postman-collections-with-the-api-172i</link>
      <guid>https://dev.to/iacons/synchronize-postman-collections-with-the-api-172i</guid>
      <description>&lt;p&gt;Working with Postman and APIs can be painful when you don't synchronize Postman collections automatically with the API.&lt;/p&gt;

&lt;p&gt;A common approach to get started with Postman is to import the API schema. This works absolutely fine until the first update on the API spec happens.&lt;/p&gt;

&lt;p&gt;While importing the updated API spec works, it creates a new Collection from scratch. Obviously this doesn't scale well.&lt;/p&gt;

&lt;p&gt;Apart from creating a new Collection each time, any customisation done (tests, scripts, etc) are not transferred to the new Collection.&lt;/p&gt;

&lt;p&gt;In this post we will be looking on how we can automate this and properly update the existing Collection, so that we don't loose our customisation and keep our Postman collections lean.&lt;/p&gt;

&lt;h2&gt;
  
  
  Manually synchronize Postman with API
&lt;/h2&gt;

&lt;p&gt;The first option is to manually update the API Schema directly, without importing. This approach is ideal when we have the URL or the actual file for the updated spec.&lt;/p&gt;

&lt;p&gt;I found that this feature in Postman, even quite powerful is well hidden and quite difficult to discover.&lt;/p&gt;

&lt;p&gt;Here are the steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In the left sidebar, click on the APIs tab and then the API that you would like to update.&lt;/li&gt;
&lt;li&gt;If you are seeing the same API multiple times, go through the list and find the right one by checking the associated collection under the Develop tab.&lt;/li&gt;
&lt;li&gt;Click on the Define tab and paste the updated spec and hit Save&lt;/li&gt;
&lt;li&gt;Then move to the Develop tab. Under the Documentation section hit 'Validate'. This will try to sync the API spec with the Collection.&lt;/li&gt;
&lt;li&gt;In case there any issues like breaking changes you will need to review the issues (press Issues found). Once you do that, you will be redirected to your browser where the changes will be listed one by one.&lt;/li&gt;
&lt;li&gt;You can choose which changes to apply and how to sync with Collection.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fdtqwa8057m3hvyh223qz.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fdtqwa8057m3hvyh223qz.gif" alt="Manually synchronize Postman with API" width="1909" height="964"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Link Postman with an existing repository
&lt;/h2&gt;

&lt;p&gt;Another option is to link the API Spec with an existing repository. This is of course ideal when the API Spec is part of your code-base and you have already access to the right repository.&lt;/p&gt;

&lt;p&gt;Here are the steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In the left sidebar, click on the APIs tab and then the API that you would like to update.&lt;/li&gt;
&lt;li&gt;If you see the same API multiple times, go through the list and find the right one by checking the associated collection under the develop tab.&lt;/li&gt;
&lt;li&gt;Click on the Define tab, hit "Connect Repository" and provide the necessary details. You will need to that once.&lt;/li&gt;
&lt;li&gt;Every time there is an update, you will need to move to the Develop tab and hit "Validate" under the Documentation section. This will try to sync the API spec with the Collection.&lt;/li&gt;
&lt;li&gt;In case there any issues like breaking changes you will need to review the issues (press Issues found). Once you do that, you will be redirected to your browser where the changes will be listed one by one.&lt;/li&gt;
&lt;li&gt;You can choose which changes to apply and how to sync with Collection.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;I hope you find this little trick useful and hopefully it will save you from some time from manual actions.&lt;/p&gt;

&lt;p&gt;Have also a look on how you can &lt;a href="https://dev.to/iacons/automatic-authorization-in-postman-34dk"&gt;automate authorization in Postman&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Make sure to follow me on &lt;a href="https://dev.to/iacons"&gt;dev.to&lt;/a&gt;,  or &lt;a href="https://twitter.com/iacons" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; to read more about web development and other development topics.&lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@timmossholder?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Tim Mossholder&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/push?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>postman</category>
      <category>api</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Access and update local files, from your browser!</title>
      <dc:creator>Iacovos Constantinou</dc:creator>
      <pubDate>Mon, 27 Dec 2021 13:57:40 +0000</pubDate>
      <link>https://dev.to/iacons/access-and-update-local-files-from-your-browser-51e0</link>
      <guid>https://dev.to/iacons/access-and-update-local-files-from-your-browser-51e0</guid>
      <description>&lt;p&gt;Consider a web based image editor. You upload the photo, edit whatever is necessary and then download a copy of the modified image.&lt;/p&gt;

&lt;p&gt;Wouldn't be better if we could simplify the process and directly update local files, like native apps?&lt;/p&gt;

&lt;p&gt;In this post we will be examining how we can achieve this by using the File System Access API.&lt;/p&gt;

&lt;h2&gt;
  
  
  File System Access API
&lt;/h2&gt;

&lt;p&gt;The File System Access API allows web apps to read or save changes directly to files and folders on the user's device. &lt;/p&gt;

&lt;p&gt;In particular, here is what we can do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read a local file&lt;/li&gt;
&lt;li&gt;Specify file types and a starting directory&lt;/li&gt;
&lt;li&gt;Open a directory and enumerating its contents &lt;/li&gt;
&lt;li&gt;Update the contents of a local file&lt;/li&gt;
&lt;li&gt;Create a new file&lt;/li&gt;
&lt;li&gt;Delete files and folders&lt;/li&gt;
&lt;li&gt;Move files around&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that this feature is &lt;a href="https://caniuse.com/native-filesystem-api" rel="noopener noreferrer"&gt;not available to all browsers&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Markdown editor
&lt;/h2&gt;

&lt;p&gt;To illustrate some of the FileSystem API features we will be building a Markdown editor to read and write Markdown files from/to the local disk.&lt;/p&gt;

&lt;p&gt;Let's go!&lt;/p&gt;

&lt;h2&gt;
  
  
  Read local files
&lt;/h2&gt;

&lt;p&gt;First let's see how we can read a local file. The following shows a file picker dialog box, and prompts the user to select any markdown file.&lt;/p&gt;

&lt;p&gt;A file handle is returned which can be used to read the file contents. Also, we can use the same handle to update its contents later on.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let fileHandle = null;
const options = {
  types: [
    {
      description: 'Markdown Files',
      accept: {
        'text/markdown': ['.md'],
      },
    },
  ],
};

[fileHandle] = await window.showOpenFilePicker(options);

const file = await fileHandle.getFile();
const contents = await file.text();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Update local file
&lt;/h2&gt;

&lt;p&gt;Now let's see how we can update a local file and overwrite its contents. The following uses the file handle returned while reading the file to update its contents.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const contents = '...';
const writable = await fileHandle.createWritable();
await writable.write(contents);
await writable.close();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Glue the above code snippets with a Markdown editor and you should be able to load markdown from you local disk, make and persist changes.&lt;/p&gt;

&lt;p&gt;Check out this &lt;a href="https://stackblitz.com/edit/web-platform-xtfna4?file=index.html" rel="noopener noreferrer"&gt;Markdown editor&lt;/a&gt;, made using &lt;a href="https://simplemde.com/" rel="noopener noreferrer"&gt;SimpleMDE&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For a better experience, click on the "Open in new Window".&lt;/p&gt;

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

&lt;p&gt;Throughout this post we went through the basics of FileSystem API and examined how it can be used to access and manipulate our local files.&lt;/p&gt;

&lt;p&gt;Make sure to follow me on &lt;a href="https://twitter.com/iacons" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; or &lt;a href="https://dev.to/iacons"&gt;dev.to&lt;/a&gt; to read more about web development.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>browser</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Improve collaboration across teams with VSCode</title>
      <dc:creator>Iacovos Constantinou</dc:creator>
      <pubDate>Sat, 27 Nov 2021 11:18:41 +0000</pubDate>
      <link>https://dev.to/iacons/vscode-for-teams-31pb</link>
      <guid>https://dev.to/iacons/vscode-for-teams-31pb</guid>
      <description>&lt;p&gt;VSCode is definitely one of the most popular code editors. In this post, we will be looking on how you can use VSCode to improve collaboration and consistency across team members.&lt;/p&gt;

&lt;p&gt;The following can be applied to any project, regardless the language being used. The only assumption is that you are using VSCode, Git and maybe Docker. &lt;/p&gt;

&lt;h2&gt;
  
  
  EditorConfig
&lt;/h2&gt;

&lt;p&gt;EditorConfig is not VSCode specific but helps maintaining consistent coding styles for multiple developers working on the same project across various editors and IDEs. &lt;/p&gt;

&lt;p&gt;It is one of the easy wins but for some strange reason you will find this missing from many code repositories.&lt;/p&gt;

&lt;p&gt;In short you can define various config styles like charset, end-of-line and indentation styles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true

# Matches multiple files with brace expansion notation
# Set default charset and indent style/size
[*.{js,json,php}]
charset = utf-8
indent_style = space
indent_size = 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check &lt;a href="https://editorconfig.org/" rel="noopener noreferrer"&gt;EditorConfig website&lt;/a&gt; and &lt;a href="https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig" rel="noopener noreferrer"&gt;EditorConfig extension&lt;/a&gt; for more details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Workspace recommended extensions
&lt;/h2&gt;

&lt;p&gt;VSCode allows you to define a list of recommended extensions under &lt;code&gt;.vscode/extensions.json&lt;/code&gt;. Then developers will be prompted automatically to install these extensions.&lt;/p&gt;

&lt;p&gt;Keep in mind that this is only a recommendation and the extensions will be installed only if the developers accept.&lt;/p&gt;

&lt;p&gt;I find this very useful across large teams but also for on-boarding new team members.&lt;/p&gt;

&lt;p&gt;While you can go crazy and add almost any extension possible here, it's better to keep this to the absolute necessary extensions.&lt;/p&gt;

&lt;p&gt;Check &lt;a href="https://code.visualstudio.com/docs/editor/extension-marketplace#_workspace-recommended-extensions" rel="noopener noreferrer"&gt;Workspace recommended extensions&lt;/a&gt; for more details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remote pair programming
&lt;/h2&gt;

&lt;p&gt;VSCode Live Share is an excellent option for remote pair programming and collaboration in general. The process to setup it is fairly simple and straightforward.&lt;/p&gt;

&lt;p&gt;Apart from installing the extension, you only need to press a button to share your workspace with other team members.&lt;/p&gt;

&lt;p&gt;The remote party can view the same code as you, even if you haven't pushed your changes yet. However, this happens from their own setup - which means both parties can use completely different VSCode Themes and Extensions without a problem!&lt;/p&gt;

&lt;p&gt;Check &lt;a href="https://code.visualstudio.com/learn/collaboration/live-share" rel="noopener noreferrer"&gt;Collaborate with Live Share&lt;/a&gt; for more details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Developer with Docker
&lt;/h2&gt;

&lt;p&gt;Containerized development environments are really useful to establish consistency and on-boarding new developers to the team. However, learning Docker can be cumbersome for some people. VSCode builds on top of this concept and goes a few steps further.&lt;/p&gt;

&lt;p&gt;In particular:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can spin off a container (or a set of containers) without leaving VSCode.&lt;/li&gt;
&lt;li&gt;You can define various settings like which terminal shell to use by default, commands to run after the container is created.&lt;/li&gt;
&lt;li&gt;You can also define the extensions, like described in the first section. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All the above go into &lt;code&gt;devcontainer.json&lt;/code&gt; which is basically a config file that determines how your dev container gets built and started.&lt;/p&gt;

&lt;p&gt;Check &lt;a href="https://code.visualstudio.com/docs/remote/containers" rel="noopener noreferrer"&gt;Development inside a Container&lt;/a&gt; for more details.&lt;/p&gt;

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

&lt;p&gt;Throughout this post we went through a few ways where VSCode can be used to improve collaboration and consistency across the team.&lt;/p&gt;

&lt;p&gt;Make sure to follow me on &lt;a href="https://dev.to/iacons"&gt;dev.to&lt;/a&gt;, &lt;a href="https://medium.com/@softius" rel="noopener noreferrer"&gt;Medium&lt;/a&gt; or &lt;a href="https://twitter.com/iacons" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; to read more about web development.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>vscode</category>
      <category>github</category>
    </item>
    <item>
      <title>Create a PR to automatically fix code linting errors</title>
      <dc:creator>Iacovos Constantinou</dc:creator>
      <pubDate>Thu, 18 Nov 2021 18:36:51 +0000</pubDate>
      <link>https://dev.to/iacons/create-a-pr-to-automatically-fix-code-linting-errors-1hf2</link>
      <guid>https://dev.to/iacons/create-a-pr-to-automatically-fix-code-linting-errors-1hf2</guid>
      <description>&lt;p&gt;This Github workflow aims to automatically fix as many lint errors as possible.&lt;/p&gt;

&lt;p&gt;In particular, it executes eslint with &lt;code&gt;--fix&lt;/code&gt; argument, so that we report and fix lint errors. Note that not all lint errors can be fixed automatically.&lt;/p&gt;

&lt;p&gt;If any errors were fixed by &lt;code&gt;eslint&lt;/code&gt;, a new PR is created including the changes made. The base is always the current branch while the assignee is the PR author. &lt;/p&gt;

&lt;p&gt;This allows the PR author to review the changes and if needed merge them into the initial PR.&lt;/p&gt;

&lt;p&gt;On top of that, the PR is marked with the label &lt;code&gt;autofix&lt;/code&gt;.&lt;/p&gt;

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

&lt;p&gt;The repository can be found &lt;a href="https://github.com/softius/actions-auto-improve-code-style/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On purpose there is a lint error in &lt;code&gt;index.js&lt;/code&gt; and you can review the PR created &lt;a href="https://github.com/softius/actions-auto-improve-code-style/pull/5" rel="noopener noreferrer"&gt;here&lt;/a&gt; to fix this. .&lt;/p&gt;

&lt;h3&gt;
  
  
  Submission Category:
&lt;/h3&gt;

&lt;p&gt;Maintainer Must-Haves&lt;/p&gt;

&lt;h3&gt;
  
  
  Yaml File or Link to Code
&lt;/h3&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/softius" rel="noopener noreferrer"&gt;
        softius
      &lt;/a&gt; / &lt;a href="https://github.com/softius/actions-auto-improve-code-style" rel="noopener noreferrer"&gt;
        actions-auto-improve-code-style
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Create a PR automatically to fix code linting errors.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;actions-auto-improve-code-style&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;This GitHub workflow executes eslint with &lt;code&gt;--fix&lt;/code&gt; argument, so that we can not only capture but also automatically fix as many as possible.&lt;/p&gt;
&lt;p&gt;If any errors were fixed by &lt;code&gt;eslint&lt;/code&gt;, a new PR is created including the changes made. The base is always the current branch while the assignee is the PR author.&lt;/p&gt;
&lt;p&gt;This allows the PR author to review the changes and if needed merge them into into the initial PR.&lt;/p&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/softius/actions-auto-improve-code-style" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h3&gt;
  
  
  Additional Resources / Info
&lt;/h3&gt;

&lt;p&gt;While this workflow uses eslint, the same approach can be used with phpcs/phpcbf and other linters.&lt;/p&gt;

&lt;p&gt;To customize the workflow further, you can refer to this &lt;a href="https://github.com/peter-evans/create-pull-request" rel="noopener noreferrer"&gt;action&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Make sure to follow me on &lt;a href="https://dev.to/iacons"&gt;dev.to&lt;/a&gt;, &lt;a href="https://medium.com/@softius" rel="noopener noreferrer"&gt;Medium&lt;/a&gt; or &lt;a href="https://twitter.com/iacons" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; to read more about PHP, Docker and other dev topics.&lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@kensuarez?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Ken Suarez&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/fix?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;  &lt;/p&gt;

</description>
      <category>actionshackathon21</category>
      <category>github</category>
      <category>opensource</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Deploy to AWS ECS with Github actions 🚀</title>
      <dc:creator>Iacovos Constantinou</dc:creator>
      <pubDate>Thu, 11 Nov 2021 18:48:33 +0000</pubDate>
      <link>https://dev.to/iacons/deploy-to-aws-ecs-with-github-actions-3365</link>
      <guid>https://dev.to/iacons/deploy-to-aws-ecs-with-github-actions-3365</guid>
      <description>&lt;p&gt;Consistency and automations are key to every project. In this article we will be looking on how we can automate deployments to AWS ECS.&lt;/p&gt;

&lt;p&gt;It is a very simple solution which requires almost zero coding. Yet, it's quite powerful as it allows you to deploy automatically every time there is a change in your code. &lt;/p&gt;

&lt;p&gt;We will be using Github actions to achieve that. In particular, we will cover the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build and deploy a new docker image to ECR&lt;/li&gt;
&lt;li&gt;Retrieve and update the task definition with the new image tag&lt;/li&gt;
&lt;li&gt;Update the corresponding services and force redeployment&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Get started
&lt;/h2&gt;

&lt;p&gt;First, you will need to register your AWS credentials as Secrets in your Github project. Assuming that you create two new Secrets &lt;code&gt;AWS_ACCESS_KEY_ID&lt;/code&gt; and &lt;code&gt;AWS_SECRET_ACCESS_KEY&lt;/code&gt;, you should be able to configure AWS in your Github action as per below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: us-east-2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above registers AWS credentials so that they are available for the next action.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy docker image to ECR
&lt;/h2&gt;

&lt;p&gt;Next we need to build and deploy our image. Most likely, this will be a time consuming action, especially if you have a large number of third party dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    - name: Login to Amazon ECR
      id: login-ecr
      uses: aws-actions/amazon-ecr-login@v1
    - name: Build, tag, and push image to Amazon ECR
      env:
        ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
        ECR_REPOSITORY: your-ecr-repot
        IMAGE_TAG: ${{ github.sha }}
      run: |
        docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
        docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
        echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There a couple of things that you will need to adjust here. First you will need to provide the repository to deploy to for env &lt;code&gt;ECR_REPOSITORY&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then you might need to change how the &lt;code&gt;IMAGE_TAG&lt;/code&gt; should look like. By default it will include only the commit SHA. Personally, I prefer to include also the date the image was build.&lt;/p&gt;

&lt;h2&gt;
  
  
  Update ECS task definition and Deploy!
&lt;/h2&gt;

&lt;p&gt;Once the image is build and pushed to ECR, it is time to update the task definition and specify the new image tag. Depending on your setup you may have one or more tasks for the same image tag. In that case, you will need to repeat these steps.&lt;/p&gt;

&lt;p&gt;Ideally, you should have the the task definition as part of your code base. In the following examples, we are assuming that the file is called &lt;code&gt;task-definition.json&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In case you don't, you can retrieve the task definition dynamically as per below. However, you might need to remove some parts of the definition, before pushing back to AWS - in that case &lt;code&gt;jq&lt;/code&gt; is quite helpful.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    - name: Download task definition
      run: |
        aws ecs describe-task-definition --task-definition my-task-definition-family --query taskDefinition &amp;gt; task-definition.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following actions specify the new image tag in our task definition and force a deployment to our service. The last option &lt;code&gt;wait-for-service-stability&lt;/code&gt; is important since it ensures that the service will reach a stable stage before proceeding further.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    - name: Fill in the new image ID in the Amazon ECS task definition
      id: task-def
      uses: aws-actions/amazon-ecs-render-task-definition@v1
      with:
        task-definition: task-definition.json
        container-name: my-container
        image: ${{ steps.build-image.outputs.image }}

    - name: Deploy Amazon ECS task definition
      uses: aws-actions/amazon-ecs-deploy-task-definition@v1
      with:
        task-definition: ${{ steps.task-def.outputs.task-definition }}
        service: my-service
        cluster: my-cluster
        wait-for-service-stability: true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similar to the previous actions there a few things that you will need to adjust here. Replace &lt;code&gt;my-container&lt;/code&gt;, &lt;code&gt;my-service&lt;/code&gt; and &lt;code&gt;my-cluster&lt;/code&gt; with your setup details.&lt;/p&gt;

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

&lt;p&gt;Having automated deployments to ECS is quite easy with Github actions. Yet, it's quite powerful as it allows you to deploy automatically every time there is a change in your code.&lt;/p&gt;

&lt;p&gt;You can also use the same approach to deploy from any branch or even deploy manually, if needed.&lt;/p&gt;

&lt;p&gt;Make sure to follow me on &lt;a href="https://dev.to/iacons"&gt;dev.to&lt;/a&gt;, &lt;a href="https://medium.com/@softius" rel="noopener noreferrer"&gt;Medium&lt;/a&gt; or &lt;a href="https://twitter.com/iacons" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; to read more about PHP, Docker and other dev topics.  &lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@spacex?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;SpaceX&lt;/a&gt; on &lt;a href="//ttps://unsplash.com/s/photos/rocket?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;  &lt;/p&gt;

</description>
      <category>aws</category>
      <category>github</category>
      <category>actions</category>
      <category>devops</category>
    </item>
    <item>
      <title>Automatic authorization in Postman 🔒</title>
      <dc:creator>Iacovos Constantinou</dc:creator>
      <pubDate>Thu, 04 Nov 2021 18:04:47 +0000</pubDate>
      <link>https://dev.to/iacons/automatic-authorization-in-postman-34dk</link>
      <guid>https://dev.to/iacons/automatic-authorization-in-postman-34dk</guid>
      <description>&lt;p&gt;Working with short lived JWTs can be painful when you don't have an automated mechanism in place to refresh the token. This is quite common when setting up a new collection in Postman. &lt;/p&gt;

&lt;p&gt;In this post we will be looking on how we can automate this, so that we retrieve, refresh and use the token right before each API call.&lt;/p&gt;

&lt;h2&gt;
  
  
  Environment configuration
&lt;/h2&gt;

&lt;p&gt;Before we get started, we need to define a few environment variables in Postman. These are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;baseUrl&lt;/code&gt; API URL&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;username&lt;/code&gt; The username to connect with&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;password&lt;/code&gt; The password for the above username&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unfortunately, there's no way to display the password as a ... password field. Postman will store and display everything in clear text, so have that in mind.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pre-request script
&lt;/h2&gt;

&lt;p&gt;Then, we need to edit the collection and configure the Pre-request script code. To do that, right click on the collection, select edit and then "Pre-request scripts"&lt;/p&gt;

&lt;p&gt;Here is an example. As you can see the implementation is quite generic so most likely you will need to adjust a few things to make that work for you.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;Initially we check whether we have a token stored. If not we go ahead and retrieve a new one along with expiration time.&lt;/p&gt;

&lt;p&gt;We store both in environment variables. You don't need to create these variables, the script will create them whenever necessary.&lt;/p&gt;

&lt;p&gt;If the token exists, we check also whether it has expired.  In that case, we refresh the token and similarly to above we store the new token and its expiry date.&lt;/p&gt;

&lt;p&gt;In either case, the token will be stored in a new environment variable called &lt;code&gt;accessToken&lt;/code&gt;. This can be used to define the default authorization method. To do this right click on the collection, select edit, then Authorisation and use the variable &lt;code&gt;{{accessToken}}&lt;/code&gt;.&lt;/p&gt;

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

&lt;p&gt;I hope you find this approach useful and hopefully it will save you from some time from manual actions.&lt;/p&gt;

&lt;p&gt;Make sure to follow me on &lt;a href="https://dev.to/iacons"&gt;dev.to&lt;/a&gt;, &lt;a href="https://medium.com/@softius" rel="noopener noreferrer"&gt;Medium&lt;/a&gt; or &lt;a href="https://twitter.com/iacons" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; to read more about PHP, Docker and other dev topics.&lt;/p&gt;

</description>
      <category>postman</category>
      <category>javascript</category>
      <category>jwt</category>
      <category>api</category>
    </item>
    <item>
      <title>Faster Docker builds with composer install ⚡</title>
      <dc:creator>Iacovos Constantinou</dc:creator>
      <pubDate>Thu, 28 Oct 2021 08:17:11 +0000</pubDate>
      <link>https://dev.to/iacons/faster-docker-builds-with-composer-install-3opj</link>
      <guid>https://dev.to/iacons/faster-docker-builds-with-composer-install-3opj</guid>
      <description>&lt;p&gt;When working with PHP projects and Docker, it is a good practice to include all the third party dependencies coming from &lt;code&gt;composer install&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This really helps to reduce the time needed to spin off a new container. However, when not done right, it can significantly increase the build time.&lt;/p&gt;

&lt;p&gt;Throughout this post we will see how we can avoid this and optimize &lt;code&gt;composer install&lt;/code&gt; for docker builds.&lt;/p&gt;

&lt;p&gt;Consider the following simple example, where we copy our source code before executing &lt;code&gt;composer install&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WORKDIR /my-cool-app
COPY . ./
RUN composer install --no-dev --no-interaction --optimize-autoloader
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While this works, there is a major flaw; we don't leverage Docker cache. Every time there is a change in our source code, the docker cache for &lt;code&gt;composer install&lt;/code&gt; will be invalidated.&lt;/p&gt;

&lt;p&gt;In real life, this will happen with almost every build, causing &lt;code&gt;composer install&lt;/code&gt; to be executed even when there are no changes in our dependencies.&lt;/p&gt;

&lt;p&gt;Not efficient, that's for sure. But how can we avoid this?  Consider the following example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WORKDIR /my-cool-app
COPY composer.json ./
COPY composer.lock ./
RUN composer install --no-dev --no-interaction --no-autoloader --no-scripts
COPY . ./
RUN composer dump-autoload --optimize
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we are copying only &lt;code&gt;composer.json&lt;/code&gt; and &lt;code&gt;composer.lock&lt;/code&gt; (instead of copying the entire source) right before doing &lt;code&gt;composer install&lt;/code&gt;. This is enough to take advantage of docker cache and &lt;code&gt;composer install&lt;/code&gt; will be executed only when &lt;code&gt;composer.json&lt;/code&gt; or &lt;code&gt;composer.lock&lt;/code&gt; have indeed changed!&lt;/p&gt;

&lt;p&gt;One downside is that we have to skip autoload generation and script execution since the source code is not available yet. Easy fix though. This can be done as the last step and it shouldn't affect the performance anyway.&lt;/p&gt;

&lt;p&gt;As part of this post we saw how we can re-structure our Dockerfile for PHP projects and take better advantage of Docker cache. I hope that's helpful for your project!&lt;/p&gt;

&lt;p&gt;Make sure to follow me on &lt;a href="https://dev.to/iacons"&gt;dev.to&lt;/a&gt;, &lt;a href="https://medium.com/@softius" rel="noopener noreferrer"&gt;Medium&lt;/a&gt; or &lt;a href="https://twitter.com/iacons" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; to read more about PHP, Docker and other dev topics.&lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@chuttersnap?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;CHUTTERSNAP&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/speed?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;  &lt;/p&gt;

</description>
      <category>php</category>
      <category>composer</category>
      <category>docker</category>
    </item>
    <item>
      <title>Why we chose Tailwind CSS</title>
      <dc:creator>Iacovos Constantinou</dc:creator>
      <pubDate>Thu, 21 Oct 2021 15:55:04 +0000</pubDate>
      <link>https://dev.to/iacons/why-we-chose-tailwind-css-f30</link>
      <guid>https://dev.to/iacons/why-we-chose-tailwind-css-f30</guid>
      <description>&lt;p&gt;This is the second part of a three posts series that discuss our switch from Bootstrap to Tailwind CSS.&lt;/p&gt;

&lt;p&gt;This post in particular reflects on what problems Tailwind has solved for us. Throughout the first post, a little story was provided as &lt;a href="https://dev.to/iacons/tailwind-css-a-year-in-review-laa"&gt;background to our project&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Naming is hard
&lt;/h2&gt;

&lt;p&gt;We all know that there are only two hard things in Computer Science: cache invalidation and naming things. Naming is especially hard in CSS.&lt;/p&gt;

&lt;p&gt;You start with a narrow scope class name i.e. &lt;code&gt;.page-header&lt;/code&gt; only then to realize that you can use the same CSS properties for &lt;code&gt;.panel-title&lt;/code&gt;. You start with a wide scope class name and after a while you start thinking that you need to narrow down the scope or combine it with other utility classes.&lt;/p&gt;

&lt;p&gt;There are of course solutions to this problem like BEM, a naming convention standard for CSS class names. I always found BEM a bit too verbose for my taste.&lt;/p&gt;

&lt;p&gt;Tailwind takes the naming away in most of the cases, especially when you combine it with a component based solution.&lt;/p&gt;

&lt;p&gt;Let's say you are building a new card component and you want to style the card header. You could introduce a new class i.e. &lt;code&gt;.card__header&lt;/code&gt;, then define the CSS properties and maybe do same for the child components.&lt;/p&gt;

&lt;p&gt;While the above works, the chances are that you are not going to use the same class anywhere else. With tailwind approach you will be ending doing something like &lt;code&gt;&amp;lt;div class="text-xl text-gray-500 mx-5"&amp;gt;&lt;/code&gt; and you are done.&lt;/p&gt;

&lt;p&gt;Looking back, this reason alone might was enough to reconsider Tailwind CSS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code reviews
&lt;/h2&gt;

&lt;p&gt;One of the problems when reviewing front-end code is how the code spans across different locations. For example, a new class is added to a particular element and then you have to check how the corresponding class was defined in another place. &lt;/p&gt;

&lt;p&gt;This over-complicates reviewing. Especially when there are changes on existing styles and you are not sure how this could affect the entire application.&lt;/p&gt;

&lt;p&gt;Code review is much easier to go through, when using utility classes along with HTML in one place. You can tell from the classes used what has been changed, without moving to the CSS. Also, you don't have to worry that the change had a negative affect somewhere else.&lt;/p&gt;

&lt;p&gt;The above benefit was not initially clear. It slowly became obvious as we proceeded with the implementation and adoption of Tailwind CSS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Built for components
&lt;/h2&gt;

&lt;p&gt;Tailwind CSS is ideal when working with any component based library or framework like Angular and Vue. It helps you to improve how you approach re-usability.&lt;/p&gt;

&lt;p&gt;Usually in such cases you have two ways to approach re-usability: via CSS classes and components.&lt;/p&gt;

&lt;p&gt;Tailwind CSS, being a utility-first CSS framework, gives a great emphasis on re-usability via CSS classes. The scope of each CSS class is very narrow, which means that it can be re-used very easily. So this part of re-usability is pretty much well covered.&lt;/p&gt;

&lt;p&gt;With the above in mind, the mindset is shifted towards component based re-usability. So instead of creating new classes that can be shared across components, we started thinking more about how we could solve this by introducing a new component.&lt;/p&gt;

&lt;p&gt;Less rules to follow and one less layer of abstraction.&lt;/p&gt;

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

&lt;p&gt;Throughout this post I analyzed the main three reasons why we eventually went with Tailwind CSS. There are other reasons of course, but these are the main ones.&lt;/p&gt;

&lt;p&gt;As part of the next and final post of this series, we will be looking at some challenges and myths around Taiwlind CSS.&lt;/p&gt;

&lt;p&gt;Make sure to follow me on &lt;a href="https://dev.to/iacons"&gt;dev.to&lt;/a&gt;, &lt;a href="https://medium.com/@softius" rel="noopener noreferrer"&gt;Medium&lt;/a&gt; or &lt;a href="https://twitter.com/iacons" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; to read more about Angular, Tailwind and other dev topics.&lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@rossf?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Ross Findon&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/transformation?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;  &lt;/p&gt;

</description>
      <category>tailwindcss</category>
      <category>css</category>
      <category>story</category>
    </item>
    <item>
      <title>Tailwind CSS - A year in review</title>
      <dc:creator>Iacovos Constantinou</dc:creator>
      <pubDate>Thu, 14 Oct 2021 18:08:35 +0000</pubDate>
      <link>https://dev.to/iacons/tailwind-css-a-year-in-review-laa</link>
      <guid>https://dev.to/iacons/tailwind-css-a-year-in-review-laa</guid>
      <description>&lt;p&gt;A year ago we made our switch from Bootstrap to Tailwind CSS. &lt;/p&gt;

&lt;p&gt;There is a large number of opinionated posts and tweets around this topic. This post series is a reflection on what problems Tailwind has solved for us, what challenges created and how our opinion has shaped after one year of usage. &lt;/p&gt;

&lt;p&gt;This is the first part of a three posts series.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some background
&lt;/h2&gt;

&lt;p&gt;I have been following Tailwind CSS since its early days. I had the chance to try it in some small projects, mostly to experiment with the framework; but never got the chance to try it out in the battle field, until last year.&lt;/p&gt;

&lt;p&gt;A new project was around the corner. The team had strong experience with Bootstrap 5, VueJS and Angular. We went with Angular and decided to give Tailwind CSS a try. &lt;/p&gt;

&lt;p&gt;The idea was to experiment while building the application layout and a few screens. This should be sufficient to give us an idea what it was like working with Tailwind CSS. The plan was to spend two weeks to do the above and then reflect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initial reaction
&lt;/h2&gt;

&lt;p&gt;The initial reaction was negative, like very negative. Here are a few comments that were brought up during the review.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How is this different from inline styles?&lt;/li&gt;
&lt;li&gt;How can someone remember all these classes?&lt;/li&gt;
&lt;li&gt;Our templates are getting huge!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It was clear that no-one got excited. We briefly discussed that most likely it is another framework hype and decided to re-start using Bootstrap. &lt;/p&gt;

&lt;p&gt;At this point the team had to re-create everything using Bootstrap for the next two weeks. And then something unexpected happened!&lt;/p&gt;

&lt;h2&gt;
  
  
  The unexpected
&lt;/h2&gt;

&lt;p&gt;At the next scheduled demo (end of the first week), team started showcasing progress on some new screens. "Oh, you already re-did everything in Bootstrap?", I asked only to found out that this process didn't even last a few days.&lt;/p&gt;

&lt;p&gt;So what happened behind the scenes? While switching to Bootstrap, some of the strong points of Tailwind started to shine. Hence, it felt more natural for the team to continue exploring Tailwind further. &lt;/p&gt;

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

&lt;p&gt;Over the next posts, we will be analyzing why we eventually sticked with Tailwind and what challenges we faced along the way.&lt;/p&gt;

&lt;p&gt;Make sure to follow me on &lt;a href="https://dev.to/iacons"&gt;dev.to&lt;/a&gt;, &lt;a href="https://medium.com/@softius" rel="noopener noreferrer"&gt;Medium&lt;/a&gt; or &lt;a href="https://twitter.com/iacons" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; to read more about Angular, Tailwind and other dev topics.&lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@dsmacinnes?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Danielle MacInnes&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/story?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>tailwindcss</category>
      <category>css</category>
      <category>story</category>
    </item>
  </channel>
</rss>
