<?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: Sorin Costea</title>
    <description>The latest articles on DEV Community by Sorin Costea (@sorincostea).</description>
    <link>https://dev.to/sorincostea</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%2F348271%2F90d1e2d5-2d0c-4ab1-ad05-cf5e53d24a9f.jpeg</url>
      <title>DEV Community: Sorin Costea</title>
      <link>https://dev.to/sorincostea</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sorincostea"/>
    <language>en</language>
    <item>
      <title>HTTPS with mutual authentication</title>
      <dc:creator>Sorin Costea</dc:creator>
      <pubDate>Fri, 24 Jun 2022 12:51:17 +0000</pubDate>
      <link>https://dev.to/sorincostea/https-with-mutual-authentication-2i1c</link>
      <guid>https://dev.to/sorincostea/https-with-mutual-authentication-2i1c</guid>
      <description>&lt;p&gt;If you want to secure your existing Tomcat server or don’t use Spring Boot there’s quite a lack of up-to-date configuration information, especially because all existing examples were made invalid by the arrival of Tomcat 10 (yes it already happened).&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6eqcfg4jdu5k4ostd816.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%2F6eqcfg4jdu5k4ostd816.png" alt="xml contents"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So because I needed a future proof SSL configuration that is also understandable to the security beginner (also me), here’s what I come up with:&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;Connector port="8443" protocol="org.apache.coyote.http11.Http11AprProtocol" scheme="https" secure="true" maxThreads="150" SSLEnabled="true" defaultSSLHostConfigName="localhost"&amp;gt;
    &amp;lt;SSLHostConfig hostName="localhost" truststoreFile="server-truststore.jks" protocols="all" truststorePassword="changeit" certificateVerification="required"&amp;gt;
        &amp;lt;Certificate certificateKeystoreFile="server-keys.p12" certificateKeystoreType="PKCS12" certificateKeystorePassword="changeit" certificateKeyAlias="server" type="RSA"/&amp;gt;
    &amp;lt;/SSLHostConfig&amp;gt;
&amp;lt;/Connector&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SSL connection&lt;/strong&gt;&lt;br&gt;
So you want that browsers connecting to your web server to see the trustworthy padlock icon in the address bar, by connecting with HTTPS? This means you need HTTP over SSL, or TLS actually.&lt;br&gt;
For this the server will send the appropriate certificate to the browsers, identifying itself with it. This server certificate is stored in a keystore – named like this because it stores the keys owned by the server. The element describes the location of this keystore, its type (default JKS, can be PKCS12) plus information on what’s inside and how to open it.&lt;br&gt;
Of course the certificate sent by the server must be signed by a CA the client is trusting, otherwise you’ll get SSL with a big warning “self signed certificate” – still protecting the communication from network snooping but saying nothing about the real identity of the server.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Client authentication&lt;/strong&gt;&lt;br&gt;
So you want that only CERTAIN browsers can connect to your web server? Not a very common requirement, quite unusual for browser connections. But you’ll need it more if you build a REST API server with HTTPS and you must restrict the access to this API.&lt;br&gt;
For this the server will need to trust the client, and the client must identify itself with a certificate. The certificates the server trusts are stored in a, guess what, truststore, which is configured in the SSLHostConfig element. You also need to force this client authentication by mandating certificateVerification.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Of course you can store all the certificates in one store and use it for both above purposes but it’s always easier to explain if theyre split according to role. Most of the time all these certificates will be x509 certificates anyway, thus also the common discussion topic of “x509 mutual authentication”. &lt;/p&gt;

&lt;p&gt;Two notes: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;    if for testing reasons you decide to create your own certificates, do yourself a favor and use only Keytool and namely that one from the Java version you are using on the server. Don’t mix it with different Keytool versions or with OpenSSL.&lt;/li&gt;
&lt;li&gt;    for all the above to work you’ll need the APR library for Tomcat as only this one supports client authentication. See also the usage of Http11AprProtocol…&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Helpful links:&lt;br&gt;
&lt;a href="https://tomcat.apache.org/tomcat-8.5-doc/config/http.html" rel="noopener noreferrer"&gt;https://tomcat.apache.org/tomcat-8.5-doc/config/http.html&lt;/a&gt;&lt;br&gt;
&lt;a href="https://tomcat.apache.org/tomcat-8.5-doc/ssl-howto.html" rel="noopener noreferrer"&gt;https://tomcat.apache.org/tomcat-8.5-doc/ssl-howto.html&lt;/a&gt;&lt;br&gt;
&lt;a href="https://docs.spring.io/spring-security/site/docs/4.0.x/reference/html/x509.html" rel="noopener noreferrer"&gt;https://docs.spring.io/spring-security/site/docs/4.0.x/reference/html/x509.html&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.baeldung.com/java-https-client-certificate-authentication" rel="noopener noreferrer"&gt;https://www.baeldung.com/java-https-client-certificate-authentication&lt;/a&gt;&lt;br&gt;
&lt;a href="https://medium.com/ing-tech-romania/a-simple-mtls-guide-for-spring-boot-microservices-c6bfc9878369" rel="noopener noreferrer"&gt;https://medium.com/ing-tech-romania/a-simple-mtls-guide-for-spring-boot-microservices-c6bfc9878369&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>tomcat</category>
      <category>java</category>
      <category>programming</category>
    </item>
    <item>
      <title>Lessons learned after losing the Terraform state file</title>
      <dc:creator>Sorin Costea</dc:creator>
      <pubDate>Wed, 31 Mar 2021 18:18:25 +0000</pubDate>
      <link>https://dev.to/sorincostea/lessons-learned-after-losing-the-terraform-state-file-48ld</link>
      <guid>https://dev.to/sorincostea/lessons-learned-after-losing-the-terraform-state-file-48ld</guid>
      <description>&lt;p&gt;&lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; is a &lt;a href="https://en.wikipedia.org/wiki/Infrastructure_as_code" rel="noopener noreferrer"&gt;infrastructure-as-code&lt;/a&gt; (IaC) tool where you describe the desired pieces of infrastructure you need in some config files, then Terraform will create the infrastructure in your cloud provider of choice. Or delete them, or modify them – because Terraform compares the cloud infrastructure state with the expected one. And the expected state is kept in… a state file, right. So if the state file gets lost, Terraform will think it never created those resources in the first place and will try to duplicate everything. Do you see the problem here? Not only your infrastructure will cost double, but you’ll get also all kind of nasty overlappings and cross-pollination between previous and fresh resources and versions. We definitely don’t want that.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzerev4ra27iujxpfg0sh.jpg" 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%2Fzerev4ra27iujxpfg0sh.jpg" alt="facepalm"&gt;&lt;/a&gt;&lt;br&gt;
That is of course only if you don’t pay attention to what &lt;code&gt;terraform plan&lt;/code&gt; and &lt;code&gt;terraform apply&lt;/code&gt; are trying to do. And that is of course only if you already lost your state file – which can happen quite easily when you keep the file like me in the project directory and you just wiped that in order to clone the project again (unrelated story). Terraform recommends keeping the state remotely, be it in their (free) &lt;a href="https://www.terraform.io/cloud" rel="noopener noreferrer"&gt;Terraform Cloud&lt;/a&gt;, in an AWS S3 bucket, anywhere else, where even though still not 100% safe it will still have better chances of recovery.&lt;/p&gt;

&lt;p&gt;How did the day went on? After I noticed &lt;code&gt;terraform plan&lt;/code&gt; wanted to create a few dozen resources where I expected three at max… ew, I said, and it didn’t take long to figure out what happened. A quick google revealed there’s an official tool for such cases called &lt;code&gt;terraform import&lt;/code&gt; which would import the current state of existing resources into the state file, nice stuff apparently. Been there yesterday, done that, got these takeaways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;   always name your resources even when names are optional. Also comment them where comments are available (not many). The more info, the easier to recognize what comes from where.&lt;/li&gt;
&lt;li&gt;   you’ll notice you always have more AWS components than you thought. Especially AWS API Gateway is a can of self-multiplicating quick-growing worms.&lt;/li&gt;
&lt;li&gt;   tagging is seriously overrated, at least in AWS. Not everything can be tagged, searching by tags doesn’t really exist and anyway the AWS Tag Editor won’t be able to export (aka find) all your resources, so you will still need to go through each service console and fetch resource IDs to import. That’s because &lt;code&gt;terraform import&lt;/code&gt; needs the IDs of the existing resources in order to import their current state (makes fully sense).&lt;/li&gt;
&lt;li&gt;   those IDs are in about half of the cases the known name of the resource. The other half is made out of all kind of crazy naming schemes, which I suspect are required by AWS itself. Sometimes it’s ID/name/scope, sometimes it’s the HTTP method, sometimes it’s the ARN… all nicely documented by Terraform but if you have to search documentation by EACH resource type and you have dozens you’re off to a less than pleasant afternoon.&lt;/li&gt;
&lt;li&gt;   surprise surprise, not all resources are supported by &lt;code&gt;terraform import&lt;/code&gt;. Not the S3 bucket objects, not the permissions… and some things I simply wasn’t able to find in the AWS console (where’s the API GW deployment ffs???). So you can’t get to 100% coverage anyway.&lt;/li&gt;
&lt;li&gt;   after a while you’ll realize that it’s easier to delete everything which can be recreated and focus to import only the (hopefully) few stateful components, basically those containing data. So I kept only my Elasticsearch domain and everything else, byes. It’s a bit of AWS console work but by orders of magnitude simpler than the above steps.&lt;/li&gt;
&lt;li&gt;   speaking of stateful components, a good advice is to protect them from accidental deletion anyway when you apply some Terraform changes. Databases come immediately in mind. Tell Terraform to:
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;lifecycle {&lt;br&gt;
  prevent_destroy = true&lt;br&gt;
}&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;and you’ll be safe(r) against misplaced command line actions.&lt;/p&gt;

&lt;p&gt;The best way is still to NOT LOSE your state file. Put it in another directory, sync it in OneDrive, upload it in Terraform Cloud or S3, do something with it, don’t be me! Just don’t save it in your versioning system or you’ll get a zillion useless versions of your software and your CI/CD (Jenkins or whatever) will go crazy.&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>iac</category>
      <category>aws</category>
      <category>infrastructureascode</category>
    </item>
    <item>
      <title>Building Quarkus native images. You can do it too!</title>
      <dc:creator>Sorin Costea</dc:creator>
      <pubDate>Mon, 30 Nov 2020 16:15:31 +0000</pubDate>
      <link>https://dev.to/sorincostea/building-quarkus-native-images-you-can-do-it-too-2api</link>
      <guid>https://dev.to/sorincostea/building-quarkus-native-images-you-can-do-it-too-2api</guid>
      <description>&lt;p&gt;If you read the &lt;a href="https://quarkus.io/guides/building-native-image" rel="noopener noreferrer"&gt;Quarkus native&lt;/a&gt; build page, it should be a breeze: enable &lt;code&gt;-Pnative&lt;/code&gt; and there we go! Well… not so fast. Not so fast at all, as they recognize in another &lt;a href="https://quarkus.io/guides/writing-native-applications-tips" rel="noopener noreferrer"&gt;“Quarkus native tips”&lt;/a&gt; page, unfortunately not linked from the first one (so good luck googling for it). And boy do you need those tips…&lt;/p&gt;

&lt;p&gt;I had this application happily running for months and while at the beginning there were some recognized issues with native generation on Windows, nowadays the general expectation was it should work. So I updated all dependencies and started a painful three days long trip, but hey no pain no gain right? &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%2Ftryingthings.files.wordpress.com%2F2020%2F11%2Fhmm.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%2Ftryingthings.files.wordpress.com%2F2020%2F11%2Fhmm.png" alt="hmm"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s what it required:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;First of all, you do have Docker installed, right? And enabled the container generation in the JVM parameters with&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-Dquarkus.native.container-build=true
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;ok? Well, if you don’t want to go through the hoops of installing GraalVM too (I did, got a tshirt), use the build JVM parameter to create the image with Mandrel (mentioned on the Quarkus native page, without any explanation what the &lt;em&gt;“Mandrel tags”&lt;/em&gt; were)&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;`-Dquarkus.native.builder-image=quay.io/quarkus/ubi-quarkus-mandrel:20.3-java11
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Speaking of JVM parameters, the generation will crash soon running out of memory. Quarkus defaults schmefaults. Add this JVM parameter to increase the Java heap during generation:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-Dquarkus.native.native-image-xmx=4096m
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Actually one of the first errors you’ll see will be the complaint you don’t have the &lt;code&gt;src/assembly/zip.xml&lt;/code&gt; file mentioned in the native Maven profile. A good start will be this:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd"&amp;gt;
  &amp;lt;id&amp;gt;function-native-zip&amp;lt;/id&amp;gt;
  &amp;lt;baseDirectory&amp;gt;/&amp;lt;/baseDirectory&amp;gt;
  &amp;lt;formats&amp;gt;
    &amp;lt;format&amp;gt;zip&amp;lt;/format&amp;gt;
  &amp;lt;/formats&amp;gt;
  &amp;lt;files&amp;gt;
    &amp;lt;file
      &amp;lt;source&amp;gt;${project.build.directory}${file.separator}${artifactId}-${version}-runner&amp;lt;/source&amp;gt;
      &amp;lt;outputDirectory&amp;gt;${file.separator}&amp;lt;/outputDirectory&amp;gt;
      &amp;lt;destName&amp;gt;bootstrap&amp;lt;/destName&amp;gt;
      &amp;lt;fileMode&amp;gt;755&amp;lt;/fileMode&amp;gt;
    &amp;lt;/file&amp;gt;
  &amp;lt;/files&amp;gt;
&amp;lt;/assembly&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You’ll have definitely sooner or later the exception in logs that it cannot find &lt;code&gt;org.apache.commons.logging.impl.LogFactoryImpl&lt;/code&gt;. Regardless how you add it to your pom, Quarkus will do some reflection magic and constantly fail at it. Just forget &lt;code&gt;commons-logging&lt;/code&gt; and use the recommended JBoss logmanager.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
  &amp;lt;groupId&amp;gt;org.jboss.logmanager&amp;lt;/groupId&amp;gt;
  &amp;lt;artifactId&amp;gt;log4j2-jboss-logmanager&amp;lt;/artifactId&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Did you know Quarkus offers their own Elasticsearch clients? I didn’t either, and I had to switch to their distribution instead of the official Elasticsearch. Same API luckily, cleaner dependencies (and no commons-logging either)&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
  &amp;lt;groupId&amp;gt;io.quarkus&amp;lt;/groupId&amp;gt;
  &amp;lt;artifactId&amp;gt;quarkus-elasticsearch-rest-high-level-client&amp;lt;/artifactId&amp;gt;
&amp;lt;/dependency&amp;gt;   
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;That Elasticsearch builder will produce a static client (configured via &lt;code&gt;application.properties&lt;/code&gt;) which nobody needs. I mean, if you want to configure the host at runtime, if you want request signing as you access an AWS Elasticsearch cluster… you’ll get ambiguous injection all in a sudden. That was easy to fix – produce and use your own &lt;code&gt;@Named&lt;/code&gt; client.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Produces
@Named("essigned")
public static RestHighLevelClient getClient(final String url) {
  ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Is it unable to load an HTTP implementation from any provider in the chain? That’s because many AWS SDK v2 clients include the Apache HTTP client only as runtime dependency, for whatever reason – and Quarkus itself does the same (in the above mentioned Elasticsearch client). So add it to your pom AND mention it SPECIFICALLY in the building of your clients!&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
  &amp;lt;groupId&amp;gt;software.amazon.awssdk&amp;lt;/groupId&amp;gt;
  &amp;lt;artifactId&amp;gt;apache-client&amp;lt;/artifactId&amp;gt;
  &amp;lt;scope&amp;gt;compile&amp;lt;/scope&amp;gt;
  &amp;lt;exclusions&amp;gt;
    &amp;lt;exclusion&amp;gt;
      &amp;lt;groupId&amp;gt;commons-logging&amp;lt;/groupId&amp;gt;
      &amp;lt;artifactId&amp;gt;commons-logging&amp;lt;/artifactId&amp;gt;
    &amp;lt;/exclusion&amp;gt;
  &amp;lt;/exclusions&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;and the Java code:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;final SsmClient ssm = SsmClient.builder().httpClientBuilder(ApacheHttpClient.builder()).credentialsProvider(chain).build();
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Oh right, don’t forget to exclude the &lt;code&gt;commons-logging&lt;/code&gt; dependency – that little pesky brat keeps crawling back.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now it’s the moment for your runtime to crash for missing proxies, more precisely because &lt;em&gt;“generating proxy classes at runtime is not supported”&lt;/em&gt;. Don’t ask why Quarkus doesn’t handle that at native image generation, or at least warn about it. So just add in your &lt;code&gt;application.properties&lt;/code&gt; this line:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;quarkus.native.additional-build-args=-H:DynamicProxyConfigurationFiles=proxies.json
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;and a new file &lt;code&gt;proxies.json&lt;/code&gt; in your &lt;code&gt;src/main/resources&lt;/code&gt;, containing exactly those interfaces the error message mentioned. Mine was:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[
    ["org.apache.http.conn.HttpClientConnectionManager", "org.apache.http.pool.ConnPoolControl", "software.amazon.awssdk.http.apache.internal.conn.Wrapped"]
]
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;The issue is mentioned in the Quarkus native tips page, but you’re left on your own trying to figure out WHAT and HOW to put into the mentioned file.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;While we’re at &lt;code&gt;application.properties&lt;/code&gt;, if you have any SSL connection in your code (and unless you do Hello World, you’ll have) place this line there as well:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;quarkus.ssl.native=true
&lt;/code&gt;&lt;/pre&gt;

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

&lt;p&gt;So, this was my adventure so far. The application works now, startup time down from 3.2s to .38s and half the memory footprint… I’m waiting for the next surprise.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftryingthings.files.wordpress.com%2F2020%2F11%2Fi-was-told-celebration-meme.jpg" 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%2Ftryingthings.files.wordpress.com%2F2020%2F11%2Fi-was-told-celebration-meme.jpg" alt="hmm"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>quarkus</category>
      <category>aws</category>
    </item>
    <item>
      <title>Poor man’s static web site protection in AWS S3 (with Terraform)</title>
      <dc:creator>Sorin Costea</dc:creator>
      <pubDate>Mon, 13 Jul 2020 21:39:00 +0000</pubDate>
      <link>https://dev.to/sorincostea/poor-man-s-static-web-site-protection-in-aws-s3-with-terraform-2bk9</link>
      <guid>https://dev.to/sorincostea/poor-man-s-static-web-site-protection-in-aws-s3-with-terraform-2bk9</guid>
      <description>&lt;p&gt;We’re talking internet so arguably the best protection would be to not use it at all, but here we are, serving static files from AWS S3 and hoping not every site and forum is going to link to them. Why? Because we’re the ones paying the AWS bills, aren’t we.&lt;/p&gt;

&lt;p&gt;I have this web application – you could call it SPA even though it’s little more than a REST API client – and it uses a few assets like images and the page itself (duh). So here’s the easy way to reach some security:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;serve the files with a CloudFront distribution (that’s AWS talk for their own CDN)&lt;/li&gt;
&lt;li&gt;restrict only CloudFront to read files from S3 (by setting up OAI – origin access identity)&lt;/li&gt;
&lt;li&gt;upgrade always the connection to HTTPS and allow only GET, HEAD and OPTIONS&lt;/li&gt;
&lt;li&gt;enable WAF (AWS web application firewall, version 2) ACL to only allow on rules&lt;/li&gt;
&lt;li&gt;and finally, restrict that acceptable requests have a custom header with a known value&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Did I say easy? WAF killed me an entire day and even now I have no idea what was initially wrong and why it works now. But here’s what works, in &lt;a href="https://www.terraform.io/"&gt;Terraform&lt;/a&gt; because I hate CloudFormation – but the concepts should be clear.&lt;/p&gt;

&lt;p&gt;1) the &lt;a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/distribution-working-with.html"&gt;CloudFront distribution&lt;/a&gt; itself, which will reference your S3 bucket where the desired assets lie (you already have your bucket, right?) and the soon to be defined ACL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;locals {
  s3_origin_id = "my_origin"
}

resource "aws_cloudfront_distribution" "my_distribution" {
  origin {
    domain_name = aws_s3_bucket.my_bucket.bucket_regional_domain_name
    origin_id   = local.s3_origin_id
    s3_origin_config {
      origin_access_identity = aws_cloudfront_origin_access_identity.my_oai.cloudfront_access_identity_path
    }
  }
  web_acl_id          = aws_wafv2_web_acl.my_acl.arn
  enabled             = true
  is_ipv6_enabled     = true
  default_root_object = "index.html"
  default_cache_behavior {
    allowed_methods  = ["GET", "HEAD", "OPTIONS"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = local.s3_origin_id
    forwarded_values {
      query_string = false
      cookies {
        forward = "none"
      }
    }
    viewer_protocol_policy = "redirect-to-https"
  }
  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }
  tags = {
    maypp = "test"
  }
  viewer_certificate {
    cloudfront_default_certificate = true
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;2) &lt;a href="https://www.terraform.io/docs/providers/aws/r/cloudfront_origin_access_identity.html"&gt;the OAI&lt;/a&gt; of course. It's only this, really.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "aws_cloudfront_origin_access_identity" "my_oai" {
  comment = "Serve securely S3 assets"
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Just, don't forget to add it to your S3 bucket policy, otherwise nothing (good) will happen:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data "aws_iam_policy_document" "my_s3_policy" {
...
  statement {
    actions   = ["s3:GetObject"]
    resources = ["${aws_s3_bucket.my_web_bucket.arn}/*"]
    principals {
      type        = "AWS"
      identifiers = ["${aws_cloudfront_origin_access_identity.my_oai.iam_arn}"]
    }
  }
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;3) the connection and method filters can be also noticed in the CloudFront distribution definition.&lt;/p&gt;

&lt;p&gt;4) The WAF2 &lt;a href="https://www.terraform.io/docs/providers/aws/r/waf_web_acl.html"&gt;access control list&lt;/a&gt; (ACL for the advanced). This is where most of my time got burned, maybe there are better ways to do it but heck if I want to invest more sweat any time soon into it. &lt;/p&gt;

&lt;p&gt;Notice it's a "CLOUDFRONT" type and even if CloudFront is global, it MUST be in the us-east-1 region. For this I needed the multi-provider Terraform hack, see below. It also needs for everything and its mother (see also next point) a mandatory "visibility_config" block even if you don't need metrics right now, because if AWS is a mess, why shouldn't Terraform imitate it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "aws_wafv2_web_acl" "my_acl" {
  name     = "my-acl"
  scope    = "CLOUDFRONT"
  provider = aws.us-east
  default_action {
    block {}
  }
  rule {
    name     = "listlik-acl-rule"
    priority = 1
    override_action {
      none {}
    }
    statement {
      rule_group_reference_statement {
        arn = aws_wafv2_rule_group.my_rule_group.arn
      }
    }
    visibility_config {
      cloudwatch_metrics_enabled = false
      metric_name                = "my-acl-rule-metric"
      sampled_requests_enabled   = false
    }
  }
  tags = {
    maypp = "test"
  }
  visibility_config {
    cloudwatch_metrics_enabled = false
    metric_name                = "my-acl-metric"
    sampled_requests_enabled   = false
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;As mentioned, Terraform needed &lt;a href="https://www.terraform.io/docs/providers/aws/index.html"&gt;two providers&lt;/a&gt; - the regular AWS one and a special one for the global CloudFront distribution which you will always refer by alias:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;provider "aws" {
  region = var.region
}
provider "aws" {
  alias  = "us-east"
  region = "us-east-1"
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;5) And finally the &lt;a href="https://www.terraform.io/docs/providers/aws/r/waf_rule_group.html"&gt;rule group&lt;/a&gt; with no geographical restrictions but a single rule, letting only requests with a custom header containing exactly a custom value. Notice that if the ACL default action (if no rule method matched) was to block, and as the rule group didn't override it, this rule's action is the only thing which can allow you receive the assets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "aws_wafv2_rule_group" "my_rule_group" {
  name     = "my-rule-group"
  scope    = "CLOUDFRONT"
  provider = aws.us-east
  capacity = 2
  rule {
    name     = "my-rule"
    priority = 1
    action {
      allow {}
    }
    statement {
      byte_match_statement {
        positional_constraint = "EXACTLY"
        search_string         = "megasecretstring"
        field_to_match {
          single_header {
            name = "x-my-referrer"
          }
        }
        text_transformation {
          priority = 1
          type     = "NONE"
        }
      }
    }
    visibility_config {
      cloudwatch_metrics_enabled = false
      metric_name                = "my-rule-metric"
      sampled_requests_enabled   = false
    }
  }
  visibility_config {
    cloudwatch_metrics_enabled = false
    metric_name                = "my-rule-group-metric"
    sampled_requests_enabled   = false
  }
  tags = {
    maypp = "test"
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now you can add to your browser an extension (like &lt;a href="https://addons.mozilla.org/de/firefox/addon/simple-modify-header/"&gt;Simple Modify Headers&lt;/a&gt; if you use Firefox) which for a specific domain - the domain of your CloudFront distribution - will always attach to requests the header as configured above. I know I could have used the standard &lt;code&gt;"X-Referrer"&lt;/code&gt; header but then it wouldn't be so obivous that for CloudFront it doesn't matter at all - if will filter the requests by anything you like.&lt;/p&gt;

&lt;p&gt;(Published as part of the #100DaysToOffload challenge &lt;a href="https://100daystooffload.com/"&gt;https://100daystooffload.com/&lt;/a&gt;)&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>aws</category>
      <category>s3</category>
      <category>cloudfront</category>
    </item>
    <item>
      <title>AWS SDK for Java v2 - so simple they saved on documentation</title>
      <dc:creator>Sorin Costea</dc:creator>
      <pubDate>Wed, 08 Jul 2020 11:04:23 +0000</pubDate>
      <link>https://dev.to/sorincostea/aws-sdk-for-java-v2-so-simple-they-saved-on-documentation-e44</link>
      <guid>https://dev.to/sorincostea/aws-sdk-for-java-v2-so-simple-they-saved-on-documentation-e44</guid>
      <description>&lt;p&gt;The &lt;a href="https://docs.aws.amazon.com/sdk-for-java/v2/developer-guide/welcome.html"&gt;version 2 of the AWS Java SDK&lt;/a&gt; is much less documented than the first one. Okay it's newer so there are less blogs and projects, obviously, but finding official code examples with v2 on AWS sites is a real struggle. Luckily, using the SDK has become much simpler and straightforward.&lt;/p&gt;

&lt;p&gt;Every service will be accessed in a pretty much similar pattern. Let's say, you want to read some data you keep in the parameter store of AWS's SSM (Systems Manager). After you include in your project the BOM of AWS for versions and the SSM dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  &amp;lt;dependencyManagement&amp;gt;
    &amp;lt;dependencies&amp;gt;
      &amp;lt;dependency&amp;gt;
        &amp;lt;groupId&amp;gt;software.amazon.awssdk&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;bom&amp;lt;/artifactId&amp;gt;
        &amp;lt;version&amp;gt;${aws.version}&amp;lt;/version&amp;gt;
        &amp;lt;type&amp;gt;pom&amp;lt;/type&amp;gt;
        &amp;lt;scope&amp;gt;import&amp;lt;/scope&amp;gt;
      &amp;lt;/dependency&amp;gt;
  ...
  &amp;lt;/dependencyManagement&amp;gt;
  &amp;lt;dependencies&amp;gt;
    &amp;lt;dependency&amp;gt;
      &amp;lt;groupId&amp;gt;software.amazon.awssdk&amp;lt;/groupId&amp;gt;
      &amp;lt;artifactId&amp;gt;ssm&amp;lt;/artifactId&amp;gt;
    &amp;lt;/dependency&amp;gt;
  ...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Note: the biggest challenge is to figure out what is the artifact name for a certain AWS service, there's like zero references to that. But reading through the (huge) BOM with some common sense will let you find it.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The following piece of code will let you read your encrypted parameters by path.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;final AwsCredentialsProviderChain chain = AwsCredentialsProviderChain
    .of(ProfileCredentialsProvider.builder().build(), DefaultCredentialsProvider.create());
final SsmClient ssm = SsmClient.builder().credentialsProvider(chain).build();
final GetParametersByPathResponse secretsResult = ssm.getParametersByPath(
    GetParametersByPathRequest.builder().path("/your/secrets").withDecryption(true).build());
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The pattern is always the same:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;build a client, the AWS builder pattern will always be in the form of &lt;code&gt;YourNeededClient.builder().optionaldata().build()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;build a service request, again in the same pattern &lt;code&gt;YourNeededOperationRequest.builder().operationdata().build()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;call the operation &lt;code&gt;client.operationName(request)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;it will return a result of type &lt;code&gt;YourNeededOperationResult&lt;/code&gt; where you can read the requested data.
&amp;gt;&lt;em&gt;Note: In my example the SsmClient needed also the credentials provider to grab the account key - the parameters were encrypted remember?&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your homework will be every time to search through the SDK classes: first find the client class name, find out the operation needed, deduce the request class name, and there you go. Sometimes you'll be able to find examples too, but you can try also blindly - most of the time the error messages will be pretty straightforward, like some request needs signing, or you're missing some needed role, or…&lt;/p&gt;

&lt;p&gt;(Published as part of the #100DaysToOffload challenge &lt;a href="https://100daystooffload.com/"&gt;https://100daystooffload.com/&lt;/a&gt;)&lt;/p&gt;

</description>
      <category>aws</category>
      <category>java</category>
      <category>sdk</category>
      <category>100daystooffload</category>
    </item>
    <item>
      <title>Handling unknown JSON structures</title>
      <dc:creator>Sorin Costea</dc:creator>
      <pubDate>Wed, 01 Jul 2020 11:45:02 +0000</pubDate>
      <link>https://dev.to/sorincostea/handling-unknown-json-structures-3gci</link>
      <guid>https://dev.to/sorincostea/handling-unknown-json-structures-3gci</guid>
      <description>&lt;p&gt;JSON object mapping, marshalling, call it whatever you want, we have you covered with Gson or Jackson. Unless you don't care about structure and want to be able to parse random JSON strings. Oops, THERE come clumsiness. &lt;/p&gt;

&lt;p&gt;I'm sure you could&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    final JsonObject json = new JsonParser().parse(yourstring).getAsJsonObject();
    final Integer start = json.has("start") ? json.get("start").getAsInt() : 0;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;(made possible with &lt;a href="https://github.com/google/gson"&gt;Google Gson&lt;/a&gt;) but why not go simpler and just&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    final JsonObject json = new JsonObject(yourstring);
    final Integer start = json.getInteger("start", 0);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Nice, right? And the same with arrays, just &lt;code&gt;json.getJsonArray("list")&lt;/code&gt; with or without providing defaults... Too bad it's just part of the &lt;a href="https://vertx.io/docs/apidocs/index.html?io/vertx/core/json/package-summary.html"&gt;core Vert.x package&lt;/a&gt; and not a separate library you could use in any project... so if you have any &lt;code&gt;vertx.core&lt;/code&gt; dependencies lying around, for example when using &lt;a href="https://quarkus.io/guides/amazon-lambda-http"&gt;Quarkus Lambda HTTP&lt;/a&gt;, or don't mind an extra 1M in size, make sure you give it a try. Cleaner simply doesn't exist in the Java world, at least as far as I know.&lt;/p&gt;

&lt;p&gt;Ah yes, and it’s a &lt;a href="https://tools.ietf.org/html/rfc7493"&gt;I-JSON implementation&lt;/a&gt; (RFC 7493, Internet JSON) so maybe some fancy number formats won’t be able to fit, but I never hit that wall.&lt;/p&gt;

&lt;p&gt;(Published as part of the #100DaysToOffload challenge &lt;a href="https://100daystooffload.com/"&gt;https://100daystooffload.com/&lt;/a&gt;)&lt;/p&gt;

</description>
      <category>java</category>
      <category>json</category>
      <category>vertx</category>
      <category>quarkus</category>
    </item>
    <item>
      <title>Wishful thinking about a Tumblr API v3</title>
      <dc:creator>Sorin Costea</dc:creator>
      <pubDate>Tue, 23 Jun 2020 17:43:51 +0000</pubDate>
      <link>https://dev.to/sorincostea/wishful-thinking-about-a-tumblr-api-v3-1bhp</link>
      <guid>https://dev.to/sorincostea/wishful-thinking-about-a-tumblr-api-v3-1bhp</guid>
      <description>&lt;p&gt;Let’s say you want to read a Tumblr post information from their &lt;a href="https://www.tumblr.com/docs/en/api/v2"&gt;API v2&lt;/a&gt;. Nothing special at the first look, but beyond the obvious parameters, there’s an interesting one: notes_info. The API designer thought to make this optional (it defaults to false) as to not overload the server and cause unneeded traffic with all the likes and reblogs your requested post might have – especially if popular.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.tumblr.com/v2/blog/your-blog-to-check/posts?api_key=0ABCDEFGH&amp;amp;id=12345678¬es_info=true
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And how will those notes look like? Well, the API documentation forgets to document that. Its last update is from 2011, the very year where the &lt;a href="https://swagger.io/"&gt;Swagger project&lt;/a&gt; was started so we can forgive them for the lack of documentation. But if we quickly try the API, it will – somewhat obviously – return only the first 50 notes. Obviously, because what if the post had a million notes?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[{
avatar_shape: "circle",
blog_name: "ablogname",
blog_url: "https://ablogname.tumblr.com/",
blog_uuid: "t:kjhlIU(/OLUGu7g",
followed: false,
post_id: "9873875387587638",
reblog_parent_blog_name: "anotherblog",
timestamp: 1592801102,
type: "reblog"
},
…
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now, HOW on earth could we paginate over these notes? The obvious answer is: no way, it’s an inner structure and the REST idea doesn’t allow for that. I mean, something like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.tumblr.com/v2/blog/your-blog-to-check/posts?api_key=0ABCDEFGH&amp;amp;id=12345678¬es_info=true&amp;amp;offset=50
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;logically would apply to the response of the call. So, whenever Tumblr does an API update, maybe 2021 to celebrate 10 years since the last one, how about they make the notes first class citizens and let one access them properly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.tumblr.com/v2/blog/your-blog-to-check/posts/12345678/notes?api_key=0ABCDEFGH&amp;amp;id&amp;amp;offset=50
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And as a modern OpenAPI and documented right? Like even the &lt;a href="https://petstore.swagger.io/"&gt;petstore of Swagger&lt;/a&gt; is? And while we’re talking about changes, discoverability wouldn’t hurt either – add a Link header! It’s official (together with the &lt;a href="http://www.iana.org/assignments/link-relations/link-relations.xml"&gt;types next/prev/first/last&lt;/a&gt;) so should be fairly well known, and Spring REST supports it out of the box.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Link = https://api.tumblr.com/v2/blog/your-blog-to-check/posts/12345678/notes?api_key=0ABCDEFGH&amp;amp;id&amp;amp;offset=100; rel=”next”
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;That was just a silly and frustrated example. It doesn’t have to be offset pagination, it could be cursor pagination or anything. Just have it there, thank you!&lt;/p&gt;

&lt;p&gt;(Published as part of the #100DaysToOffload challenge &lt;a href="https://100daystooffload.com/"&gt;https://100daystooffload.com/&lt;/a&gt;)&lt;/p&gt;

</description>
      <category>rest</category>
      <category>api</category>
      <category>tumblr</category>
      <category>100daystooffload</category>
    </item>
    <item>
      <title>Vue.js if you’re not a frontend developer</title>
      <dc:creator>Sorin Costea</dc:creator>
      <pubDate>Sun, 21 Jun 2020 18:30:42 +0000</pubDate>
      <link>https://dev.to/sorincostea/vue-js-if-you-re-not-a-frontend-developer-2534</link>
      <guid>https://dev.to/sorincostea/vue-js-if-you-re-not-a-frontend-developer-2534</guid>
      <description>&lt;p&gt;Say, you want a simple web page to show you in a nice way some JSON data you loaded from a REST API. Doing it with &lt;a href="https://vuejs.org/"&gt;vue.js&lt;/a&gt; is easy: just npm and… heck no! You definitely don’t need to install and/or learn another server stack for a few scripts on your web page. Instead include simply the vue.js in the good old traditional way, here the development version (aka not minified and with useful console logging):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then, you will use a new component to show looping over data, the pet-item which you’ll define in a second:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div id="petApp"&amp;gt;
    &amp;lt;div&amp;gt;
      &amp;lt;pet-item v-for="item in petList" v-bind:pet="item" v-bind:key="item.id"&amp;gt;&amp;lt;/pet-item&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;While the pet item definition is a Vue template you will define the simplest way in a script element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script type="text/javascript"&amp;gt;
      Vue.component('pet-item', {
        props : [ 'pet' ],
        template: `
        &amp;lt;div&amp;gt;
          &amp;lt;h3&amp;gt;{{ pet.name }}&amp;lt;/h3&amp;gt;
          &amp;lt;div&amp;gt;ID: {{ pet.id }}&amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
      `
      })...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: watch for the back quote used to define multiline strings, not all older browsers will support it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The template will use mostly &lt;code&gt;{{Mustache}}&lt;/code&gt; tags (except where it doesn’t, see the &lt;code&gt;v-bind&lt;/code&gt; attributes) but even then the whole bunch will look familiar if you are used to Thymeleaf templates – if you’re coming like me from the Spring world (and will have just as Thymeleaf its peculiarities, see again the &lt;code&gt;v-bind&lt;/code&gt; attributes).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: it’s important to remember that your template must have only one root element (here a div) to contain everything.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Of course in order to see anything you’ll need some data, and while you could add by hand some example data structures, why not bringing it from an API? I’ll use a simple call to the &lt;a href="https://petstore3.swagger.io/"&gt;Swagger demo petstore API&lt;/a&gt;, namely&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://petstore3.swagger.io/api/v3/pet/findByStatus?status=available
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To make REST calls from javascript &lt;a href="https://github.com/axios/axios"&gt;using Axios&lt;/a&gt; is a rather trivial matter, so here is your main Vue application code fetching data with Axios (haha I called this simple thing “application”):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var petApp = new Vue({
  el : '#petApp',
  data () {
    return {
      petList: null
    }
  },
  mounted () {
    axios
      .get('https://petstore3.swagger.io/api/v3/pet/findByStatus?status=available')
      .then(response =&amp;gt; {
          console.log(response.data)
          this.petList = response.data
      })
      .catch(error =&amp;gt; {
        console.log(error)
      })
      .finally(() =&amp;gt; this.loading = false)
  }
})
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The only extras there is that I wanted to log to the browser console the response and the errors if any. Also, the API request will be made in the Vue mounted() function which is pretty much the most basic use case (more about &lt;a href="https://vuejs.org/v2/guide/instance.html#Lifecycle-Diagram"&gt;Vue lifecycle here&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;All this being said and done, when you load the HTML page in your browser you’ll get something working and… as ugly as this:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--a89LIHAC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/s5v2bboqap9jk4i9uv8j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--a89LIHAC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/s5v2bboqap9jk4i9uv8j.png" alt="basic vue.js result"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Works but… ew. Well, as we’re in the middle of trying things, why not throwing some Bootstrap styling around the divs and make them flowing cards? The &lt;a href="https://gist.github.com/sorin-costea/29ea39b88ac7860d84d6d0fe43444489"&gt;final page is here&lt;/a&gt; and will look like the fragment below, flowing in 1, 2, 4 or 6 columns depending on your screen width.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CN6LUAl0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/boscx3otnp7ea0s1c1cw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CN6LUAl0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/boscx3otnp7ea0s1c1cw.png" alt="basic vue.js with bootstrap"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See? And I didn’t even bother to create a repo for such a small thing. A gist is all that one needs to get started using vue.js. And axios.&lt;/p&gt;

&lt;p&gt;(Published as part of the &lt;code&gt;#100DaysToOffload&lt;/code&gt; challenge &lt;a href="https://100daystooffload.com/"&gt;https://100daystooffload.com/&lt;/a&gt;)&lt;/p&gt;

</description>
      <category>vue</category>
      <category>handson</category>
      <category>javascript</category>
      <category>html</category>
    </item>
  </channel>
</rss>
