<?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: Alex Hyett</title>
    <description>The latest articles on DEV Community by Alex Hyett (@alexhyettdev).</description>
    <link>https://dev.to/alexhyettdev</link>
    <image>
      <url>https://res.cloudinary.com/practicaldev/image/fetch/s--478N1VLD--/c_fill,f_auto,fl_progressive,h_90,q_auto,w_90/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/491468/0db5c797-8bb7-4b4f-b340-8e76227a5635.jpeg</url>
      <title>DEV Community: Alex Hyett</title>
      <link>https://dev.to/alexhyettdev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alexhyettdev"/>
    <language>en</language>
    <item>
      <title>Privacy Focused Blog Services</title>
      <dc:creator>Alex Hyett</dc:creator>
      <pubDate>Fri, 28 Jan 2022 11:51:41 +0000</pubDate>
      <link>https://dev.to/alexhyettdev/privacy-focused-blog-services-3ik3</link>
      <guid>https://dev.to/alexhyettdev/privacy-focused-blog-services-3ik3</guid>
      <description>&lt;p&gt;Towards the end of 2021, I started getting into &lt;a href="https://www.alexhyett.com/what-is-web3/"&gt;web3&lt;/a&gt; a lot more. Not only did I join the &lt;a href="https://www.developerdao.com/"&gt;Developer DAO&lt;/a&gt; but I also launched my &lt;a href="https://www.sleepyslothclub.com/"&gt;own NFT collection&lt;/a&gt; and wrote a few blog posts on the topic.&lt;/p&gt;

&lt;p&gt;One of the key components of web3 is the ownership of data. In web 2.0, our data is owned by the likes of Facebook, Google and Apple. We have no control over our own data and in most cases, these companies are profiting from it.&lt;/p&gt;

&lt;p&gt;Now my blog is a static website created using &lt;a href="https://www.alexhyett.com/building-a-blog-with-gatsbyjs/"&gt;Gatsby.js&lt;/a&gt;. The lack of an underlying database has meant I have had to rely on 3rd party services for analytics and comments.&lt;/p&gt;

&lt;p&gt;Until recently, I have been using Google Analytics for tracking the number of visitors and Disqus for my commenting system. However, both of these companies track their users and are profiting from the data they collect. As a web3 advocate, I felt a bit hypocritical writing about all the benefits but still using these services that are tracking data.&lt;/p&gt;

&lt;p&gt;So at the end of 2021, I switched to privacy-focused services that don’t collect identifiable information on visitors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Plausible Analytics
&lt;/h2&gt;

&lt;p&gt;I don’t know the exact statistic but I imagine the majority of the web has Google Analytics running on it. As a result, Google is able to collect an enormous amount of data on the websites you visit and can track your movements around the web.&lt;/p&gt;

&lt;p&gt;Obviously, as a simple user, I am not able to identify individual people on my site but Google can, especially if you have a Google account too.&lt;/p&gt;

&lt;p&gt;Google Analytics has a dizzying number of features but ultimately there are only a few data points I care about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How many people are visiting my site?&lt;/li&gt;
&lt;li&gt;What posts are they reading?&lt;/li&gt;
&lt;li&gt;Where did they come from?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Possibly in the future when I have products I might want to know how many of my visitors convert into paid users.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://plausible.io/"&gt;Plausible Analytics&lt;/a&gt; does all of this without compromising the privacy of the users of my site.&lt;/p&gt;

&lt;p&gt;Now Plausible Analytics isn’t free at £9 a month but not only are you protecting your user’s privacy you are also helping out an indie development team. Plausible is developed by a small 2 man team &lt;a href="https://twitter.com/ukutaht"&gt;Uku Täht&lt;/a&gt; and &lt;a href="https://twitter.com/markosaric"&gt;Marko Saric&lt;/a&gt; and it is nice to be able to support them rather hand more money over to Google. Plausible also give you the first 2 months free so you can see whether it is the right tool for you.&lt;/p&gt;

&lt;p&gt;I also much prefer the dark dashboard compared to Google Analytics:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ot5FA8b---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1643367454039/Yh2Lt13CO.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ot5FA8b---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1643367454039/Yh2Lt13CO.png" alt="Plausible Analytics" width="880" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have been really impressed with Plausible. I wish they had a mobile app and widgets but the dashboard is mobile friendly so it isn’t a big loss.&lt;/p&gt;

&lt;p&gt;Plausible is simple to set up, it is just a case of adding the javascript to your site. It is also a smaller script than Google Analytics so it will help your website performance as well.&lt;/p&gt;

&lt;p&gt;I always found it ironic that Google’s Page Speed was always asking me to fix the Google Analytics script due to its size.&lt;/p&gt;

&lt;h2&gt;
  
  
  Giscus
&lt;/h2&gt;

&lt;p&gt;When I switched from WordPress to Gatsby, I needed to add a third-party comment system. &lt;a href="https://disqus.com/"&gt;Disqus&lt;/a&gt; is very easy to set up but requires users to sign in and will often try and add adverts if you don’t turn them off. I am sure there is a fair amount of data collection that goes on as well.&lt;/p&gt;

&lt;p&gt;The downside of comment systems is they cannot be completely anonymous as otherwise, you will end up with a lot of spam on your website.&lt;/p&gt;

&lt;p&gt;When I was hunting around for alternatives I found &lt;a href="https://utteranc.es/"&gt;utterances&lt;/a&gt;. Utterances is open-source and uses GitHub to store your comments. It seemed perfect but I wasn’t keen on the fact that conversations were being stored as issues.&lt;/p&gt;

&lt;p&gt;I then found &lt;a href="https://giscus.app/"&gt;Giscus&lt;/a&gt; a fork of utternances that uses GitHub discussions for the storage mechanism. If you look at the sites you will see they are basically identical in how they work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Cr4UbvOO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1643370258435/j2M0vF3rjcs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Cr4UbvOO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1643370258435/j2M0vF3rjcs.png" alt="Giscus" width="880" height="337"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You still need a GitHub account to be able to comment but as a developer-focused site, it is likely the majority of my users probably would have one anyway.&lt;/p&gt;

&lt;h3&gt;
  
  
  Issues with setup
&lt;/h3&gt;

&lt;p&gt;I did hit a few roadblocks when setting up Giscus. The main issue was with the way I had my website set up.&lt;/p&gt;

&lt;p&gt;I currently have my website hosted on AWS with my static website hosted in an S3 bucket and CloudFront as my CDN.&lt;/p&gt;

&lt;p&gt;When I first tried to sign in to GitHub to comment I would get redirected back to my website but it will still tell me I needed to sign in. The issue was there was a redirect happening which was causing the query string containing the token to get dropped.&lt;/p&gt;

&lt;p&gt;First I made sure I enabled query string forwarding in my CloudFront distribution. That didn’t help by itself but I guessed was needed anyway.&lt;/p&gt;

&lt;p&gt;In an S3 website if you visit a page hosted in a folder without specifying the trailing slash it will redirect you to the trailing slash version. When it does this, it drops the query string. To me, this seems like a bug with the way S3 hosted sites work, either way, we need to work around it.&lt;/p&gt;

&lt;p&gt;The trick is to add a function to your CloudFront distribution that will add the trailing slash for you before it passed to the S3 bucket.&lt;/p&gt;

&lt;p&gt;To do this, you go to the Behaviour section of your CloudFront distribution. I have a default behaviour added already to redirect HTTP to HTTPS so I just added to this. If you scroll to the bottom of your behaviour you will find a section called Function association.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EfgcXKak--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1643369372648/ZEFUpDxVC.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EfgcXKak--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1643369372648/ZEFUpDxVC.png" alt="Function association" width="880" height="374"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your lambda needs to be hosted in &lt;code&gt;us-east-1&lt;/code&gt; to be able to use it with CloudFront.&lt;/p&gt;

&lt;p&gt;In my case, I only need to add a slash if it wasn’t there. This is what my code looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const path = require('path');
exports.handler = async (event) =&amp;gt; {
  const { request } = event.Records[0].cf;
  const { uri } = request;
  const extension = path.extname(uri);
  if (extension &amp;amp;&amp;amp; extension.length &amp;gt; 0) {
    return request;
  }
  const last_character = uri.slice(-1);
  if (last_character === "/") {
    return request;
  }
  const newUri = `${uri}/`;
  console.log(`Rewriting ${uri} to ${newUri}...`);
  request.uri = newUri;
  return request;
};

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

&lt;/div&gt;



&lt;p&gt;You can test your lambda in the console before publishing and hooking up to your CloudFront distribution.&lt;/p&gt;

&lt;p&gt;A big thank you to Sean Devine for his post on this, &lt;a href="https://www.barelyknown.com/posts/add-trailing-slash-to-cloudfront-request"&gt;Add Trailing Slash to AWS CloudFront Request&lt;/a&gt;&lt;/p&gt;

</description>
      <category>blogging</category>
    </item>
    <item>
      <title>What is DeFi? Understanding Decentralized Finance</title>
      <dc:creator>Alex Hyett</dc:creator>
      <pubDate>Fri, 19 Nov 2021 11:52:22 +0000</pubDate>
      <link>https://dev.to/alexhyettdev/what-is-defi-understanding-decentralized-finance-3k6b</link>
      <guid>https://dev.to/alexhyettdev/what-is-defi-understanding-decentralized-finance-3k6b</guid>
      <description>&lt;p&gt;If you are new to the crypto space the concepts of decentralised finance can seem a bit overwhelming. There are so many new terms to try and understand, it can be challenging to see how everything fits together.&lt;/p&gt;

&lt;p&gt;I have been working in the traditional FinTech space for the last 10 years and it has taken me a while to get my head around everything and I am still learning, so you are not alone here.&lt;/p&gt;

&lt;p&gt;We are still very early in the web3 crypto space, and many implementations are still quite technical. We are still quite a way off to having mainstream adoption by the general public.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Centralized Finance (CeFi)?
&lt;/h2&gt;

&lt;p&gt;Before we try and explain DeFi it helps to understand the system we currently know and tolerate.&lt;/p&gt;

&lt;p&gt;Centralised finance encompasses the entire banking system as we know it today.&lt;/p&gt;

&lt;p&gt;There are quite a few financial services that have been developed over the last century.&lt;/p&gt;

&lt;h3&gt;
  
  
  Transferring
&lt;/h3&gt;

&lt;p&gt;This is a pretty basic service. All banks offer the ability to transfer money from one bank account to another. Over the years we have come up with a few ways of doing this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Internal Transfers - transferring between your accounts in the same bank. These transfers are generally instant.&lt;/li&gt;
&lt;li&gt;Faster payments (FP) - transferring money between different supported institutions. This is supported by most banks in the UK (in the US the equivalent is probably ACH or RTP). Generally, transactions are instant but can take up to 2 hours to appear in the target bank account.&lt;/li&gt;
&lt;li&gt;CHAPS/Swift - for larger payments that can’t go by FP we have CHAPS. There is a fee to transfer by CHAPS and transfers can take 3 - 5 days.&lt;/li&gt;
&lt;li&gt;Transfer Services - On top of traditional transfer services we also have services such as PayPal, Wise, Western Union to name but a few.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Exchanging
&lt;/h3&gt;

&lt;p&gt;If you have ever been abroad you will likely have transferred money into a different currency. Simply converting from one currency to another is one factor the other is trading currencies like stocks and for that we have Forex.&lt;/p&gt;

&lt;h3&gt;
  
  
  Saving
&lt;/h3&gt;

&lt;p&gt;A savings account is usually one of the first accounts you open when you are younger. Put money in a savings account and as a reward for leaving it there, you earn a bit of interest (like a tiny bit).&lt;/p&gt;

&lt;p&gt;If you are willing to lock up your money for longer there are also savings bonds where you get a guaranteed fixed interest rate but you can’t access your money for a set period.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lending and Borrowing
&lt;/h3&gt;

&lt;p&gt;If you need more money than you currently have in savings then you need a loan. A lot of people have loans for things like houses, cars etc.&lt;/p&gt;

&lt;p&gt;To be able to get a loan you need to have a good credit score so they can work out how likely you are to pay the money back.&lt;/p&gt;

&lt;p&gt;If you need a loan you used to have to go to a bank. However, there are now quite a few loan services offering “payday loans” usually with extremely high-interest rates.&lt;/p&gt;

&lt;p&gt;On the other side, there are also services for lending your money and getting interest in return.&lt;/p&gt;

&lt;p&gt;Of course, we also have credit cards which are a form of flexible loan that you only pay interest on if you don’t pay back the money on time each month.&lt;/p&gt;

&lt;h3&gt;
  
  
  Insurance
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Where there is money and risk there is insurance — Me (just now)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In life, there is always a risk. Your house could burn down, you could get into a car accident or someone could steal your stuff. For that we have insurance.&lt;/p&gt;

&lt;p&gt;There are thousands of insurance companies that weigh up the risks and decide how much to charge you for peace of mind. In all cases, it is up to the insurance company to decide whether they are going to pay out or not.&lt;/p&gt;

&lt;h3&gt;
  
  
  Margin trading
&lt;/h3&gt;

&lt;p&gt;We are now getting into the more complex sides of CeFi which you generally have to be into finance to get involved with. So I will explain what these are in case you aren’t familiar.&lt;/p&gt;

&lt;p&gt;If you invest £1,000 in stock and the stock goes up 5% you have just made £50. If only you had more money to invest!&lt;/p&gt;

&lt;p&gt;Let’s say you borrowed £100,000 instead in this case you would have made £5,000. You then pay back the £100,000 and keep the £5,000. This is what margin trading is.&lt;/p&gt;

&lt;p&gt;Of course, it can go the other way, say the stock drops 5% you now only have £95,000 but still owe £100,000.&lt;/p&gt;

&lt;p&gt;I am no expert in margin trading so feel free to correct me in the comments :).&lt;/p&gt;

&lt;h3&gt;
  
  
  Derivatives
&lt;/h3&gt;

&lt;p&gt;Derivatives are a bit like betting. You are betting whether an asset is going to go up (long) or down (short). That is how people make money from stocks going down.&lt;/p&gt;

&lt;p&gt;A derivative is essentially a contract between 2 parties to agree to buy or sell at a set price at some point in the future. How much a derivative is worth depends on the value of the underlying asset.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Say for example you want to sell the popular bike you own in 1 months time. You agree to sell your bike to Bob for $1,000 and a contract is written up. In the meantime, the company stops making your bike and everyone is disappointed. The remaining supply of that model is now going for $1,500 in shops.&lt;/p&gt;

&lt;p&gt;Now Bob who agreed to buy your bike no longer wants it but knows Alice does. Bob decides to sell the contract to buy your bike to Alice for $100. As a result, Bob has made $100, Alice has saved $400 on that model of bike and you get your $1000 as agreed.&lt;/p&gt;

&lt;p&gt;The contract that Bob sold to Alice is a derivative. If Alice wanted to she could have sold it on to someone else before the contract was settled.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are different types of derivatives such as forward contracts, futures contracts, options contracts and swaps.&lt;/p&gt;

&lt;p&gt;In these cases, you are buying the contract and are taking on the profit and loss of the underlying asset without actually owning the asset.&lt;/p&gt;

&lt;p&gt;Again, I am not an expert in derivatives but hopefully, that should give you some basic understanding. Feel free to correct me in the comments.&lt;/p&gt;

&lt;h2&gt;
  
  
  So what is decentralized finance (DeFi)?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HoVGtJZo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1637322634231/vmuxiciu0.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HoVGtJZo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1637322634231/vmuxiciu0.jpeg" alt="DeFi" width="880" height="542"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Simply, DeFi is creating everything we have in CeFi mentioned above but in a decentralised way.&lt;/p&gt;

&lt;p&gt;DeFi solves some of the key issues we have with the centralised system.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Centralised - the current systems are run by individual companies. Generally, you have to give them your money to be able to do anything with it. The company could go bust, be fraudulent or get censored.&lt;/li&gt;
&lt;li&gt;Closed source - a company can do anything with your money once they have it. No one outside the company knows how the process works and there is no way to validate things are going to work.&lt;/li&gt;
&lt;li&gt;Expensive - Now currently DeFi can be expensive too (e.g. gas fees) but this is only on certain blockchains. It is generally cheaper to send cryptocurrency than it is to send fiat currency.&lt;/li&gt;
&lt;li&gt;Identity/Permission - to do anything with CeFi you generally need to hand over your entire identity in the form of ID cards, Passports, Birth certificates etc. Do you trust all that personal information with every company you interact with?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cryptocurrency
&lt;/h3&gt;

&lt;p&gt;When I think of cryptocurrency I generally compare it to physical notes (cash) rather than money in a bank account. I can give you a £10 note and you then have that money without having to go through someone else.&lt;/p&gt;

&lt;p&gt;Cryptocurrency is internet cash. Keep this in mind while we take a look at the services that make up DeFi.&lt;/p&gt;

&lt;h3&gt;
  
  
  Transferring
&lt;/h3&gt;

&lt;p&gt;This forms the heart of cryptocurrency. With all of the different cryptocurrencies, there is always the ability to transfer crypto from one wallet to another with no middleman.&lt;/p&gt;

&lt;p&gt;The CeFi alternative is to deposit cash into a bank account, which is a pot of money owned by a company. Which then gets transferred to another bank account which is again owned by a different company. To then withdraw as cash.&lt;/p&gt;

&lt;p&gt;With cryptocurrency, the money goes from me to you without anyone else getting involved or having to hand over your personal details in the process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exchanging
&lt;/h3&gt;

&lt;p&gt;There are quite a few crypto exchanges now but not all of them are created equally.&lt;/p&gt;

&lt;p&gt;You are probably familiar with the main ones such as &lt;a href="https://www.coinbase.com/"&gt;Coinbase&lt;/a&gt;, &lt;a href="https://www.binance.com/"&gt;Binance&lt;/a&gt; and &lt;a href="https://crypto.com/"&gt;Crypto.com&lt;/a&gt;. We need these services to get started in Crypto and convert your fiat currency into cryptocurrency.&lt;/p&gt;

&lt;p&gt;However, they are not decentralised. For one, you need to hand over your identity to be able to start buying crypto with fiat. In most cases, your money isn’t sitting in a secure wallet you own. As such it could get hacked or the company could go bust and you would lose your money.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Always move your money out of these services into a wallet you own. Such as a &lt;a href="https://metamask.io/"&gt;MetaMask&lt;/a&gt; wallet or better yet a hardware wallet such as &lt;a href="https://www.ledger.com/"&gt;Ledger&lt;/a&gt;. I compare MetaMask to the money I keep on my person to allow me to buy things and my hardware wallet as my savings account.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A purely decentralised exchange allows you to move money from one currency to another and is controlled by smart contracts not by an individual company.&lt;/p&gt;

&lt;p&gt;Good examples of decentralised exchanges are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://uniswap.org/"&gt;Uniswap&lt;/a&gt; - simple exchange with quite a few supported currencies.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.sushi.com/"&gt;SushiSwap&lt;/a&gt; - a nice interface for doing exchanges&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dmm.exchange/#/swap"&gt;Kyber&lt;/a&gt; - with Kyber you can choose to either lower fees or maximise your return. In some cases, it will even use Uniswap for the exchange if it is preferable.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dex.ag/"&gt;DEX.AG&lt;/a&gt; - DEX is an aggregator and will help find the best exchange to use.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are also a few non-decentralised exchanges that work on an order based system such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://loopring.io/#/layer2"&gt;Loopring&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://idex.io/"&gt;Idex&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Saving
&lt;/h3&gt;

&lt;p&gt;There isn’t a direct comparison with traditional savings accounts in DeFi (feel free to correct me if I am wrong).&lt;/p&gt;

&lt;p&gt;The way traditional savings accounts work is a bank acts as a middleman between lenders and borrowers. Banks will give you &amp;gt;1% interest on your savings which they are then lending out to others with an interest rate of 2 - 19% depending on the type of loan.&lt;/p&gt;

&lt;p&gt;You get a higher interest rate on bonds because banks know exactly how long they have your money for and how long they can loan it out.&lt;/p&gt;

&lt;p&gt;DeFi is all about removing the middleman as such we will go straight to the next section on lending and borrowing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lending and Borrowing
&lt;/h3&gt;

&lt;p&gt;The first lending protocol that was introduced was the MakerDAO smart contracts. MakerDAO introduced DAI as a stablecoin. DAI is pegged to 1 USD, this is done programmatically through the use of smart contracts.&lt;/p&gt;

&lt;p&gt;In order to get DAI, you need to deposit another cryptocurrency to use as collateral. In fact, you need to deposit 150% of what you want to borrow.&lt;/p&gt;

&lt;p&gt;So why would you want to do this?&lt;/p&gt;

&lt;p&gt;Let’s say you want to buy another coin like BAND. So you swap your 1 ETH (currently at ~$4,600) for the equivalent in BAND.&lt;/p&gt;

&lt;p&gt;BAND then drops 50%. You now only have $2,300 worth of BAND. In the meantime, ETH has skyrocketed to $8,000 and now you really wish you had kept it in ETH.&lt;/p&gt;

&lt;p&gt;Now, let’s say instead you deposited it for DAI. You now have $3,000 worth of DAI (remember you need 150% as collateral).&lt;/p&gt;

&lt;p&gt;You do the same thing and buy BAND. It again drops 50% as before. However, your original ETH is still tied up in your loan. If you can get $3,000 DAI plus the interest you can get back your 1 ETH which is now worth $8000.&lt;/p&gt;

&lt;p&gt;There are now lots of other decentralised lending platforms that let you lock up cryptocurrency and get back interest in return. Alternatively, you can use it as collateral for taking out a loan. Smart contracts ensure that your collateral doesn’t drop below the loan amount.&lt;/p&gt;

&lt;p&gt;The most popular lending platforms are &lt;a href="https://compound.finance/"&gt;Compound&lt;/a&gt; and &lt;a href="https://aave.com/"&gt;Aave&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you deposit ETH you will get back cETH with Compound or aETH with Aave. These tokens are pegged against the original amount you deposited and will earn interest every second you hold them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Insurance
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0EF2DpcW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1637322452934/ki3Dp1kl4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0EF2DpcW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1637322452934/ki3Dp1kl4.png" alt="Nexus Mutual" width="880" height="531"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Even with everything being decentralised there are still risks with crypto. For example, your exchange could get hacked or there could be a bug in the smart contract you are using.&lt;/p&gt;

&lt;p&gt;Buying “insurance” works a bit differently from traditional insurance companies. These decentralised insurance providers are owned and run by their members and use Ethereum smart contracts.&lt;/p&gt;

&lt;p&gt;With Nexus Mutual members buy NXM and then stake them against smart contracts that they believe are secure. The members then receive rewards when users buy insurance for those contracts and don’t claim on them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nexusmutual.io/"&gt;Nexus Mutual&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bridgemutual.io/"&gt;Bridge Mutual&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Margin Trading
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UKE70OH1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1637322410272/cEw7oa3OO.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UKE70OH1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1637322410272/cEw7oa3OO.png" alt="Margin Trading" width="880" height="565"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Margin trading is when you borrow money to invest so that you get larger returns. Being decentralised you borrow money from lenders who in return earn interest from you on the money borrowed.&lt;/p&gt;

&lt;p&gt;Everything is controlled using Smart Contracts. Borrowing money to buy another asset is called a leveraged position. These leveraged positions are controlled by smart contracts. If the value of your asset you bought drops too much then the leveraged position is automatically liquidated.&lt;/p&gt;

&lt;p&gt;Some of these exchanges also have additional insurance in place so that lenders don’t lose their initial investment.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://fulcrum.trade/"&gt;Fulcrum&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dydx.exchange/"&gt;dydx&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Derivatives
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QbOX2yni--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1637322322161/aqaolO-j-.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QbOX2yni--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1637322322161/aqaolO-j-.png" alt="Synthetix" width="880" height="578"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Derivatives work in a similar way as in the CeFi world but the derivatives are managed using smart contracts.&lt;/p&gt;

&lt;p&gt;Derivatives in DeFi aren’t just limited to crypto though. You can buy derivatives in the same things you can in the CeFi world. However, the key difference is that derivatives are just ERC-20 tokens and as such can be exchanged for other tokens on DeFi platforms or invested again.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.synthetix.io/"&gt;Synthetix&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Hopefully, this post gives you a basic understanding of what CeFi and DeFi are and how they differ.&lt;/p&gt;

&lt;p&gt;I am not a financial expert so feel free to correct me if something isn’t right. If you know of any other platforms that are worth mentioning in this article let me know in the comments and I will add them.&lt;/p&gt;

</description>
      <category>web3</category>
      <category>defi</category>
      <category>ethereum</category>
      <category>crypto</category>
    </item>
    <item>
      <title>Using Hashnode as a Source for Gatsby</title>
      <dc:creator>Alex Hyett</dc:creator>
      <pubDate>Fri, 05 Nov 2021 17:51:49 +0000</pubDate>
      <link>https://dev.to/alexhyettdev/using-hashnode-as-a-source-for-gatsby-hk8</link>
      <guid>https://dev.to/alexhyettdev/using-hashnode-as-a-source-for-gatsby-hk8</guid>
      <description>&lt;p&gt;I have done quite a few website migrations over the years. My blog like many first started out on WordPress. After several &lt;a href="https://www.alexhyett.com/speed-up-wordpress"&gt;attempts at optimisation&lt;/a&gt;, I ended up generating a static version of my WordPress website.&lt;/p&gt;

&lt;p&gt;On my static site generation journey, I discovered Gatsby.js and completely redesigned my website around it. However, writing posts locally in markdown has its own downsides and lacks the discoverability you get from blogging on Hashnode.&lt;/p&gt;

&lt;p&gt;So I decided to move my blog posts over to Hashnode but I still wanted my posts to appear on my &lt;a href="https://www.alexhyett.com/blog"&gt;main blog&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migrating the Blog Posts
&lt;/h2&gt;

&lt;p&gt;The first step was to import all my blog posts to Hashnode. There are a few methods you can use to import your blog posts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EnL_LOmN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1636016684113/yDQESA6zu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EnL_LOmN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1636016684113/yDQESA6zu.png" alt="Hashnode import methods" width="880" height="373"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I decided to use the Dev.to importer as I repost all my posts on dev.to anyway. Unfortunately, the bulk import didn’t work for me so I had to import my posts one at a time.&lt;/p&gt;

&lt;p&gt;This was handy though as you need to set the following for each post manually:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Post slug&lt;/li&gt;
&lt;li&gt;Tags&lt;/li&gt;
&lt;li&gt;Canonical URL&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once all my posts were imported it was time to edit my Gatsby website to start pulling down the posts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gatsby plugins
&lt;/h2&gt;

&lt;p&gt;If you do a search for &lt;code&gt;gatsby-source-hashnode&lt;/code&gt; you will find a couple of plugins on the Gatsby website.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.gatsbyjs.com/plugins/gatsby-source-hashnode"&gt;gatsby-source-hashnode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.gatsbyjs.com/plugins/gatsby-source-hashnode-devblog"&gt;gatsby-source-hashnode-devblog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I decided to go with the first one (gatsby-source-hashnode) as it has a lot more downloads and still appears to be maintained.&lt;/p&gt;

&lt;p&gt;However, I quickly noticed when testing out the plugin that it was only pulling down the first 6 posts from my Hashnode blog. This might be a recent change to the API which hasn’t yet been picked up by the plugins.&lt;/p&gt;

&lt;p&gt;Luckily, the plugin is open source so I &lt;a href="https://github.com/nitzano/gatsby-source-hashnode/pull/184"&gt;raised a PR&lt;/a&gt; for the plugin owner to review.&lt;/p&gt;

&lt;p&gt;In the meantime, I copied my modified code to a folder in my gatsby repository called &lt;code&gt;plugins/gatsby-source-hashnode&lt;/code&gt;. I then used the plugin as described in the documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  GraphQL queries
&lt;/h2&gt;

&lt;p&gt;I am using GraphQL queries in a few places to pull down posts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Blog Feed&lt;/li&gt;
&lt;li&gt;Blog Post&lt;/li&gt;
&lt;li&gt;Latest Posts&lt;/li&gt;
&lt;li&gt;RSS Feed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For reference, this is what a couple of my queries look like for pulling from Hashnode.&lt;/p&gt;

&lt;h3&gt;
  
  
  Blog Feed
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;query pageQuery($skip: Int!, $limit: Int!) {
    site {
      siteMetadata {
        title
        description
      }
    }
    allHashNodePost(
      sort: { fields: [dateAdded], order: DESC }
      limit: $limit
      skip: $skip
    ) {
      edges {
        node {
          brief
          slug
          title
          coverImage {
            childImageSharp {
              gatsbyImageData(
                width: 920
                height: 483
                layout: CONSTRAINED
                transformOptions: {cropFocus: CENTER}
              )
            }
          }
        }
      }
    }
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Blog Post
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;query BlogPostBySlug($slug: String!) {
    site {
      siteMetadata {
        title
        author
        siteUrl
      }
    }
    hashNodePost(slug: { eq: $slug }) {
      _id
      brief
      childMarkdownRemark {
        htmlAst
      }
      slug
      title
      dateAdded
      coverImage {
        publicURL
        childImageSharp {
          gatsbyImageData(
            width: 920
            height: 483
            layout: CONSTRAINED
            transformOptions: {cropFocus: CENTER}
          )
        }
      }
    }
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I am not yet pulling down tags or reading time from the API. That is something I will try out later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automation with GitHub actions
&lt;/h2&gt;

&lt;p&gt;I already have GitHub actions set up for my website so that any new commits to the &lt;code&gt;main&lt;/code&gt; branch trigger a website build and push to S3. I &lt;a href="https://www.alexhyett.com/github-actions-deploy-to-s3"&gt;wrote a post&lt;/a&gt; about it if you want to find out how.&lt;/p&gt;

&lt;p&gt;However, now that I am not committing articles to that repo I needed a way to automatically trigger builds when I publish a new post on Hashnode.&lt;/p&gt;

&lt;p&gt;Luckily, Hashnode has an option to backup your blog posts to GitHub which we can use as the trigger point.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Add workflow_dispatch to your GitHub action
&lt;/h3&gt;

&lt;p&gt;To be able to remotely trigger a build on GitHub you need to add &lt;code&gt;workflow_dispatch&lt;/code&gt; to the &lt;code&gt;on:&lt;/code&gt; section of your workflow file.&lt;/p&gt;

&lt;p&gt;This is what the top of mine looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name: Deploy Blog

on:
  workflow_dispatch:
  push:
    branches:
      - main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Create a personal access token
&lt;/h3&gt;

&lt;p&gt;Next, we need to create a personal access token that we will use as the API Key for triggering the remote build.&lt;/p&gt;

&lt;p&gt;You can do this in GitHub under Settings &amp;gt; Developer Settings &amp;gt; &lt;a href="https://github.com/settings/tokens"&gt;Personal Access Tokens&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I set my access token with the following permissions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FPCh5y2A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1636018806528/CHKEV-sv_.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FPCh5y2A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1636018806528/CHKEV-sv_.png" alt="GitHub Access" width="880" height="738"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You will need to set an expiry date as well and set yourself a reminder to generate a new token before it expires.&lt;/p&gt;

&lt;p&gt;Make sure you copy the access key as you won’t get a chance again!&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Add the key as a secret to your backup repository
&lt;/h3&gt;

&lt;p&gt;To be able to use this access key we need to add it as a secret to the repository where Hashnode is backing up your posts.&lt;/p&gt;

&lt;p&gt;On the backup repository go to Settings &amp;gt; Secrets and add a new repository secret called &lt;code&gt;ACCESS_TOKEN&lt;/code&gt; and put in the key from the previous step.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Add a workflow file
&lt;/h3&gt;

&lt;p&gt;Next, we need to add a workflow that gets triggered every time a commit is added to this repository. In our case, this will be whenever we publish a post on Hashnode (Note: Drafts aren’t saved in GitHub).&lt;/p&gt;

&lt;p&gt;Add the following file to your backup repository &lt;code&gt;.github/workflows/workflow.yml&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;--------
name: Trigger Deploy

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - run: |
          curl -X POST \
          -H "Authorization: Bearer ${{secrets.ACCESS_TOKEN}}" \
          -H "Accept: application/vnd.github.v3+json" \
          https://api.github.com/repos/GITHUB_USERNAME/WEBSITE_REPO/actions/workflows/workflow.yml/dispatches \
          -d '{"ref": "main"}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure you change &lt;code&gt;GITHUB_USERNAME&lt;/code&gt; to match your GitHub username, &lt;code&gt;WEBSITE_REPO&lt;/code&gt; to the repository with your Gatsby.js website and &lt;code&gt;workflow.yml&lt;/code&gt; to match the name of your deploy workflow file.&lt;/p&gt;

&lt;p&gt;To test, try updating one of your posts in Hashnode and you see this workflow get triggered then followed by your website workflow.&lt;/p&gt;

</description>
      <category>gatsby</category>
      <category>hashnode</category>
    </item>
    <item>
      <title>What is Web3? The Decentralised Internet Explained</title>
      <dc:creator>Alex Hyett</dc:creator>
      <pubDate>Fri, 15 Oct 2021 12:00:00 +0000</pubDate>
      <link>https://dev.to/alexhyettdev/what-is-web3-the-decentralised-internet-explained-1fh</link>
      <guid>https://dev.to/alexhyettdev/what-is-web3-the-decentralised-internet-explained-1fh</guid>
      <description>&lt;p&gt;If you have stumbled upon this article you have probably seen the term web3 being mentioned around the internet. Especially on Twitter at the moment it seems to be the latest buzzword.&lt;/p&gt;

&lt;p&gt;With all the hype around NFTs and crypto at the moment, a lot of people are assuming that this is all web3 is. However, web3 (or web 3.0) has a lot more to it than that.&lt;/p&gt;

&lt;p&gt;Before I go into more details, let us look at how we got here and what we consider web 1.0 and 2.0.&lt;/p&gt;

&lt;h2&gt;
  
  
  Web 1.0: The Read-Only Web
&lt;/h2&gt;

&lt;p&gt;When the internet first went mainstream it consisted of static HTML pages that were uploaded to servers using FTP.&lt;/p&gt;

&lt;p&gt;People who had the technical skills were the only ones that could contribute to the internet at that time. The internet was limited to news sites and technical sites and wasn’t of great interest to the general public.&lt;/p&gt;

&lt;p&gt;This stage of the internet is now nicknamed the read-only web.&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/753308eb647d6791a57c4ab0a3599d45/a1792/first-website.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_IcqGU_8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.alexhyett.com/static/753308eb647d6791a57c4ab0a3599d45/a1792/first-website.png" alt="First Website" title="First Website" width="780" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Web 2.0: The Social Web
&lt;/h2&gt;

&lt;p&gt;Soon websites started to be backed by databases instead of static files. It was now possible to interact with websites and not just read them.&lt;/p&gt;

&lt;p&gt;Websites started allowing people to comment on their articles and soon we had online stores that we could buy from.&lt;/p&gt;

&lt;p&gt;As traffic to these websites increased people started looking for ways to monetise their work. As with traditional media such as magazines and newspapers, the solution was to sell advertising space on their website to companies looking to advertise their products and services.&lt;/p&gt;

&lt;p&gt;In the early days of Web 2.0, apart from comments the content on the web was still mostly produced by website owners.&lt;/p&gt;

&lt;h3&gt;
  
  
  Social media begins
&lt;/h3&gt;

&lt;p&gt;In 2003 MySpace was launched. People now had a space to create their own content and share it with others. By 2005 MySpace was seeing 16 million monthly users and was acquired by News Corporation for $580 million.&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/2e88f3a2b6c5b38d7ccafa464db5b5be/5595f/facebook.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_NhY2YTi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.alexhyett.com/static/2e88f3a2b6c5b38d7ccafa464db5b5be/5595f/facebook.png" alt="Facebook" title="Facebook" width="474" height="166"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Facebook was already out but limited to university students. This all changed in 2006 when Facebook opened up to everyone over 13. It didn’t take long for everyone and their Mum to have a Facebook account and sharing everything about themselves with the world.&lt;/p&gt;

&lt;p&gt;With everyone using Facebook and Google, these companies amassed a large amount of data about their users.&lt;/p&gt;

&lt;p&gt;Facebook was looking for ways of monetising its site. As selling the user data was morally wrong (although it didn’t stop some) they instead went with allowing people to target users with adverts.&lt;/p&gt;

&lt;p&gt;Instead of paying for advertising space on a website and hoping your target market was looking, you could now push adverts directly into people’s Facebook feeds based on age, sex, location and interests.&lt;/p&gt;

&lt;p&gt;Social media companies started reaching billion-dollar valuations even before they started monetising due to the information they had on their millions of users.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If the product is free, you are the product.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Tech companies didn’t need to sell products or charge for the use of their service. Instead, they could collect data on their users and sell access to that anonymised data to companies.&lt;/p&gt;

&lt;p&gt;The users became the product.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is wrong with the internet now?
&lt;/h2&gt;

&lt;p&gt;Of course, with there being so much value in user data these companies became targets for hackers and over the last decade, there have been a lot of high profile data leaks.&lt;/p&gt;

&lt;p&gt;Not convinced, sites like &lt;a href="https://haveibeenpwned.com/"&gt;Have I Been Pwned&lt;/a&gt; can show you whether your details have been compromised. There is also a very long list of data breaches listed on &lt;a href="https://en.wikipedia.org/wiki/List_of_data_breaches"&gt;Wikipedia&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have used any of the major websites chances are your details have been leaked.&lt;/p&gt;

&lt;p&gt;In some cases, it hasn’t been the result of hackers but the companies outright selling your data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where is your data?
&lt;/h3&gt;

&lt;p&gt;If you are an active internet user like me, chances are you have lost track of the number of websites that you have given your email address and other information.&lt;/p&gt;

&lt;p&gt;There are so many services that I have signed up to but later decided it wasn’t what I was looking for. Most of these sites don’t have ways to delete your account and there is no way of knowing they do delete your details anyway.&lt;/p&gt;

&lt;p&gt;Let’s take the example of buying something from a trusted website.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;As part of the checkout process you have to enter your name, email, card number and billing address (even if you are buying a digital product). These are stored (hopefully securely) on the servers of the company you are buying from. Depending on how the company has set up their store this could be another third party like Shopify.&lt;/li&gt;
&lt;li&gt;To be able to take payment for your order, they need to use a payment gateway like Stripe. So your name, email address, card number and billing address are now on the payment gateway’s servers too.&lt;/li&gt;
&lt;li&gt;Did you get an email confirmation of your order and possibly some marketing emails? Well, your data is now stored on their email providers servers such as Mailchimp.&lt;/li&gt;
&lt;li&gt;If you bought a physical product then I can guarantee the company isn’t going to hand-deliver it to you. They are going to use a courier and as such your details would be given to them as well so they can deliver your product and contact you in case of difficulties.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Chances are you would have agreed to all this in the 20 pages of terms and conditions you agreed to but didn’t read.&lt;/p&gt;

&lt;p&gt;Of course, to be able to have a credit or debit card you would have had to hand over all your details and money to a bank. They also would have used a bunch of third parties to be able to validate who you are and print the cards you are using.&lt;/p&gt;

&lt;h2&gt;
  
  
  Web 3.0: The Decentralised Web
&lt;/h2&gt;

&lt;p&gt;In 2008 a &lt;a href="https://bitcoinwhitepaper.co/"&gt;whitepaper&lt;/a&gt; was written by someone called &lt;a href="https://en.wikipedia.org/wiki/Satoshi_Nakamoto"&gt;Satoshi Nakamoto&lt;/a&gt; on a peer to peer cash system that lives on a “blockchain” and Bitcoin was born.&lt;/p&gt;

&lt;p&gt;With cryptocurrency, it is now possible to send money (or crypto tokens) from one wallet to another without sharing any personal information. No third party is involved it is purely peer-to-peer.&lt;/p&gt;

&lt;p&gt;Now if this blockchain was hosted by a single company it wouldn’t be much better than the payment system we have now. However, the blockchain is decentralised, not owned by a single entity and impossible to take down. All the payments are immutable and verified by the network.&lt;/p&gt;

&lt;p&gt;If something is completely decentralised then it cannot be taken down as it doesn’t exist in a single place. Of course, this is useful for more than just payments.&lt;/p&gt;

&lt;p&gt;The power of web3 is the ability to be able to create applications and websites that live and work with the blockchain. Completely decentralised and available to everyone.&lt;/p&gt;

&lt;h3&gt;
  
  
  NFTs
&lt;/h3&gt;

&lt;p&gt;NFTs or Non-Fungible Tokens are assets that can be bought for cryptocurrency and stored in your crypto wallet.&lt;/p&gt;

&lt;p&gt;You will probably be familiar with NFTs from people talking about NFT art. NFT Art is about ownership. Yes, it might just be a JPEG that you are buying but you own it and can prove that you own it.&lt;/p&gt;

&lt;p&gt;It is the digital equivalent of owning the Mona Lisa versus owning a print.&lt;/p&gt;

&lt;p&gt;The uses of NFT go far beyond the artwork. It provides a trustless system for ownership. Anything digital that you own can be represented as an NFT and then verified by a third party that you are the owner.&lt;/p&gt;

&lt;p&gt;Nearly every website you log into requires a username and password. With web3 all you need is your wallet connected with your NFT. No personal information needs to be transferred.&lt;/p&gt;

&lt;p&gt;Not only that but you can transfer your ownership to someone else. This is something that the digital space has been sorely lacking.&lt;/p&gt;

&lt;p&gt;Let’s have a look at a few examples:&lt;/p&gt;

&lt;h4&gt;
  
  
  Digital Media
&lt;/h4&gt;

&lt;p&gt;&lt;a href="///static/67e6017f0afad620551dcb6ab251db16/9d6a9/books.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4g2Nxh_v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.alexhyett.com/static/67e6017f0afad620551dcb6ab251db16/c0255/books.png" alt="Books" title="Books" width="880" height="585"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At some point, you have probably bought a digital copy of a book, game, song or movie.&lt;/p&gt;

&lt;p&gt;Chances are it wasn’t much cheaper than buying the physical object. However, with a physical book, you can donate or sell it once you are done. With a digital copy, you don’t have that option.&lt;/p&gt;

&lt;p&gt;However, if that book or game was an NFT in your wallet you would be able to donate or sell it when you no longer wanted it.&lt;/p&gt;

&lt;h4&gt;
  
  
  Authorisation
&lt;/h4&gt;

&lt;p&gt;An NFT can also be used to provide access to a resource. By having the NFT in your wallet a web3 website can check whether it exists before granting you access.&lt;/p&gt;

&lt;p&gt;Let’s say you bought an online course. You might be given lifetime access to this course but if you find you no longer need it you are stuck with it.&lt;/p&gt;

&lt;p&gt;However, if course access was granted via an NFT then you would have the ability to sell or donate that course when you were done with it.&lt;/p&gt;

&lt;p&gt;This can be used in the physical world too. Any event that requires a ticket could be sold as an NFT. Only those with that NFT in their wallet have access.&lt;/p&gt;

&lt;h4&gt;
  
  
  Gaming
&lt;/h4&gt;

&lt;p&gt;I love the scene at the start of Ready Player One where they all fighting in one of the game rooms to try and get the special item (I think it was a sword but I need to watch it again).&lt;/p&gt;

&lt;p&gt;In the online gaming world, there are already unofficial markets for gaming items. I never played WoW but I remember people selling their rare items on marketplaces.&lt;/p&gt;

&lt;p&gt;The problem is you can easily be scammed as you are trusting the user to hand over the item once you have paid. Or trusting the marketplace to verify that the money and items have been transferred.&lt;/p&gt;

&lt;p&gt;If game items were NFTs they would be yours to own and transfer.&lt;/p&gt;

&lt;p&gt;There are already some games coming out that rely on NFTs and these are being coined Play-To-Earn games. &lt;a href="https://axieinfinity.com/"&gt;Axie Infinity&lt;/a&gt; is the most popular at the moment.&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/3daca2e52fc96cb70fe33d9b353ac20b/3ceac/axie-infinity.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--j6COJHxM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.alexhyett.com/static/3daca2e52fc96cb70fe33d9b353ac20b/c0255/axie-infinity.png" alt="Axie Infinity" title="Axie Infinity" width="880" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In some cases, it is possible to earn several thousand dollars a month playing this game.&lt;/p&gt;

&lt;p&gt;This game is particularly popular in some countries in Asia at the moment as people can earn more playing this game than they can from local jobs. What would you rather do, work in a factory, on the fields or at home playing a video game?&lt;/p&gt;

&lt;p&gt;Unfortunately, you need to own 3 Axies to start playing and with prices starting at 0.034 ETH ($129) per Axie it can be steep for some.&lt;/p&gt;

&lt;p&gt;Axie Infinity has started offering sponsorships as an option. Where people can pay for the Axie’s for someone else and in return get a percentage of the profits.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Spatial Connected Web
&lt;/h3&gt;

&lt;p&gt;The other aspect of web3 is how the physical world is going to merge with our digital one.&lt;/p&gt;

&lt;p&gt;We now have a few VR (virtual reality) headsets on the market as well as AR (augmented reality) built into our phones.&lt;/p&gt;

&lt;p&gt;Facebook is also currently working on &lt;a href="https://about.facebook.com/realitylabs/projectaria/"&gt;AR glasses&lt;/a&gt;, I am sure other companies too.&lt;/p&gt;

&lt;p&gt;As more devices get released, there are sure to be more uses for them. The same way everyone has come to embrace wireless payments at some point the same will be true for AR and VR.&lt;/p&gt;

&lt;p&gt;You can read more about the spatial web in the book &lt;a href="https://geni.us/afx1Bf"&gt;The Spatial Web&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Potential uses
&lt;/h4&gt;

&lt;p&gt;Imagine wearing your AR glasses to the supermarket and being shown a route to the items on your shopping list.&lt;/p&gt;

&lt;p&gt;AR could also be used to subtly highlight the products on the shelves that meet your dietary requirements. No need to look at labels to check for wheat or dairy.&lt;/p&gt;

&lt;p&gt;Looking online for clothes but not sure how they would look on you? Well, your AR glasses could help with that too. Augmenting the clothes you pick onto your body, maybe even helping you pick the correct size.&lt;/p&gt;

&lt;p&gt;Can’t get out at the moment? Wellm why not visit the same shops but virtually and pick the items from your shelves in VR.&lt;/p&gt;

&lt;p&gt;The problem with these uses at the moment is it is dependent on device support. It isn’t going to work if some stores only support certain devices.&lt;/p&gt;

&lt;p&gt;Imagine being able to use your Apple Glasses in Waitrose but you need Samsung Glasses in Asda. It just won’t work. This is where the metaverse comes in.&lt;/p&gt;

&lt;h4&gt;
  
  
  Metaverse
&lt;/h4&gt;

&lt;p&gt;The virtual world (or worlds) and the augmented world is being collectively called the metaverse. The metaverse is an interconnected network of worlds that allow continuity of identity and objects.&lt;/p&gt;

&lt;p&gt;Currently, the apps and games we use are often dependent on the devices we have.&lt;/p&gt;

&lt;p&gt;Some games are only available on XBOX or Playstation while others can be played on all devices.&lt;/p&gt;

&lt;p&gt;Even online games available on all platforms have limitations between them. Sometimes you can’t see other players on a different device than yours and you certainly can’t transfer your saved game between different types of devices.&lt;/p&gt;

&lt;p&gt;The metaverse is completely connected so not only would you be able to move your identity between different devices but also between different virtual worlds. Everything is interconnected. Say you win a sword in one game, you could then take this sword with you and use it in another game.&lt;/p&gt;

&lt;p&gt;Again, I am going to reference Ready Player One here. Where no matter what virtual world they were in, their identity travelled with them, as did their inventory and they could use that in any of the other worlds.&lt;/p&gt;

&lt;p&gt;Where do you think your identity and inventory would live? You guessed it, your digital wallet on the blockchain.&lt;/p&gt;

&lt;p&gt;With the help of AR you could even see your VR items in the real world too.&lt;/p&gt;

&lt;h3&gt;
  
  
  DAO: Decentralised Autonomous Organisations
&lt;/h3&gt;

&lt;p&gt;With everything else being decentralised then why not businesses. Instead of a business being owned by a CEO and its founders, a DAO is own by all of its token holders. Each token holder is given voting rights and can decide on the direction the DAO will take.&lt;/p&gt;

&lt;p&gt;Any profits made by a DAO are then evenly distributed to the token holders.&lt;/p&gt;

&lt;p&gt;There are a few DAOs going around at the moment. It is worth having a read about them and see if you want (or can) join them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://makerdao.com/en/"&gt;MakerDAO&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/developer_dao"&gt;Developer DAO&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/thugDAO"&gt;Thug DAO&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Where we are now
&lt;/h3&gt;

&lt;p&gt;This all sounds great but we are only at the beginning at the moment. We are a little way off chasing keys to the kingdom in the Oasis metaverse just yet.&lt;/p&gt;

&lt;p&gt;So we have our decentralised currency that, will work across all our virtual worlds and soon the physical world. Bitcoin is the main candidate and now being legal tender in El Salvador.&lt;/p&gt;

&lt;p&gt;However, we also have a mind-boggling array of other cryptocurrencies. Ethereum is the most well known. It builds on Bitcoin by having smart contracts and assets which give us NFTs. It is currently hindered by the expensive “gas” fees that are needed to do anything.&lt;/p&gt;

&lt;p&gt;Until cryptocurrency is accepted in physical stores we are still reliant on exchanges to convert the currency back into local fiat currencies. This usually involves handing over a lot of personal information before you can make the conversion.&lt;/p&gt;

&lt;p&gt;At some point, we may select a winning cryptocurrency but for now, we need to cope with all the different varieties and rely on exchanges to convert between them.&lt;/p&gt;

&lt;p&gt;Web3 websites are starting to crop up here and there mostly around the NFT space. If you own an NFT you gain access to their community and unlock areas of their website. I expect we will see more websites using NFTs for access soon.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started with Web3 Development
&lt;/h2&gt;

&lt;p&gt;Currently, web3 is still browser-based. You will need a browser extension to be able to access web3 features.&lt;/p&gt;

&lt;p&gt;You need the &lt;a href="https://metamask.io/"&gt;Metamask&lt;/a&gt; extension, that we allow you to connect your wallet to web3 websites. Provided you have funds in your wallet you will be able to mint NFTs on their websites as well active the owners only features.&lt;/p&gt;

&lt;p&gt;If you want to get started with web3 development there are some great resources out there already.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://twitter.com/dabit3"&gt;Nader Dabit&lt;/a&gt; has a couple of great articles on &lt;a href="https://dev.to/dabit3/"&gt;dev.to&lt;/a&gt;. He also has some great content on his &lt;a href="https://www.youtube.com/naderdabit/"&gt;YouTube Channel&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;If you are interested in building NFTs then &lt;a href="https://twitter.com/de_botha"&gt;Daniel Botha&lt;/a&gt; has loads of videos on the subject on his &lt;a href="https://www.youtube.com/c/hashlipsnft/featured"&gt;Hashlips YouTube&lt;/a&gt; channel.&lt;/li&gt;
&lt;li&gt;Join the &lt;a href="https://twitter.com/developer_dao"&gt;Developer DAO&lt;/a&gt;. You will need to mint a “free” (gas fees still apply) NFT token to join &lt;a href="https://developerdao.vercel.app/"&gt;https://developerdao.vercel.app/&lt;/a&gt;. There are only 8,000 so join soon. This will give you access to contribute to the discord community.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I am still getting started myself with web3 but it is an interesting space and I am sure to post more content here soon.&lt;/p&gt;

</description>
      <category>web3</category>
    </item>
    <item>
      <title>Ditch the Ads: The Future of Website Monetization</title>
      <dc:creator>Alex Hyett</dc:creator>
      <pubDate>Fri, 08 Oct 2021 12:00:00 +0000</pubDate>
      <link>https://dev.to/alexhyettdev/ditch-the-ads-the-future-of-website-monetization-330h</link>
      <guid>https://dev.to/alexhyettdev/ditch-the-ads-the-future-of-website-monetization-330h</guid>
      <description>&lt;p&gt;Do you remember that scene in Ready Player One, when IOI are showing what the future of the Oasis will look like once they take over?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“We have determined that we’ll be able to fill 80% of the user’s display with advertising before inducing seizures”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/35MG6KoNC3zyAkGes0/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/35MG6KoNC3zyAkGes0/giphy.gif" alt="Ready Player One" width="480" height="198"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Display Advertising
&lt;/h2&gt;

&lt;p&gt;We aren’t living our lives in virtual reality just yet but already it feels like adverts are taking over the web. Especially if you go on some of the big news websites it can be hard to distinguish the content from the adverts.&lt;/p&gt;

&lt;p&gt;I recently experimented with using Google Adsense on this website and the results were surprising. Now, this isn’t a high traffic website. I currently only get around 250 - 300 page views a day. However, I was still able to “earn” on average £0.50 a day. This isn’t bad considering I haven’t written a post in 4 months and this is entirely passive income.&lt;/p&gt;

&lt;p&gt;Over the month, this would have added up to around £15, which is more than enough to pay for this websites expenses. However, with a minimum Adsense payout of £60, it would have been 4 months until I was able to cash out.&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/71ce6c27e553675e870b40b6a231b46c/8deec/adsense-earnings.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DgSCZNnj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.alexhyett.com/static/71ce6c27e553675e870b40b6a231b46c/c0255/adsense-earnings.png" alt="Adsense Earnings" title="Adsense Earnings" width="880" height="258"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I used the Auto Ads feature of Google Adsense and made sure I turned off the personalised ads and the obnoxious page filling ads. I also made sure to only include ads on blog post pages and turned the ad load down to half of the recommended setting.&lt;/p&gt;

&lt;p&gt;This resulted in around 5 - 8 adverts per blog post mixed in amongst the content. If I was only interested in the money, then I could have left them on and in a few more months I would have got a paycheque from the big G.&lt;/p&gt;

&lt;p&gt;However, I wasn’t comfortable not knowing exactly what adverts were being shown on my website and with the auto ads feature you have little control over where the ads appear.&lt;/p&gt;

&lt;p&gt;Adverts have given website owners a lazy way of generating an income from their content but times are changing and more people are choosing to block adverts.&lt;/p&gt;

&lt;p&gt;As a software developer with a tech-savvy readership, the chances of my readers choosing to block adverts is quite high. I even have a Pi-Hole set up in my house to block adverts on all my devices (I should really do a tutorial on this). So it seems a bit hypocritical to serve adverts on my website as well.&lt;/p&gt;

&lt;p&gt;So what is the alternative? It turns out there are quite a few alternatives to make an income from your content without resorting to advertising.&lt;/p&gt;

&lt;h2&gt;
  
  
  Medium
&lt;/h2&gt;

&lt;p&gt;As part of my blog post syndication, I also re-publish my blog posts on &lt;a href="https://alexhyett.medium.com/"&gt;Medium&lt;/a&gt; with a canonical link back to my website. As you are probably aware, Medium has a paid membership service where you pay $5 a month which allows you to get access to all articles on Medium.&lt;/p&gt;

&lt;p&gt;For publishers you get a portion of this membership, depending on how many people read your articles. It is similar to how authors earn revenue on course sites like Pluralsight.&lt;/p&gt;

&lt;p&gt;My articles aren’t that popular on Medium. I think part of that is because since they are re-published rather than Medium exclusive articles. So not only are they less likely to be promoted by Medium, they are also less likely to rank in Google as the original article is on &lt;a href="https://www.alexhyett.com/"&gt;alexhyett.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So far this year I have earned $0.40 (£0.30) from Medium. I won’t be cracking open the champagne just yet.&lt;/p&gt;

&lt;p&gt;However, on Medium, I have only had 26 people read my content according to the Medium stats. So actually $0.015 per reading isn’t that bad. I will continue publishing on Medium and may even do some Medium exclusive posts if the content doesn’t quite match what I normally publish on this site.&lt;/p&gt;

&lt;h2&gt;
  
  
  Web Monetization
&lt;/h2&gt;

&lt;p&gt;With the rise of cryptocurrency coming into mainstream use, we now have other ways of transferring money from one person to another.&lt;/p&gt;

&lt;p&gt;Including the ability to stream fractional amounts of money to the content creators you visit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Metadata tag monetization
&lt;/h3&gt;

&lt;p&gt;This has now made its way into the browser and is also being proposed as a W3C standard. You can read more about the standard at &lt;a href="https://webmonetization.org/"&gt;webmonetization.org&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is all done with a metadata tag on your website. This is what mine looks like:&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;meta name="monetization" content="$ilp.uphold.com/3QmRGLJq4F9d"&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can get your own wallet address by signing up with an account with &lt;a href="https://uphold.com/en-gb"&gt;Uphold&lt;/a&gt; or &lt;a href="https://gatehub.net/"&gt;Gatehub&lt;/a&gt;. This will allow you to start receiving funds via the &lt;a href="https://interledger.org/"&gt;Interledger network&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At the moment there is only one Web Monetization Provider called &lt;a href="https://coil.com/"&gt;Coil&lt;/a&gt;. Coil is a bit like Medium, in that it costs $5 a month and that money is used to distribute funds to the websites you visit. You will need to install the Coil Extension in your browser to start supporting content creators.&lt;/p&gt;

&lt;p&gt;Once the above has been added to your website you will start to receive micropayments from your readers who are signed up for Coil. You get $0.36 for every hour a visitor spends on your site. Payments are made using micropayments every second per visitor.&lt;/p&gt;

&lt;p&gt;You can even link this to your &lt;a href="https://dev.to/settings/extensions"&gt;Dev.to&lt;/a&gt; account and receive funds from your readers there too. You will find this option under Settings &amp;gt; Extensions &amp;gt; Web monetization.&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/bed15f6d094f6c1ef6a2ba98b78f2893/66632/dev-to-settings.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--37qpV6xj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.alexhyett.com/static/bed15f6d094f6c1ef6a2ba98b78f2893/c0255/dev-to-settings.png" alt="Dev To Settings" title="Dev To Settings" width="880" height="329"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you sign up for Coil and download the extension you will see this when you are on a monetised website:&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/9a0eb6f467723e2dc48b9d5288e9b8a1/41be6/coil-extension.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SuIQZ6ZY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.alexhyett.com/static/9a0eb6f467723e2dc48b9d5288e9b8a1/41be6/coil-extension.png" alt="Coil Extension" title="Coil Extension" width="638" height="590"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can actually see money streaming into your account in the Uphold app which is pretty cool. To be able to transfer the money to your account you need to have received at least £0.02 which isn’t hard.&lt;/p&gt;

&lt;p&gt;It is also possible to make exclusive content for paying visitors which is something I will experiment with soon.&lt;/p&gt;

&lt;h3&gt;
  
  
  Paywall
&lt;/h3&gt;

&lt;p&gt;Talking of exclusive content. There are more and more websites that are locking all of their content behind a paywall. This is in response to dropping ad revenue.&lt;/p&gt;

&lt;p&gt;Generally, this is being used by big established media outlets like The Times or The Guardian. However, there are smaller websites that are locking content behind membership sites. They have most of their content available for free but for additional help, exclusive information and access to a community a membership option is available.&lt;/p&gt;

&lt;p&gt;If you are expecting people to pay monthly for access to your content you need to make sure you are committed to creating content on a regular basis.&lt;/p&gt;

&lt;h3&gt;
  
  
  Brave Browser
&lt;/h3&gt;

&lt;p&gt;The other option for streaming money is via the &lt;a href="https://brave.com/"&gt;Brave Browser&lt;/a&gt;. This browser is based on Chrome but is security and privacy-focused and has better support for Web3 with cryptocurrency wallets built-in.&lt;/p&gt;

&lt;p&gt;Brave will block ads and trackers for you while you visit websites. This does mean that sites that rely on adverts for their income won’t be getting any money from your visits. To counteract this Brave have come up with &lt;a href="https://brave.com/brave-rewards/"&gt;Brave Rewards&lt;/a&gt;. With Brave Rewards turned on you will get the occasional notifications adverts that don’t track you or use your data.&lt;/p&gt;

&lt;p&gt;Each time you view an advert you earn BAT tokens. These can then be converted to fiat currencies by you or you can use them to contribute to the websites you visit or you can even tip a website directly.&lt;/p&gt;

&lt;p&gt;To become a creator and earn BAT from visitors to your site you will need to &lt;a href="https://creators.brave.com/sign-up"&gt;signup and verify&lt;/a&gt; your website.&lt;/p&gt;

&lt;p&gt;Once verified visitors will see this in the browser:&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/37d64199b08db224e60f5d6a1358c943/1e043/brave-verified.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QwZIlenZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.alexhyett.com/static/37d64199b08db224e60f5d6a1358c943/1e043/brave-verified.png" alt="Brave Verified Creator" title="Brave Verified Creator" width="690" height="122"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Users then have the option to Send a Tip or add in a monthly contribution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Donations
&lt;/h2&gt;

&lt;p&gt;The other option you may have noticed on my website is my &lt;a href="https://www.buymeacoffee.com/alexhyett"&gt;Buy Me A Coffee&lt;/a&gt; button. We don’t think twice about spending £3 - 4 on a coffee at Starbucks but we don’t even consider paying for content that we consume. A long blog post can take many hours to research and write and the chances are the blog owner won’t earn anything from that.&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/ca078caea816ac9a49dab61a2a3e1a53/442cb/buy-me-a-coffee.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YT6SXnMv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.alexhyett.com/static/ca078caea816ac9a49dab61a2a3e1a53/442cb/buy-me-a-coffee.png" alt="Buy Me a Coffee" title="Buy Me a Coffee" width="752" height="1290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If a blog post has helped you and is written by a single developer then sending that person a “coffee” can be a great way to support them.&lt;/p&gt;

&lt;p&gt;Buy Me A Coffee is set up to send money directly to Stripe or Paypal which means there is no minimum payout, unlike ad networks.&lt;/p&gt;

&lt;p&gt;I am very grateful to the few people that have already bought me a coffee as a thank you for the articles I have written.&lt;/p&gt;

&lt;h2&gt;
  
  
  Affiliate Links
&lt;/h2&gt;

&lt;p&gt;The other way many website owners earn money is through affiliate links. Affiliates are people who promote another companies product and in return earn a commission.&lt;/p&gt;

&lt;p&gt;The person clicking the link doesn’t pay anymore but if they buy something, you will earn a commission from the sale. Lots of websites have affiliate programs you can sign up for. The commissions can vary from 4 - 25% and generally there will be a minimum payout before you can receive the funds.&lt;/p&gt;

&lt;p&gt;As a software developer, I don’t talk about a lot of products. The software I use daily is free after all (Visual Studio Code). However, I do have affiliate links to the physical things that make up my &lt;a href="https://www.alexhyett.com/resources/gear/"&gt;home office&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Product Sales
&lt;/h2&gt;

&lt;p&gt;Finally, the last ad-free way of making money on your blog is to sell products. This could be eBooks, software, website templates or courses. Of course, a lot of effort goes into building out a product but it can be very profitable.&lt;/p&gt;

&lt;p&gt;I have made some money in the past from website templates and desktop software and it can be a good little side hustle or even a full-time income.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Dealing with Imposter Syndrome as a Software Developer</title>
      <dc:creator>Alex Hyett</dc:creator>
      <pubDate>Fri, 28 May 2021 11:30:00 +0000</pubDate>
      <link>https://dev.to/alexhyettdev/dealing-with-imposter-syndrome-as-a-software-developer-1ce8</link>
      <guid>https://dev.to/alexhyettdev/dealing-with-imposter-syndrome-as-a-software-developer-1ce8</guid>
      <description>&lt;p&gt;I have been a professional software developer for over a decade and I have been writing code for over 25 years.&lt;/p&gt;

&lt;p&gt;However, sometimes I still feel like a fraud.&lt;/p&gt;

&lt;p&gt;It turns out, I am not the only one that feels this way and they have a name for it, it is called “Imposter Syndrome”.&lt;/p&gt;


&lt;center&gt;
&lt;br&gt;
    &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GcW15ZVi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgs.xkcd.com/comics/impostor_syndrome.png" alt="xkcd: Imposter Syndrome" width="318" height="379"&gt;&lt;br&gt;
  &lt;br&gt;
    &lt;a href="https://xkcd.com/1954/"&gt;xkcd: Imposter Syndrome&lt;/a&gt;&lt;br&gt;
  &lt;br&gt;
&lt;/center&gt;
&lt;h2&gt;
  
  
  What is Imposter Syndrome?
&lt;/h2&gt;

&lt;p&gt;Have you ever felt like you don’t know what you are doing? Do you feel like you aren’t good enough for your job, that you don’t deserve to be where you are in life?&lt;/p&gt;

&lt;p&gt;That everyone else seems to know more than you do and your are just stumbling along working things out as you go along. One of these days they are going to find out you don’t know what you are doing!&lt;/p&gt;

&lt;p&gt;If you have, you likely suffered from imposter syndrome.&lt;/p&gt;

&lt;h3&gt;
  
  
  Imposter Syndrome Definition
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.dictionary.com/browse/impostor-syndrome"&gt;Dictionary.com&lt;/a&gt; defines this as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;anxiety or self-doubt that results from persistently undervaluing one’s competence and active role in achieving success, while falsely attributing one’s accomplishments to luck or other external forces.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Essentially, imposter syndrome comes about when you doubt your abilities.&lt;/p&gt;

&lt;p&gt;It is common in all careers but I think it is especially apparent in the software development industry.&lt;/p&gt;

&lt;h3&gt;
  
  
  Imposter Syndrome as a Programmer
&lt;/h3&gt;

&lt;p&gt;We are constantly having to keep up with technologies and learn new skills. As such, it is impossible to be able to know everything. There will always be gaps in your knowledge and someone else is bound to know something that you don’t.&lt;/p&gt;

&lt;p&gt;Anyone who has worked in frontend development will relate to this. With new frameworks coming out every year, it can feel like you are constantly on a hamster wheel never getting to the finish line. You dedicate your time to learning Angular just to find out that everyone is switching to React or Vue, and now you are back to square one.&lt;/p&gt;

&lt;p&gt;This becomes especially apparent when you start to specialise in a particular area. The more time you dedicate to one subject, the less time you are going to have to learn about new things.&lt;/p&gt;

&lt;p&gt;This always reminds me of this quote:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You learn more and more about less and less until you know absolutely everything about nothing. - Nicholas Butler&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I would consider myself a full stack developer. I know enough technologies to be able to take a product from idea to production including, frontend, backend, database and infrastructure.&lt;/p&gt;

&lt;p&gt;However, I specialise in backend development. My frontend knowledge is limited to creating my own personal projects or updating existing projects. I wouldn’t say I was up to date with the latest best practices. My database knowledge is fairly good but I don’t know as much as a dedicated DBA. My infrastructure knowledge is getting better but I am constantly learning from my colleagues.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dunning-Kruger Effect
&lt;/h3&gt;

&lt;p&gt;What makes matters worse is something called the Dunning-Kruger effect. Another definition, this time from &lt;a href="https://en.wikipedia.org/wiki/Dunning%E2%80%93Kruger_effect"&gt;Wikipedia&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Dunning–Kruger effect is a hypothetical cognitive bias stating that people with low ability at a task overestimate their ability.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With the “Anyone Can Code” movement (which I am not entirely against) we have more and more people who know just enough to be dangerous. In some cases, they have only just started and think because they can put an HTML page together that they suddenly know all there is to know. Or they might have a couples of years experience and have got very good at one thing.&lt;/p&gt;

&lt;p&gt;These people tend to have a lot of confidence, as they are unaware at just how clueless they are about everything they don’t know.&lt;/p&gt;

&lt;p&gt;Whereas those of us with many years experience under our belt, understand that there is a lot that we don’t know and therefore you shouldn’t get too cocky with what you can do.&lt;/p&gt;

&lt;p&gt;However, as we end up feeling inadequate we are less likely to share what we do know with the world.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Get Over Imposter Syndrome?
&lt;/h2&gt;

&lt;p&gt;So know that you understand what imposter syndrome is (and probably relate to a lot of it), how do we deal with this?&lt;/p&gt;

&lt;h3&gt;
  
  
  Recognise your accomplishments
&lt;/h3&gt;

&lt;p&gt;A lot of us tend to downplay our accomplishments. Maybe you have just done a difficult release, managed to solve a bug that has been driving your team mad or even just completing your work for the sprint on time.&lt;/p&gt;

&lt;p&gt;If you just move on to the next task without patting yourself on the back it can feel like you aren’t accomplishing anything.&lt;/p&gt;

&lt;p&gt;I recommend you write down your achievements. This is especially important when it comes to review time and you need to tell your boss why you should get a pay rise.&lt;/p&gt;

&lt;h3&gt;
  
  
  Share your knowledge
&lt;/h3&gt;

&lt;p&gt;This is something that I am trying to do more. Push myself out of my comfort zone and get out there and share what I know.&lt;/p&gt;

&lt;p&gt;Whether this is via blog, company talk, Instagram, Twitter or YouTube, sharing is an important part of giving back to the development community and helping aspiring engineers improve their skills.&lt;/p&gt;

&lt;p&gt;I forget that I have been writing code for 25 years and end up thinking that I haven’t got anything worth sharing. However, not everyone is in the same place you are. Even if you are just starting out there is going to be someone who doesn’t know what you know and would benefit from your knowledge.&lt;/p&gt;

&lt;p&gt;So don’t be afraid to get out there and start that blog that you have been putting off for years.&lt;/p&gt;

&lt;p&gt;It is always nice to know that you have helped others and it can stop you from feeling like a fraud.&lt;/p&gt;

&lt;h3&gt;
  
  
  Be Humble
&lt;/h3&gt;

&lt;p&gt;This is my motto for life and the number 1 advice I give to others, “Be Humble”. Once you realise that you do actually know more than other people it can be difficult to keep your ego in check (how do you get your head out of the door?).&lt;/p&gt;

&lt;p&gt;So remind yourself to always be humble. Don’t look down on others that don’t know as much as you, as I can guarantee they know more than you about a different topic.&lt;/p&gt;

&lt;p&gt;Don’t be afraid to ask questions, even if you think it will make you look stupid. Chances are that if you don’t know the answer there will be someone else who doesn’t either. It is always better to clarify something than assume.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Never &lt;strong&gt;ASSUME&lt;/strong&gt; as it will make an &lt;strong&gt;ASS&lt;/strong&gt; out of &lt;strong&gt;U&lt;/strong&gt; and &lt;strong&gt;ME&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--T5eKNYmV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.alexhyett.com/static/f11683dab1dc91402bcefd89ad8a0411/4d836/BE-HUMBLE.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--T5eKNYmV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.alexhyett.com/static/f11683dab1dc91402bcefd89ad8a0411/4d836/BE-HUMBLE.jpg" alt="BE HUMBLE" title="BE HUMBLE" width="880" height="880"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;No matter how much you know, there is going to be plenty of things you are clueless about. Instead of getting down about it, take the opportunity to learn more and share your knowledge with others.&lt;/p&gt;

&lt;p&gt;Hopefully, this post will be helpful for those going through something similar.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Using WireMock.net for Integration Testing</title>
      <dc:creator>Alex Hyett</dc:creator>
      <pubDate>Fri, 21 May 2021 14:40:00 +0000</pubDate>
      <link>https://dev.to/alexhyettdev/using-wiremocknet-for-integration-testing-48ee</link>
      <guid>https://dev.to/alexhyettdev/using-wiremocknet-for-integration-testing-48ee</guid>
      <description>&lt;p&gt;Last week I showed you how you can use &lt;a href="https://www.alexhyett.com/mock-api-calls-wiremock/"&gt;Wiremock in a docker&lt;/a&gt; container to mock API calls that your application uses. This week is on a similar theme, but this time using WireMock.net to help with your integration tests.&lt;/p&gt;

&lt;p&gt;Unlike WireMock which is Java-based, as you can guess from the name &lt;a href="https://github.com/WireMock-Net/WireMock.Net"&gt;WireMock.Net&lt;/a&gt; is written in .NET and based on &lt;a href="https://github.com/alexvictoor/mock4net"&gt;mock4net&lt;/a&gt;. Everything you can do in Wiremock you can also do in WireMock.net. This also includes hosting it in a docker container like we did in the &lt;a href="https://www.alexhyett.com/mock-api-calls-wiremock/"&gt;last post&lt;/a&gt; (the ASCII art is missing though).&lt;/p&gt;

&lt;p&gt;In this tutorial, we are going to be looking at using WireMock.net to mock API calls in our integration tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;First of all, we need a .Net project to test. For this project I am just going to create a new .Net Core Web API from the templates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dotnet new webapi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will generate a simple API that gives us a random weather forecast.&lt;/p&gt;

&lt;p&gt;Running &lt;code&gt;dotnet run&lt;/code&gt; and then calling the GET endpoint &lt;code&gt;https://localhost:5001/WeatherForecast&lt;/code&gt; returns the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[
  {
    "date": "2021-05-22T10:22:17.824198+01:00",
    "temperatureC": 36,
    "temperatureF": 96,
    "summary": "Chilly"
  },
  {
    "date": "2021-05-23T10:22:17.824551+01:00",
    "temperatureC": 25,
    "temperatureF": 76,
    "summary": "Warm"
  },
  {
    "date": "2021-05-24T10:22:17.824557+01:00",
    "temperatureC": 20,
    "temperatureF": 67,
    "summary": "Hot"
  },
  {
    "date": "2021-05-25T10:22:17.824557+01:00",
    "temperatureC": 14,
    "temperatureF": 57,
    "summary": "Bracing"
  },
  {
    "date": "2021-05-26T10:22:17.824558+01:00",
    "temperatureC": -15,
    "temperatureF": 6,
    "summary": "Sweltering"
  }
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can tell it is random, as I wouldn’t consider -15°C, “Sweltering”.&lt;/p&gt;

&lt;p&gt;The actual code that generates this looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    private static readonly string[] Summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering",
    };

    private readonly ILogger&amp;lt;WeatherForecastController&amp;gt; _logger;

    public WeatherForecastController(ILogger&amp;lt;WeatherForecastController&amp;gt; logger)
    {
        _logger = logger;
    }

    [HttpGet]
    public IEnumerable&amp;lt;WeatherForecast&amp;gt; Get()
    {
        var rng = new Random();
        return Enumerable.Range(1, 5).Select(index =&amp;gt; new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = rng.Next(-20, 55),
            Summary = Summaries[rng.Next(Summaries.Length)]
        })
        .ToArray();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is one subtle change that has to be made to be able to run integration tests against this. That is to replace the &lt;code&gt;IHostBuilder&lt;/code&gt; in &lt;code&gt;Program.cs&lt;/code&gt; with an &lt;code&gt;IWebHostBuilder&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Your &lt;code&gt;Program.cs&lt;/code&gt; should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;

namespace WireMock.Net.Api
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =&amp;gt;
            WebHost.CreateDefaultBuilder(args).UseStartup&amp;lt;Startup&amp;gt;();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Adding an API to Test
&lt;/h2&gt;

&lt;p&gt;The whole point of an integration test is to test the integration between components. So we need to add an API to our project so that we can mock it using wiremock.net.&lt;/p&gt;

&lt;p&gt;As this API is already returning random weather, we are going to add in real weather API to get back realistic data. For this, we are going to use 7timer’s weather API. This API is free and doesn’t require any authentication.&lt;/p&gt;

&lt;p&gt;The API we are going to call is this one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://www.7timer.info/bin/civillight.php?lat=51.5074&amp;amp;lon=0.1278&amp;amp;ac=0&amp;amp;unit=metric&amp;amp;output=json&amp;amp;tzshift=0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns back weather for the next 7 days for London (51.5074° N, 0.1278° W). The format of the data we get back is slightly different so we will need to format it before returning the data. This is what the first few entries in the array look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "product": "civillight",
  "init": "2021052100",
  "dataseries": [
    {
      "date": 20210521,
      "weather": "lightrain",
      "temp2m": { "max": 11, "min": 9 },
      "wind10m_max": 4
    },
    {
      "date": 20210522,
      "weather": "lightrain",
      "temp2m": { "max": 10, "min": 7 },
      "wind10m_max": 3
    },
    {
      "date": 20210523,
      "weather": "lightrain",
      "temp2m": { "max": 12, "min": 4 },
      "wind10m_max": 4
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For our API we need the following information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;dataseries[].date&lt;/code&gt; - The date which we can just convert to a DateTime.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dataseries[].temp2m&lt;/code&gt; - This is the min max temperature in Celsius so we can just use this as is. We can update our API to return both.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dataseries[].weather&lt;/code&gt; - The weather summary, again we can use as is.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I am going to use Refit for calling the API. If you haven’t used Refit it is a great tool for standardising your API calls and saves on a lot of boilerplate code for calling your API. So add the following packages to your API project:&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;PackageReference Include="Refit" Version="6.0.38"/&amp;gt;
&amp;lt;PackageReference Include="Refit.HttpClientFactory" Version="6.0.38"/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next we need to create an interface for our API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System.Threading;
using System.Threading.Tasks;

using Refit;

using WireMock.Net.Api.Client.Model;

namespace WireMock.Net.Api.Client
{
    public interface IWeatherClient
    {
        [Get("/bin/civillight.php?lat={latitude}&amp;amp;lon={longitude}&amp;amp;ac=0&amp;amp;unit=metric&amp;amp;output=json&amp;amp;tzshift=0")]
        Task&amp;lt;ApiResponse&amp;lt;WeatherResponse&amp;gt;&amp;gt; GetWeatherAsync(decimal latitude, decimal longitude, CancellationToken ct);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My WeatherResponse class looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System.Collections.Generic;

using Newtonsoft.Json;

namespace WireMock.Net.Api.Client.Model
{
    public class WeatherResponse
    {
        [JsonProperty("product")]
        public string Product { get; set; }

        [JsonProperty("init")]
        public string Init { get; set; }

        [JsonProperty("dataseries")]
        public IEnumerable&amp;lt;WeatherData&amp;gt; Dataseries { get; set; }
    }

    public class WeatherData
    {
        [JsonProperty("date")]
        public int Date { get; set; }

        [JsonProperty("weather")]
        public string Weather { get; set; }

        [JsonProperty("temp2m")]
        public Temperature Temp2m { get; set; }
    }

    public class Temperature
    {
        [JsonProperty("max")]
        public int Max { get; set; }

        [JsonProperty("min")]
        public int Min { get; set; }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are also going to set up a configuration section in our &lt;code&gt;appsettings.json&lt;/code&gt; so that the base address can be mocked later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "WeatherClient": {
    "BaseAddress": "https://www.7timer.info",
    "TimeoutSeconds": 30
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So now we need to set up the refit HttpClient. We can do this inside ConfigureServices. To make things nicer I often put the code inside an extension method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddWeatherClient(this IServiceCollection services, IConfiguration configuration)
    {
        var settings = configuration.GetSection("WeatherClient").Get&amp;lt;WeatherSettings&amp;gt;();
        services.AddRefitClient&amp;lt;IWeatherClient&amp;gt;().ConfigureHttpClient((sp, client) =&amp;gt;
        {
            client.BaseAddress = new Uri(settings.BaseAddress);
            client.Timeout = TimeSpan.FromSeconds(settings.TimeoutSeconds);
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        });
        return services;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you just need to add &lt;code&gt;AddWeatherClient(Configuration)&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;public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    services.AddWeatherClient(Configuration);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lastly, we are going to call our API from our controller.&lt;/p&gt;

&lt;p&gt;Normally I would create a service for this but keeping it simple for brevity. Our controller now looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    private readonly ILogger&amp;lt;WeatherForecastController&amp;gt; _logger;
    private readonly IWeatherClient _weatherClient;
    public WeatherForecastController(ILogger&amp;lt;WeatherForecastController&amp;gt; logger, IWeatherClient weatherClient)
    {
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        _weatherClient = weatherClient ?? throw new ArgumentNullException(nameof(weatherClient));
    }
    [HttpGet]
    public async Task&amp;lt;IActionResult&amp;gt; GetAsync(CancellationToken ct)
    {
        var weatherResponse = await _weatherClient.GetWeatherAsync(51.5074m, 0.1278m, ct);
        if (!weatherResponse.IsSuccessStatusCode || weatherResponse?.Content?.Dataseries == null)
        {
            _logger.LogError("Unexpected status code from Weather API {StatusCode}", weatherResponse.StatusCode);
            return new StatusCodeResult(StatusCodes.Status500InternalServerError);
        }
        return Ok(weatherResponse.Content.Dataseries.Select(weather =&amp;gt; new WeatherForecast
        {
             Date = DateTime.ParseExact(weather.Date.ToString(), "yyyyMMdd", CultureInfo.InvariantCulture),
             TemperatureC = new Temperature { Min = weather.Temp2m.Min, Max = weather.Temp2m.Max },
             Summary = weather.Weather
        }));
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when we run our API and call the get endpoint we will get back the results from the actual API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[
  {
    "date": "2021-05-21T00:00:00",
    "temperatureC": { "min": 9, "max": 11 },
    "temperatureF": { "min": 48, "max": 51 },
    "summary": "lightrain"
  },
  {
    "date": "2021-05-22T00:00:00",
    "temperatureC": { "min": 7, "max": 10 },
    "temperatureF": { "min": 44, "max": 49 },
    "summary": "lightrain"
  },
  {
    "date": "2021-05-23T00:00:00",
    "temperatureC": { "min": 4, "max": 12 },
    "temperatureF": { "min": 39, "max": 53 },
    "summary": "lightrain"
  },
  {
    "date": "2021-05-24T00:00:00",
    "temperatureC": { "min": 7, "max": 12 },
    "temperatureF": { "min": 44, "max": 53 },
    "summary": "rain"
  },
  {
    "date": "2021-05-25T00:00:00",
    "temperatureC": { "min": 7, "max": 12 },
    "temperatureF": { "min": 44, "max": 53 },
    "summary": "rain"
  },
  {
    "date": "2021-05-26T00:00:00",
    "temperatureC": { "min": 6, "max": 15 },
    "temperatureF": { "min": 42, "max": 58 },
    "summary": "rain"
  },
  {
    "date": "2021-05-27T00:00:00",
    "temperatureC": { "min": 6, "max": 18 },
    "temperatureF": { "min": 42, "max": 64 },
    "summary": "clear"
  }
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because we are calling the actual API we can’t test what happens with a different response from the API. This is why we need to mock out this external dependency in our integration tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating our Integration Test Project
&lt;/h2&gt;

&lt;p&gt;I prefer to have my projects laid out in the following format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/
├── Project/
│ ├── Project.csproj
│
test/
├── Project.Tests/
│ ├── Project.Tests.csproj
Project.sln
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After moving the project into a separate &lt;code&gt;src&lt;/code&gt; folder we can then use the dotnet templates to create our xunit test project inside our test project folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dotnet new xunit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lastly back in the route folder we are going to create our solution file and add our projects to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dotnet new sln
dotnet sln add src/*
dotnet sln add test/*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next we need to add a few packages to our test project:&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;PackageReference Include="WireMock.Net" Version="1.4.15"/&amp;gt;
&amp;lt;PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="5.0.6"/&amp;gt;
&amp;lt;PackageReference Include="FluentAssertions" Version="5.10.3"/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also need to add a reference to our main project as well as an &lt;code&gt;appsettings.test.json&lt;/code&gt; file and a &lt;code&gt;Resources&lt;/code&gt; folder that we will add in a moment. Your test project should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Project Sdk="Microsoft.NET.Sdk"&amp;gt;
  &amp;lt;PropertyGroup&amp;gt;
    &amp;lt;TargetFramework&amp;gt;netcoreapp5.0&amp;lt;/TargetFramework&amp;gt;
    &amp;lt;RootNamespace&amp;gt;WireMock.Net.Test&amp;lt;/RootNamespace&amp;gt;
    &amp;lt;IsPackable&amp;gt;false&amp;lt;/IsPackable&amp;gt;
  &amp;lt;/PropertyGroup&amp;gt;
  &amp;lt;ItemGroup&amp;gt;
    &amp;lt;PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0"/&amp;gt;
    &amp;lt;PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="5.0.6"/&amp;gt;
    &amp;lt;PackageReference Include="xunit" Version="2.4.0"/&amp;gt;
    &amp;lt;PackageReference Include="xunit.runner.visualstudio" Version="2.4.0"/&amp;gt;
    &amp;lt;PackageReference Include="coverlet.collector" Version="1.2.0"/&amp;gt;
    &amp;lt;PackageReference Include="WireMock.Net" Version="1.4.15"/&amp;gt;
    &amp;lt;PackageReference Include="FluentAssertions" Version="5.10.3"/&amp;gt;
  &amp;lt;/ItemGroup&amp;gt;

  &amp;lt;ItemGroup&amp;gt;
    &amp;lt;ProjectReference Include="..\..\src\WireMock.Net.Api\WireMock.Net.Api.csproj" /&amp;gt;
  &amp;lt;/ItemGroup&amp;gt;

  &amp;lt;ItemGroup&amp;gt;
    &amp;lt;None Update="appsettings.test.json"&amp;gt;
        &amp;lt;CopyToOutputDirectory&amp;gt;Always&amp;lt;/CopyToOutputDirectory&amp;gt;
    &amp;lt;/None&amp;gt;
    &amp;lt;Content Include="Resources\*.*"&amp;gt;
        &amp;lt;CopyToOutputDirectory&amp;gt;Always&amp;lt;/CopyToOutputDirectory&amp;gt;
    &amp;lt;/Content&amp;gt;
  &amp;lt;/ItemGroup&amp;gt;
&amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now go and create an &lt;code&gt;appsettings.test.json&lt;/code&gt; file and a &lt;code&gt;Resources&lt;/code&gt; folder. The Resources folder is where we are going to put the mocked responses for our API.&lt;/p&gt;

&lt;p&gt;In your &lt;code&gt;appsettings.test.json&lt;/code&gt; file put the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "WeatherClient": {
    "BaseAddress": "http://localhost:50000"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will set up our wiremock on this port in a bit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Set up the WebApplicationFactory
&lt;/h3&gt;

&lt;p&gt;To be able to replace the call to our actual API with our WireMock version we need our API to pick up our &lt;code&gt;appsettings.test.json&lt;/code&gt; file so that we can replace the &lt;code&gt;BaseAddress&lt;/code&gt;. To do this we are going to create a WebApplicationFactory and use it as a Class fixture in our tests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System.IO;

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.Configuration;

using WireMock.Net.Api;

namespace WireMock.Net.Test.Infrastructure
{
    public class ApiWebFactory&amp;lt;TStartup&amp;gt; : WebApplicationFactory&amp;lt;TStartup&amp;gt; where TStartup : class
    {
        protected override IWebHostBuilder CreateWebHostBuilder() =&amp;gt;
            WebHost.CreateDefaultBuilder()
            .ConfigureAppConfiguration((context, config) =&amp;gt;
            {
                config
                    .SetBasePath(Directory.GetCurrentDirectory())
                    .AddJsonFile("appsettings.json", true, true)
                    .AddJsonFile("appsettings.test.json", false, false);
            })
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseKestrel()
            .UseStartup&amp;lt;Startup&amp;gt;();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we are going to create a base class for our integration tests to simplify making calls to our API and reading the responses.&lt;/p&gt;

&lt;p&gt;Note: we need to add an XUnit Collection so that the tests are run sequentially, otherwise we are going to have issues with conflicting port numbers when running our test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System.Net;
using System.Net.Http;
using System.Threading.Tasks;

using Microsoft.AspNetCore.Mvc.Testing;

using Newtonsoft.Json;

using WireMock.Net.Api;

using Xunit;

namespace WireMock.Net.Test.Infrastructure
{
    [Collection("sequential")]
    public abstract class IntegrationBase : IClassFixture&amp;lt;ApiWebFactory&amp;lt;Startup&amp;gt;&amp;gt;
    {
        protected HttpClient HttpClient { get; }
        protected IntegrationBase(WebApplicationFactory&amp;lt;Startup&amp;gt; factory)
        {
            HttpClient = factory.CreateClient();
            factory.Server.AllowSynchronousIO = true;
        }

        protected HttpRequestMessage CreateGetRequest(string url)
        {
            return new HttpRequestMessage(HttpMethod.Get, url);
        }

        protected static async Task&amp;lt;T&amp;gt; ReadResponseAsync&amp;lt;T&amp;gt;(HttpResponseMessage response)
        {
            var result = await response.Content.ReadAsStringAsync();
            return JsonConvert.DeserializeObject&amp;lt;T&amp;gt;(result);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we need to create our mock API for the weather endpoint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System;
using System.IO;

using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Server;

namespace WireMock.Net.Test.Infrastructure
{
    public class WeatherFixture : IDisposable
    {
        protected readonly WireMockServer _mockApi;

        public WeatherFixture()
        {
            _mockApi = WireMockServer.Start(50000);
        }

        public void Dispose()
        {
            _mockApi.Stop();
        }

        public void Reset()
        {
            _mockApi.Reset();
        }

        public IRequestBuilder SetupGetWeather(string responseBodyResource, int statusCode = 200)
        {
            var request = Request.Create()
                .UsingGet()
                .WithPath("/bin/civillight.php*");

            var responseBody = string.IsNullOrWhiteSpace(responseBodyResource) ? new byte[0] : File.ReadAllBytes(responseBodyResource);

            _mockApi.Given(request)
                .RespondWith(
                    Response.Create()
                    .WithStatusCode(statusCode)
                    .WithHeader("content-type", "application/json")
                    .WithBody(responseBody)
                );

            return request;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this class, we are starting the WireMockServer on port 50000. This is so that we know that our mock API is going to be running on &lt;code&gt;localhost:50000&lt;/code&gt; when we run our tests.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;SetupGetWeather&lt;/code&gt; method is used to set up the expected request and response. In our case, anything that matches &lt;code&gt;/bin/civillight.php&lt;/code&gt; at the start is going to be mocked.&lt;/p&gt;

&lt;p&gt;Then have the response, which is retrieved from a file and returned in the body. Remember that &lt;code&gt;Resources&lt;/code&gt; folder we created earlier? That is where we are going to put our JSON responses.&lt;/p&gt;

&lt;p&gt;Create a file called &lt;code&gt;success.json&lt;/code&gt; in the &lt;code&gt;Resources&lt;/code&gt; folder and put the successful response from the weather API in there.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "product": "civillight",
  "init": "2021052100",
  "dataseries": [
    {
      "date": 20210521,
      "weather": "lightrain",
      "temp2m": { "max": 11, "min": 9 },
      "wind10m_max": 4
    },
    {
      "date": 20210522,
      "weather": "lightrain",
      "temp2m": { "max": 10, "min": 7 },
      "wind10m_max": 3
    },
    {
      "date": 20210523,
      "weather": "lightrain",
      "temp2m": { "max": 12, "min": 4 },
      "wind10m_max": 4
    },
    {
      "date": 20210524,
      "weather": "rain",
      "temp2m": { "max": 12, "min": 7 },
      "wind10m_max": 3
    },
    {
      "date": 20210525,
      "weather": "rain",
      "temp2m": { "max": 12, "min": 7 },
      "wind10m_max": 3
    },
    {
      "date": 20210526,
      "weather": "rain",
      "temp2m": { "max": 15, "min": 6 },
      "wind10m_max": 3
    },
    {
      "date": 20210527,
      "weather": "clear",
      "temp2m": { "max": 18, "min": 6 },
      "wind10m_max": 3
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lastly we create our Integration test and make sure we get back the expected response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;

using FluentAssertions;

using WireMock.Net.Api;
using WireMock.Net.Test.Infrastructure;

using Xunit;

namespace WireMock.Net.Test
{
    public class IntegrationTest : IntegrationBase, IDisposable
    {
        private readonly WeatherFixture _weatherFixture;
        public IntegrationTest(ApiWebFactory&amp;lt;Startup&amp;gt; factory) : base(factory)
        {
            _weatherFixture = new WeatherFixture();
        }

        public void Dispose()
        {
            _weatherFixture.Reset();
            _weatherFixture.Dispose();
        }

        [Fact]
        public async Task Given_weather_api_successful_returns_weather()
        {
            // Arrange
            _weatherFixture.SetupGetWeather("Resources/success.json");

            // Act
            var request = CreateGetRequest("/weatherforecast");
            var result = await HttpClient.SendAsync(request);

            // Assert
            result.StatusCode.Should().Be(HttpStatusCode.OK);
            var response = await ReadResponseAsync&amp;lt;IEnumerable&amp;lt;WeatherForecast&amp;gt;&amp;gt;(result);
            response.Should().HaveCount(7);

            response.Should().Contain(x =&amp;gt;
                x.Date == DateTime.Parse("2021-05-25") &amp;amp;&amp;amp;
                x.Summary == "rain" &amp;amp;&amp;amp;
                x.TemperatureC.Max == 12 &amp;amp;&amp;amp;
                x.TemperatureC.Min == 7);
        }

        [Fact]
        public async Task Given_weather_api_unsuccessful_returns_500()
        {
            // Arrange
            _weatherFixture.SetupGetWeather(null, 503);

            // Act
            var request = CreateGetRequest("/weatherforecast");
            var result = await HttpClient.SendAsync(request);

            // Assert
            result.StatusCode.Should().Be(HttpStatusCode.InternalServerError);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now run the tests using &lt;code&gt;dotnet test&lt;/code&gt; and you should see that your API is calling the mocked endpoint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;info: System.Net.Http.HttpClient.Refit.Implementation.Generated+WireMockNetApiClientIWeatherClient, WireMock.Net.Api, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null.LogicalHandler[100]
      Start processing HTTP request GET http://localhost:50000/bin/civillight.php?tzshift=0&amp;amp;output=json&amp;amp;unit=metric&amp;amp;ac=0&amp;amp;lon=0.1278&amp;amp;lat=51.5074
info: System.Net.Http.HttpClient.Refit.Implementation.Generated+WireMockNetApiClientIWeatherClient, WireMock.Net.Api, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null.ClientHandler[100]
      Sending HTTP request GET http://localhost:50000/bin/civillight.php?tzshift=0&amp;amp;output=json&amp;amp;unit=metric&amp;amp;ac=0&amp;amp;lon=0.1278&amp;amp;lat=51.5074
info: System.Net.Http.HttpClient.Refit.Implementation.Generated+WireMockNetApiClientIWeatherClient, WireMock.Net.Api, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null.ClientHandler[101]
      Received HTTP response headers after 107.6376ms - 200
info: System.Net.Http.HttpClient.Refit.Implementation.Generated+WireMockNetApiClientIWeatherClient, WireMock.Net.Api, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null.LogicalHandler[101]
      End processing HTTP request after 117.0377ms - 200
info: System.Net.Http.HttpClient.Refit.Implementation.Generated+WireMockNetApiClientIWeatherClient, WireMock.Net.Api, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null.LogicalHandler[100]
      Start processing HTTP request GET http://localhost:50000/bin/civillight.php?tzshift=0&amp;amp;output=json&amp;amp;unit=metric&amp;amp;ac=0&amp;amp;lon=0.1278&amp;amp;lat=51.5074
info: System.Net.Http.HttpClient.Refit.Implementation.Generated+WireMockNetApiClientIWeatherClient, WireMock.Net.Api, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null.ClientHandler[100]
      Sending HTTP request GET http://localhost:50000/bin/civillight.php?tzshift=0&amp;amp;output=json&amp;amp;unit=metric&amp;amp;ac=0&amp;amp;lon=0.1278&amp;amp;lat=51.5074
info: System.Net.Http.HttpClient.Refit.Implementation.Generated+WireMockNetApiClientIWeatherClient, WireMock.Net.Api, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null.ClientHandler[101]
      Received HTTP response headers after 13.8969ms - 503
info: System.Net.Http.HttpClient.Refit.Implementation.Generated+WireMockNetApiClientIWeatherClient, WireMock.Net.Api, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null.LogicalHandler[101]
      End processing HTTP request after 14.0039ms - 503
fail: WireMock.Net.Api.Controllers.WeatherForecastController[0]
      Unexpected status code from Weather API ServiceUnavailable

Passed! - Failed: 0, Passed: 2, Skipped: 0, Total: 2, Duration: 832 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final Comments
&lt;/h2&gt;

&lt;p&gt;As I showed in my last post you can easily mock an API using WireMock in a docker container. However, if you want to be able to test different responses using the same endpoint then, you need to set up Wiremock.net directly in your integration tests.&lt;/p&gt;

&lt;p&gt;You can find the complete code for this tutorial on my &lt;a href="https://github.com/alexhyett/wiremock-net-example"&gt;GitHub page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have any questions let me know in the comments below.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>testing</category>
    </item>
    <item>
      <title>Mocking API calls using WireMock</title>
      <dc:creator>Alex Hyett</dc:creator>
      <pubDate>Fri, 14 May 2021 17:00:00 +0000</pubDate>
      <link>https://dev.to/alexhyettdev/mocking-api-calls-using-wiremock-kef</link>
      <guid>https://dev.to/alexhyettdev/mocking-api-calls-using-wiremock-kef</guid>
      <description>&lt;p&gt;It is rare in software development that you are building something in complete isolation from everything else. Generally, you are going to be making calls to other systems or components.&lt;/p&gt;

&lt;p&gt;If you are lucky you are building against an existing API that you can test against. If you are working with other teams to similar deadlines, you generally need to agree on a contract beforehand and build against it. In this case, you are going to need to mock the API to be able to test what you are building.&lt;/p&gt;

&lt;p&gt;This is where Wiremock comes in. Other tools that can be used for mocking APIs but Wiremock is the one I have the most experience with.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running Wiremock in a Docker container
&lt;/h2&gt;

&lt;p&gt;If you need to run your component locally against a mocked source then running wiremock standalone from a docker container is the best way to do that. It works similarly to the way you mock AWS by running localstack. You can also use Wiremock to mock AWS services but I will save that for another post!&lt;/p&gt;

&lt;p&gt;To save us some time we are going to use this popular (30 million pulls) docker image, &lt;a href="https://github.com/rodolpheche/wiremock-docker"&gt;rodolpheche/wiremock-docker&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want a one-liner to get this up and running you can use this:&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 -it --rm -p 8080:8080 rodolpheche/wiremock
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When wiremock starts you should see something like this on the console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; /$$ /$$ /$$ /$$ /$$ /$$
| $$ /$ | $$|__/ | $$$ /$$$ | $$
| $$ /$$$| $$ /$$ /$$$$$$ /$$$$$$ | $$$$ /$$$$ /$$$$$$ /$$$$$$$| $$ /$$
| $$/$$ $$ $$| $$ /$$ __$$ /$$__ $$| $$ $$/$$ $$ /$$ __$$ /$$_____ /| $$ /$$/
| $$$$_ $$$$| $$| $$ \__/| $$$$$$$$| $$ $$$| $$| $$ \ $$| $$ | $$$$$$/
| $$$/ \ $$$| $$| $$ | $$ _____ /| $$\ $ | $$| $$ | $$| $$ | $$_ $$
| $$/ \ $$| $$| $$ | $$$$$$$| $$ \/ | $$| $$$$$$/| $$$$$$$| $$ \ $$
| __/ \__ /| __/|__ / \ _______/|__ / | __/ \______ / \ _______/|__ / \__/

port: 8080
enable-browser-proxying: false
disable-banner: false
no-request-journal: false
verbose: false

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

&lt;/div&gt;



&lt;p&gt;Got to love the ASCII art!&lt;/p&gt;

&lt;p&gt;You should be able to get a list of mappings (which will be empty to start with) using &lt;code&gt;http://localhost:8080/__admin/mappings&lt;/code&gt; which will give us this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "mappings": [],
  "meta": {
    "total": 0
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Adding mappings using the API
&lt;/h4&gt;

&lt;p&gt;You can use the wiremock API to add mappings, this is particularly useful if you want to test out examples on the go.&lt;/p&gt;

&lt;h5&gt;
  
  
  GET Request
&lt;/h5&gt;

&lt;p&gt;Using postman you can import the following &lt;code&gt;curl&lt;/code&gt; statement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl --location --request POST 'http://localhost:8080/__admin/mappings/new' \
--header 'Content-Type: application/json' \
--data-raw '{
    "request": {
        "method": "GET",
        "url": "/api/test"
    },
    "response": {
        "status": 200,
        "body": "{ \"test\": \"data\" }",
        "headers": {
            "Content-Type": "application/json"
        }
    }
}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running this should give you a 201 created response.&lt;/p&gt;

&lt;p&gt;Now when we call &lt;code&gt;http://localhost:8080/api/test&lt;/code&gt; we should get back the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "test": "data"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All you need to do is change the request and response and you can mock any API call.&lt;/p&gt;

&lt;h5&gt;
  
  
  POST Request
&lt;/h5&gt;

&lt;p&gt;Mocking GET request by URL is the simplest option but if you are doing POST requests you are probably going to want to mock based on the request body.&lt;/p&gt;

&lt;p&gt;For example, lets mock the POST endpoint that maps based on the body content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl --location --request POST 'http://localhost:8080/__admin/mappings/new' \
--header 'Content-Type: application/json' \
--data-raw '{
    "request": {
        "method": "POST",
        "url": "/api/test",
        "bodyPatterns" : [{
          "matchesJsonPath" : {
             "expression": "$",
             "equalToJson": "{ \"test\": \"banana\" }"
          }
        }]
    },
    "response": {
        "status": 201,
        "body": "{ \"test\": \"banana created\" }",
        "headers": {
            "Content-Type": "application/json"
        }
    }
}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just to prove it works we are now going to map another one based on a different body (fruit).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl --location --request POST 'http://localhost:8080/__admin/mappings/new' \
--header 'Content-Type: application/json' \
--data-raw '{
    "request": {
        "method": "POST",
        "url": "/api/test",
        "bodyPatterns" : [{
          "matchesJsonPath" : {
             "expression": "$",
             "equalToJson": "{ \"test\": \"apple\" }"
          }
        }]
    },
    "response": {
        "status": 201,
        "body": "{ \"test\": \"apple created\" }",
        "headers": {
            "Content-Type": "application/json"
        }
    }
}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can test it out by running the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl --location --request POST 'http://localhost:8080/api/test' \
--header 'Content-Type: application/json' \
--data-raw '{
    "test": "banana"
}'

curl --location --request POST 'http://localhost:8080/api/test' \
--header 'Content-Type: application/json' \
--data-raw '{
    "test": "apple"
}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is just a simple example, if you need something more advanced, check out the &lt;a href="http://wiremock.org/docs/request-matching/"&gt;request matching documentation&lt;/a&gt; for more examples.&lt;/p&gt;

&lt;h4&gt;
  
  
  Resetting Mappings
&lt;/h4&gt;

&lt;p&gt;If you want to reset all your mappings at any point then you can do so by sending a POST request to &lt;code&gt;http://localhost:8080/_admin/reset&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;curl --location --request POST 'http://localhost:8080/__admin/reset'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Adding mappings using files
&lt;/h4&gt;

&lt;p&gt;The other way to add your mock mappings is by using files that are loaded when the container is started. This is particularly useful if you want other developers to be able to run your code.&lt;/p&gt;

&lt;p&gt;I prefer to run all my docker containers using &lt;code&gt;docker-compose&lt;/code&gt;. That way all people need to do is run &lt;code&gt;docker-compose up&lt;/code&gt; and everything will be set up for them.&lt;/p&gt;

&lt;p&gt;Your &lt;code&gt;docker-compose.yml&lt;/code&gt; file should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '3.4'

services:
  wiremock:
    image: rodolpheche/wiremock
    container_name: wiremock
    volumes:
      - ./ __files:/home/wiremock/__ files
      - ./mappings:/home/wiremock/mappings
    ports:
      - 8080:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you run &lt;code&gt;docker-compose up&lt;/code&gt; it will create a &lt;code&gt;__files&lt;/code&gt; and a &lt;code&gt;mappings&lt;/code&gt; folder in your current directory that are mounted to the same folders in your container.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;mappings&lt;/code&gt; folder is where you save your json mappings. Each file is exactly the same as the body of the request that we were sending earlier.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "request": {
    "url": "/api/test",
    "method": "GET"
  },
  "response": {
    "status": 200,
    "body": "{ \"test\": \"data\" }",
    "headers": {
      "Content-Type": "application/json"
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want you can even just save the mappings we created by API earlier by sending a POST request to &lt;code&gt;http://localhost:8080/__admin/mappings/save&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;curl --location --request POST 'http://localhost:8080/__admin/mappings/save'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you update any of the files that are saved in the &lt;code&gt;mappings&lt;/code&gt; folder then you will need to run a reset before they take effect.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl --location --request POST 'http://localhost:8080/__admin/reset'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;__files&lt;/code&gt; folder can be used to serve static files. For example if we put a simple &lt;code&gt;index.html&lt;/code&gt; file in this folder like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;Mocked File&amp;lt;/h1&amp;gt;
    &amp;lt;p&amp;gt;Something interesting here&amp;lt;/p&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then go to &lt;code&gt;http://localhost:8080&lt;/code&gt; you should see this:&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/f9a07b52591c1bfa2c7e453e7d76b075/e8e04/mocked-website.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hTB9v8_5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.alexhyett.com/static/f9a07b52591c1bfa2c7e453e7d76b075/c0255/mocked-website.png" alt="Mocked Website" title="Mocked Website"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Using mocks can be a great way to simulate the behaviour of external APIs. Wiremock is one of the simplest ways to get started. That being said, I have found that wiremock isn’t the fastest way to mock an API. I recently had to mock an FX rate endpoint with a set of static FX rates. It was much faster to do this with a simple API written in Node.js.&lt;/p&gt;

&lt;p&gt;There is another version of Wiremock that I use regularly called &lt;a href="https://github.com/WireMock-Net/WireMock.Net"&gt;Wiremock.Net&lt;/a&gt; which is particularly useful when writing .NET integration tests. I will cover that in my next post as it is a bit more involved than this one.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Using ngrok to test local websites and APIs</title>
      <dc:creator>Alex Hyett</dc:creator>
      <pubDate>Wed, 07 Apr 2021 12:00:00 +0000</pubDate>
      <link>https://dev.to/alexhyettdev/using-ngrok-to-test-local-websites-and-apis-30d6</link>
      <guid>https://dev.to/alexhyettdev/using-ngrok-to-test-local-websites-and-apis-30d6</guid>
      <description>&lt;p&gt;Often when I am creating a new website, I want to see how it is going to look on an actual device like my phone or tablet. You can use Chrome Web Tools for mimicing a device but it isn’t the same as an actual phone.&lt;/p&gt;

&lt;p&gt;Chances are however that if you try and access the service using your IP Address on another device it will likely be blocked by your operating systems firewall. If you want to share the website or API with someone not on your network then you need to jump through lots of hoops such as forwarding the port on your router.&lt;/p&gt;

&lt;p&gt;This is where ngrok comes in. It allows you to expose your local service or website over a secure tunnel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing Ngrok
&lt;/h2&gt;

&lt;p&gt;Ngrok couldn’t be simpler to install.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Head over to &lt;a href="https://dashboard.ngrok.com/signup"&gt;ngrok.com and sign up&lt;/a&gt; for an account (it’s free).&lt;/li&gt;
&lt;li&gt;Download ngrok for your operating system. They support Windows, Mac (Intel and Arm), Linux (Intel and Arm) and FreeBSD.&lt;/li&gt;
&lt;li&gt;Unzip the downloaded zip file&lt;/li&gt;
&lt;li&gt;Connect ngrok to your account &lt;code&gt;./ngrok authtoken &amp;lt;auth token here&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Using Ngrok
&lt;/h2&gt;

&lt;p&gt;Once Ngrok is installed it is simple to use.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First start up your application so that it is running on your local machine. If I run this Gatsby website locally it runs on port 8000.&lt;/li&gt;
&lt;li&gt;Start a tunnel to your local port (8000 in this case) &lt;code&gt;./ngrok http 8000&lt;/code&gt;. You should see an output like this:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="///static/6d97d5b528ef80ab4d370729d86ac0b7/5b2ff/ngrok-running.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--za8bZPbg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.alexhyett.com/static/6d97d5b528ef80ab4d370729d86ac0b7/c0255/ngrok-running.png" alt="Ngrok Window" title="Ngrok Window"&gt;&lt;/a&gt;Ngrok gives you the http and https address that you can connect to and you will see the incoming HTTP requests on the console.&lt;/p&gt;

&lt;p&gt;From their you can access the service running locally on your phone, tablet or even give it to someone else to try out.&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/f7b4ba3978dad28cd28605bcd3f2098e/4d836/ngrok-on-iphone.jpg"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--C7uPFrCE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.alexhyett.com/static/f7b4ba3978dad28cd28605bcd3f2098e/4d836/ngrok-on-iphone.jpg" alt="ngrok on iPhone" title="ngrok on iPhone"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also access a Dashboard locally on port &lt;code&gt;4040&lt;/code&gt; if you want to get a detailed look of the requests and responses going to your service.&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/bea627e346f4c59841ec9c457fefd4ac/28b65/ngrok-dashboard.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--d0kc55Ii--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.alexhyett.com/static/bea627e346f4c59841ec9c457fefd4ac/c0255/ngrok-dashboard.png" alt="ngrok dashboard" title="ngrok dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ngrok is free for single use quick demos, if you need something more such as custom domains then you will need the paid account which starts at $5 per month.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Ngrok is one of those services that every developer should have in their toolbox. The dashboard is especially useful if you want to monitor the requests going to your service without another tool like fiddler. The free plan is good enough for most use cases, if you need more than the paid options are reasonable.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Using GitHub Actions to Deploy to S3</title>
      <dc:creator>Alex Hyett</dc:creator>
      <pubDate>Fri, 26 Mar 2021 12:00:00 +0000</pubDate>
      <link>https://dev.to/alexhyettdev/using-github-actions-to-deploy-to-s3-1925</link>
      <guid>https://dev.to/alexhyettdev/using-github-actions-to-deploy-to-s3-1925</guid>
      <description>&lt;p&gt;Recently I went through the process of setting up &lt;a href="https://dev.to/drone-ci-raspberry-pi/"&gt;Drone CI on my Raspberry Pi&lt;/a&gt;. The plan was to use my Raspberry Pi as a build server for this website as well as other projects.&lt;/p&gt;

&lt;p&gt;However, the &lt;a href="https://github.com/lovell/sharp"&gt;Sharp&lt;/a&gt; image library that Gatsby uses to resize images doesn’t natively work on ARM processors. I did try to compile it in the docker image but I couldn’t get the correct version to be picked up when running the builds. On top of that the builds were incredibly slow on my already overworked Raspberry Pi 2B+.&lt;/p&gt;

&lt;p&gt;So I started looking for x86 based single board computers (SBC) that would be able to run them. However, most of them aren’t that great and start at around £100, a bit more than a Raspberry Pi (the &lt;a href="https://www.lattepanda.com/products"&gt;LattePanda&lt;/a&gt; looked quite good if you are interested).&lt;/p&gt;

&lt;p&gt;At that price point, I might as well just run Drone CI on AWS in a micro EC2 (t2.micro £73 a year or free for first year).&lt;/p&gt;

&lt;p&gt;Then I remembered, we have been using GitHub Actions at work and they are surprisingly good and cost effective. Really I am a bit late to the party with this!&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost of GitHub Actions
&lt;/h2&gt;

&lt;p&gt;If you have a public repository then you can run GitHub actions for free. Yes free!&lt;/p&gt;

&lt;p&gt;Most of my code however is not public and for private repositories GitHub give you 2,000 minutes per month free (3,000 on a pro account). These builds also run with a generous 2 cores and 7 GB of RAM, a lot more than a t2.micro!&lt;/p&gt;

&lt;p&gt;It takes around 2 - 3 minutes to build and deploy my blog to S3, so I am not likely to run out minutes each month with the frequency I post.&lt;/p&gt;

&lt;p&gt;This is assuming your code runs on Linux which mine does. If you need to run on Windows then you only get 1,000 minutes a month. If for some reason you need your build to run on MacOS then that drops to 200 minutes.&lt;/p&gt;

&lt;p&gt;Essentially at 2,000 minutes and $0.008 per minute on Linux, GitHub are giving us $16 dollars of runtime for free per month. As Windows and MacOS are more expensive to run you will use up your free credits quicker.&lt;/p&gt;

&lt;h2&gt;
  
  
  What can you do with GitHub Actions
&lt;/h2&gt;

&lt;p&gt;With GitHub Actions you can pretty much do anything you would normally do on a build server.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Running builds and tests&lt;/li&gt;
&lt;li&gt;Starting up docker containers&lt;/li&gt;
&lt;li&gt;Run integration tests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On top of that it comes with tons of community made plugins as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying a Gatsby website to S3
&lt;/h2&gt;

&lt;p&gt;So this blog is hosted on Amazon S3 with Cloudfront. At the moment, this website costs me around £0.50 a month to run, which I can’t complain about (I have shared my &lt;a href="https://dev.to/terraform-s3-static-website-hosting/"&gt;terraform build in a previous post&lt;/a&gt; if you are interested).&lt;/p&gt;

&lt;p&gt;However, until now I have been pushing up changes to my S3 bucket locally from my machine (yuck).&lt;/p&gt;

&lt;p&gt;My current build process consists of a few commands that are set up in my package.json file.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;yarn lint&lt;/code&gt; which runs &lt;code&gt;./node_modules/.bin/eslint --ext .js,.jsx --ignore-pattern public .&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;yarn build&lt;/code&gt; runs &lt;code&gt;gatbsy build&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;yarn deploy&lt;/code&gt; runs the following command to upload to S3 and invalidate my Cloudfront cache.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gatsby-plugin-s3 deploy --yes; export AWS_PAGER=""; aws cloudfront create-invalidation --distribution-id E5FDMTLPHUTLTL --paths '/*';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above is assuming I have also run &lt;code&gt;yarn install&lt;/code&gt; at some point in the past and I have credentials set up on my machine to allow me to upload to Amazon S3.&lt;/p&gt;

&lt;h3&gt;
  
  
  GitHub Workflow File
&lt;/h3&gt;

&lt;p&gt;To be able to use GitHub actions you need to create a workflow file under &lt;code&gt;.github/workflows&lt;/code&gt; in your project.&lt;/p&gt;

&lt;p&gt;This is what my &lt;code&gt;workflow.yml&lt;/code&gt; file looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name: Deploy Blog

on:
  push:
    branches:
      - main
jobs:
  build:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: 12
      - name: Caching Gatsby
        id: gatsby-cache-build
        uses: actions/cache@v2
        with:
          path: |
            public
            .cache
            node_modules
          key: ${{ runner.os }}-gatsby-alexhyett-site-build-${{ github.run_id }}
          restore-keys: |
            ${{ runner.os }}-gatsby-alexhyett-site-build-
      - name: Install dependencies
        run: yarn install
      - name: Run Lint
        run: yarn run lint
      - name: Build
        run: yarn run build
      - name: Set 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: eu-west-1
      - name: Deploy to S3
        run: yarn run deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I am going to go through each of these steps so you know what they do.&lt;/p&gt;

&lt;p&gt;This is my scripts section in my &lt;code&gt;package.json&lt;/code&gt; file. I reference these in my workflow to keep things consistant.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"scripts": {
    "lint": "./node_modules/.bin/eslint --ext .js,.jsx --ignore-pattern public .",
    "develop": "gatsby develop",
    "build": "gatsby build",
    "clean": "gatsby clean",
    "serve": "gatsby serve",
    "deploy": "gatsby-plugin-s3 deploy --yes; export AWS_PAGER=\"\"; aws cloudfront create-invalidation --distribution-id E5FDMTLPHUTLTL --paths '/*';"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Define Build Environment and Timeout
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;build:
  runs-on: ubuntu-latest
  timeout-minutes: 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we specify what operating system our workflow is going to run on. Here I am running on linux so I pick the ubuntu runner. You also have the option of Windows or MacOS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;windows-latest&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;macos-latest&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find a list of runners in the &lt;a href="https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners"&gt;GitHub Docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I have also specified a timeout of 10 minutes. It is good practise to set a timeout so that a malfunctioning workflow doesn’t eat up all your free minutes.&lt;/p&gt;

&lt;h4&gt;
  
  
  Define when to run this workflow
&lt;/h4&gt;



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

&lt;/div&gt;



&lt;p&gt;As it is only me contributing to this blog, I just have the workflow set to run when something is committed to the main branch. So if I merge a PR or commit straight to main it will kick off a build and deploy my blog.&lt;/p&gt;

&lt;h4&gt;
  
  
  Checkout Code
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- uses: actions/checkout@v2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will definitely need this step, as it checks out you code into the runner.&lt;/p&gt;

&lt;h4&gt;
  
  
  Setup Node
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- uses: actions/setup-node@v2
  with:
    node-version: 12
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As Gatsby relies on Node we need to set it up. I am using version 12 so I set that up here in the script.&lt;/p&gt;

&lt;h4&gt;
  
  
  Cache Folders
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- name: Caching Gatsby
  id: gatsby-cache-build
  uses: actions/cache@v2
  with:
    path: |
      public
      .cache
      node_modules
    key: ${{ runner.os }}-gatsby-alexhyett-site-build-${{ github.run_id }}
    restore-keys: |
      ${{ runner.os }}-gatsby-alexhyett-site-build-
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you may know most React applications end up with a huge number of packages in the &lt;code&gt;node_modules&lt;/code&gt; folder. If you are starting from a fresh project, a considerable amount of time is taken up downloading these packages. This will impact your build times and therefore your free minutes on GitHub.&lt;/p&gt;

&lt;p&gt;For Gatsby we also want to cache the &lt;code&gt;.cache&lt;/code&gt; folder and &lt;code&gt;public&lt;/code&gt; folder so we don’t have to rebuild these each time.&lt;/p&gt;

&lt;p&gt;GitHub action caches work by specifying a key which we use to find the cache. In this case, my key will look like this:&lt;code&gt;Linux-gatsby-alexhyett-site-build-675994106&lt;/code&gt;. As I have specified &lt;code&gt;restore-keys&lt;/code&gt; it will also match any keys starting with &lt;code&gt;Linux-gatsby-alexhyett-site-build-&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If I chose to build on Windows instead then it would create a new cache key. There are various methods you can use to create a cache key which you can find more about on the &lt;a href="https://github.com/actions/cache"&gt;repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Caches are kept while they are in use but if not used in 7 days they are removed.&lt;/p&gt;

&lt;h4&gt;
  
  
  Install Yarn Packages
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- name: Install dependencies
  run: yarn install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This installs all of the npm packages require by my website. As the &lt;code&gt;node_modules&lt;/code&gt; folder is cached this will be quicker after the first run.&lt;/p&gt;

&lt;h4&gt;
  
  
  Run Lint
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- name: Run Lint
  run: yarn run lint
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check for any lint errors in my code before continuing. Lint is set up as script in my &lt;code&gt;package.json&lt;/code&gt; file and is the equivalent to &lt;code&gt;./node_modules/.bin/eslint --ext .js,.jsx --ignore-pattern public .&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Run Build
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- name: Build
  run: yarn run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another script in my package.json equivalent to &lt;code&gt;gatsby build&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Set to AWS Credentials
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- name: Set 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: eu-west-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To be able to push to AWS and invalidate the Cloudfront cache we need to provide access keys and specify the region. Access keys are added using GitHub secrets in your project. I will cover how to set these up in a bit.&lt;/p&gt;

&lt;h4&gt;
  
  
  Upload to S3 and Invalidate Cache
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- name: Deploy to S3
  run: yarn run deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally we need to push the built files in the public directory up to S3 and invalidate the Cloudfront cache. I do this with another script set up in &lt;code&gt;package.json&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Which is the equivalent to running this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gatsby-plugin-s3 deploy --yes; export AWS_PAGER=""; aws cloudfront create-invalidation --distribution-id E5FDMTLPHUTLTL --paths '/*';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In case you are wondering, the &lt;code&gt;export AWS_PAGER="";&lt;/code&gt; command is so that the AWS CLI doesn’t prompt you to press enter after the invalidation has been done. This was a breaking change that the AWS team introduced recently.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting GitHub Secrets
&lt;/h3&gt;

&lt;p&gt;The workflow script specifies a couple of secrets, &lt;code&gt;${{ secrets.AWS_ACCESS_KEY_ID }}&lt;/code&gt; and &lt;code&gt;${{ secrets.AWS_SECRET_ACCESS_KEY }}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Ideally, you should create a user with just enough permissions to be able to perform what we need and no more.&lt;/p&gt;

&lt;p&gt;To do this we need to create a new policy by logging on to AWS Console and going to IAM &amp;gt; Policies.&lt;/p&gt;

&lt;p&gt;Then we need to create a policy with the following JSON, replacing &lt;code&gt;yourdomain.com&lt;/code&gt; with the name of your S3 bucket for your website.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AccessToGetBucketLocation",
      "Effect": "Allow",
      "Action": ["s3:GetBucketLocation"],
      "Resource": ["arn:aws:s3:::*"]
    },
    {
      "Sid": "AccessToWebsiteBuckets",
      "Effect": "Allow",
      "Action": [
        "s3:PutBucketWebsite",
        "s3:PutObject",
        "s3:PutObjectAcl",
        "s3:GetObject",
        "s3:ListBucket",
        "s3:DeleteObject"
      ],
      "Resource": [
        "arn:aws:s3:::yourdomain.com",
        "arn:aws:s3:::yourdomain.com/*"
      ]
    },
    {
      "Sid": "AccessToCloudfront",
      "Effect": "Allow",
      "Action": ["cloudfront:GetInvalidation", "cloudfront:CreateInvalidation"],
      "Resource": "*"
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the policy is created you need to create a new user under IAM &amp;gt; Users. Give it a name such as &lt;code&gt;githubuser&lt;/code&gt; and select programmatic access:&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/2293436f80e4e0025e41260ec21ce39b/3126c/aws-user-account.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w3zjblDP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.alexhyett.com/static/2293436f80e4e0025e41260ec21ce39b/c0255/aws-user-account.png" alt="Set user details"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next select “Attach existing policies directly” and pick the policy we created in the last step.&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/0686c1370badc0c8b06c850db3f5dbc5/a2ef2/aws-attach-policy.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TdhserMa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.alexhyett.com/static/0686c1370badc0c8b06c850db3f5dbc5/c0255/aws-attach-policy.png" alt="Set permissions"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the last page you will be given your Access Key Id and Secret. You will only be shown this secret once so make sure you copy it down.&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/a14b76ea71a37f34042ebb99f220bfcf/ca98b/access-key.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qx3F3M5L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.alexhyett.com/static/a14b76ea71a37f34042ebb99f220bfcf/c0255/access-key.png" alt="Access Key"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lastly we need to add these secrets to GitHub. On your repository go to Settings &amp;gt; Secrets from their you need to add “New repository secret” and add in &lt;code&gt;AWS_ACCESS_KEY_ID&lt;/code&gt; and &lt;code&gt;AWS_SECRET_ACCESS_KEY&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/7c2248bceac6a14516b390a7c09084e8/71ba4/github-secrets.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--q04mpahs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.alexhyett.com/static/7c2248bceac6a14516b390a7c09084e8/c0255/github-secrets.png" alt="GitHub Secrets"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now the next time you commit changes to your &lt;code&gt;main&lt;/code&gt; branch it will kick off the build:&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/1446ec0583aa9e6a10c10b6e0f9919a0/21335/gihub-build.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--plv-OG91--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.alexhyett.com/static/1446ec0583aa9e6a10c10b6e0f9919a0/c0255/gihub-build.png" alt="GitHub Build"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There you have it. Much simpler than setting up your own CI tool either locally or in the cloud.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conditional Compilation
&lt;/h2&gt;

&lt;p&gt;This is a fairly simple example, although you can do a lot more with GitHub actions if you use docker or some of the &lt;a href="https://github.com/marketplace"&gt;action plugins&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If I wasn’t the only one contributing to this blog, I would also set up some of these steps to run on pull requests as well.&lt;/p&gt;

&lt;p&gt;To do that I would set the &lt;code&gt;on&lt;/code&gt; statement to run:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;And then add an if statement to the AWS and Deploy Steps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if: { { github.ref == 'ref/head/main' } }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;If you don’t have your own build server using GitHub Actions can be a great alternative. For public projects it is free and for private personal projects the 2,000 minutes is likely to be more than enough.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>tutorial</category>
      <category>aws</category>
      <category>github</category>
    </item>
    <item>
      <title>Getting Started with AWS Step Functions</title>
      <dc:creator>Alex Hyett</dc:creator>
      <pubDate>Fri, 12 Mar 2021 12:40:00 +0000</pubDate>
      <link>https://dev.to/alexhyettdev/getting-started-with-aws-step-functions-bj6</link>
      <guid>https://dev.to/alexhyettdev/getting-started-with-aws-step-functions-bj6</guid>
      <description>&lt;p&gt;I have recently been looking into AWS Step Functions. For those not familiar with them, Step Functions are Amazon’s way of providing a state machine as a service.&lt;/p&gt;

&lt;p&gt;If you have used AWS you have probably used Lambda functions. Lambda functions allow you to easily put a bit of functionality into a stateless scalable component.&lt;/p&gt;

&lt;p&gt;Of course, the issue with this is generally most business applications require some form of state to be useful. Business processes generally follow a workflow where it is only possible to complete one task once another has been completed.&lt;/p&gt;

&lt;p&gt;AWS Step Functions allows you to easily set up scalable workflows for your serverless components.&lt;/p&gt;

&lt;h2&gt;
  
  
  Features of Step Functions
&lt;/h2&gt;

&lt;p&gt;The main reason for choosing Step Functions rather than implementing your own is all the functionality you get out of the box. I have implemented workflows before and they can get complicated quickly.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Error Handling - AWS has error handling built-in and you can easily specify various catch blocks and tell what action you want to perform if something fails.&lt;/li&gt;
&lt;li&gt;Retry Handling - Step Functions support retries as well to cope with the inevitable transient error.&lt;/li&gt;
&lt;li&gt;Multiple ways to Trigger workflow step - there are a few ways you can kick off a workflow such as via API or a CloudWatch event. I will cover them in more detail later.&lt;/li&gt;
&lt;li&gt;Workflow Progress Visualisation - The AWS Console will show you a visual representation of your workflow as well as showing the progress and whether a step has failed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Main Benefits of Step Functions
&lt;/h2&gt;

&lt;p&gt;Using AWS State Functions has many benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Handles all error handling and retry logic - as mentioned in the features, all this work is already built-in.&lt;/li&gt;
&lt;li&gt;Can handle complex workflows with hundreds of steps - Step Functions can cope with hundreds if not thousands of steps.&lt;/li&gt;
&lt;li&gt;Copes with manual steps - many business processes require a manual review at some point.&lt;/li&gt;
&lt;li&gt;Handles long-lived workflows up to 1 year - workflows using the standard type can last for as long as a year. So you don’t have to worry about your workflow timing out if there is a manual approval step.&lt;/li&gt;
&lt;li&gt;Manage state between executions of stateless functions&lt;/li&gt;
&lt;li&gt;Decouple the application work from business logic&lt;/li&gt;
&lt;li&gt;Scales well and can perform parallel executions&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Drawbacks &amp;amp; Limitations of Step Function
&lt;/h2&gt;

&lt;p&gt;Of course, no technology is perfect and there are a few limitations you need to be aware of.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Decoupling all the workflow logic might make your application difficult to understand. Once you start linking together numerous individual Lambda components you need to makes sure that the workflow is well understood and documented.&lt;/li&gt;
&lt;li&gt;Need to learn the AWS States Language which is based on JSON.&lt;/li&gt;
&lt;li&gt;Locked into an AWS specific technology - Step Functions are unique to AWS and therefore if you plan on moving to a different cloud provider you will need to come up with a different solution.&lt;/li&gt;
&lt;li&gt;Execution History limited to 90 days - if you need longer execution history you will need to rely on the CloudWatch logs.&lt;/li&gt;
&lt;li&gt;Currently, only a few ways to trigger workflow transitions - not all the AWS services support triggering a workflow. The notable ones that are missing are from DynamoDB and Kinesis.&lt;/li&gt;
&lt;li&gt;Data sent between workflow steps are not encrypted - if you want to pass sensitive data between steps you need to store it encrypted in either DynamoDB or S3 and provide a pointer in your step output.&lt;/li&gt;
&lt;li&gt;Workflows can last up to 1 year on Standard and 5 mins on Express.&lt;/li&gt;
&lt;li&gt;Execution History has a 25,000 hard limit - if you think the number of executions (including retries) is likely to go above this you will need to split up your workflow into multiple workflows. Luckily you can kick off other step functions from a step function.&lt;/li&gt;
&lt;li&gt;Payload size limited to 32Kb - if you want to store above this amount you will need to use some other storage such as S3 or DynamoDB.&lt;/li&gt;
&lt;li&gt;Only works natively with a handful of AWS services - if you need other applications to be triggered this will need to be done with a lambda wrapper.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  AWS Step Function Workflow Types
&lt;/h2&gt;

&lt;p&gt;When you are setting up a step function you will be given 2 options for the type of workflow you want, Standard or Express.&lt;/p&gt;

&lt;h3&gt;
  
  
  Standard
&lt;/h3&gt;

&lt;p&gt;This is the default and is best if you have any long-running or manual steps in your workflow. This is best for low volume systems (requests measured per minute rather than per second).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Workflows can run for up to one year&lt;/li&gt;
&lt;li&gt;2,000 executions per second&lt;/li&gt;
&lt;li&gt;4,000 transitions per second per AWS account&lt;/li&gt;
&lt;li&gt;Priced per state transition&lt;/li&gt;
&lt;li&gt;Supports Activities &amp;amp; Callbacks - these are for long-running services that you don’t want in a lambda.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Express
&lt;/h3&gt;

&lt;p&gt;If you have a high throughput system then you need the express workflow type. The express type is priced differently and does not support long-running processes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Workflows can run for up to 5 minutes&lt;/li&gt;
&lt;li&gt;100,000 executions per second&lt;/li&gt;
&lt;li&gt;Nearly unlimited state transitions&lt;/li&gt;
&lt;li&gt;Priced on the number of executions, duration and memory consumption&lt;/li&gt;
&lt;li&gt;Doesn’t support Activities or Callbacks&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  AWS Step Function Pricing
&lt;/h2&gt;

&lt;p&gt;The cost of using step functions will depend on whether you have chosen Standard or Express as your workflow type.&lt;/p&gt;

&lt;h3&gt;
  
  
  Standard Pricing
&lt;/h3&gt;

&lt;p&gt;Standard workflows are priced at $0.025 per 1,000 state transitions. So a workflow with 10 states, executed 100 times will cost you $0.025.&lt;/p&gt;

&lt;p&gt;If you have a high throughput system that executes your workflow 1 million times a day. That is 10 million state transitions so would cost $250 a day (or $91,250 a year!).&lt;/p&gt;

&lt;h3&gt;
  
  
  Express Pricing
&lt;/h3&gt;

&lt;p&gt;Express workflows have 2 levels of pricing. They are charged at $1.00 per 1 million workflow executions ($0.000001 per request) and you are also charged for the workflow duration.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;$0.00001667 per GB-Second ($0.0600 per GB-hour) for the first 1,000 hours GB-hours&lt;/li&gt;
&lt;li&gt;$0.00000833 per GB-Second ($0.0300 per GB hour) for the next 4,000 hours GB-hours&lt;/li&gt;
&lt;li&gt;$0.00000456 per GB-Second ($0.01642 per GB-hour) beyond that&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Calculating pricing for express workflows is a bit harder but depending on your throughput it could save you a lot of money.&lt;/p&gt;

&lt;p&gt;AWS give a few &lt;a href="https://aws.amazon.com/step-functions/pricing/"&gt;pricing examples&lt;/a&gt; on their website which might help you work out the pricing for your application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview of Step Functions
&lt;/h2&gt;

&lt;p&gt;Now you have seen some of the benefits of step functions, let’s have a look at how we set one up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Triggering a Step Function
&lt;/h3&gt;

&lt;p&gt;The whole point of having step functions is being able to automate a manual workflow. So naturally, you aren’t going to want to trigger these workflows manually via the AWS Console.&lt;/p&gt;

&lt;p&gt;Luckily, AWS comes with several ways that you can trigger Step Functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API Gateway - using StartExecution from an API Gateway and pass in the payload from a POST call to your step function.&lt;/li&gt;
&lt;li&gt;Cloudwatch/EventBridge Events - using EventBridge or Cloudwatch you can trigger step functions when another event occurs. Such as a file being uploaded to S3.&lt;/li&gt;
&lt;li&gt;Amazon SDK - You can also trigger StartExecution using the SDK either from a Lambda function or another application.&lt;/li&gt;
&lt;li&gt;Other Step Functions - it is good practice to split up large workflows into smaller ones that you can trigger using the task state.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Amazon States Language
&lt;/h3&gt;

&lt;p&gt;To be able to write step functions you need to learn Amazon’s States Language. Don’t worry it is basically just JSON.&lt;/p&gt;

&lt;p&gt;Here is a simple one state hello world example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "StartAt": "HelloWorld",
  "States": {
    "HelloWorld": {
      "Type": "Pass",
      "Result": "Hello World!",
      "End": true
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The workflow starts with &lt;code&gt;StartAt&lt;/code&gt; which indicates which step it is going to first.&lt;/p&gt;

&lt;p&gt;You then have all of your states for your workflow inside the &lt;code&gt;states&lt;/code&gt; property. Note, even though this has multiple items it isn’t an array.&lt;/p&gt;

&lt;p&gt;You can indicate whether a state is an end state or passes on to another state using &lt;code&gt;End&lt;/code&gt; and &lt;code&gt;Next&lt;/code&gt;. For example, we could split this state up into a Hello state which then passes on to a World state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "StartAt": "Hello",
  "States": {
    "Hello": {
      "Type": "Pass",
      "Result": "Hello",
      "Next": "World"
    },
    "World": {
      "Type": "Pass",
      "Result": "World!",
      "End": true
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This isn’t a great example as we aren’t doing anything with the output of the first state. The result of this step function will just be “World!“. I will get into how we can make this better using States.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step Function States
&lt;/h3&gt;

&lt;p&gt;As we saw from our Hello World example a state is made up of a &lt;code&gt;Type&lt;/code&gt;, and either &lt;code&gt;Next&lt;/code&gt; or &lt;code&gt;End&lt;/code&gt;. The other parameters are dependent on the state &lt;code&gt;Type&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The other fields you can use on any state are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Comment&lt;/code&gt; - you can add comments so others know what your workflow is doing (and you in a few weeks)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;InputPath&lt;/code&gt; &amp;amp; &lt;code&gt;OutputPath&lt;/code&gt; - these allow you to select a portion of the input to pass on to your state or output to pass on to the next state. I will cover these in more detail in the Inputs and Outputs section below.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are 8 different states we can use to make up our workflow.&lt;/p&gt;

&lt;h4&gt;
  
  
  Pass
&lt;/h4&gt;

&lt;p&gt;The pass state we saw in our hello world example. As the name suggests this state doesn’t do any work but can be used to manipulate the data from the last state before passing it on to the next.&lt;/p&gt;

&lt;p&gt;As we did with our example you can hard code results using the &lt;code&gt;Result&lt;/code&gt; parameter. This can be an object as well as a string.&lt;/p&gt;

&lt;p&gt;Such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Result": {
    "x-datum": 0.381018,
    "y-datum": 622.2269926397355
 },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More useful is the &lt;code&gt;Parameters&lt;/code&gt; option which allows you to construct an object using values from the previous state result.&lt;/p&gt;

&lt;p&gt;For example, if you have the following output of your last state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "product": {
    "name": "T-Shirt",
    "details": {
      "color": "green",
      "size": "large",
      "material": "nylon"
    },
    "availability": "in stock",
    "cost": "$23"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But your next state needed the information in this format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "product": "T-Shirt",
  "colour": "green",
  "size": "large",
  "availability": "in stock"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can do this using the &lt;code&gt;Parameters&lt;/code&gt; field as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"MapValues": {
  "Type": "Pass",
  "Parameters": {
    "product.$": "$.product.name",
    "colour.$": "$.product.details.color",
    "size.$": "$.product.details.size",
    "availability.$": "$.product.availability"
  }
  "Next": "AnotherState"
},
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use the &lt;code&gt;Parameters&lt;/code&gt; field in the Task state as well to change the details you pass on.&lt;/p&gt;

&lt;h4&gt;
  
  
  Task
&lt;/h4&gt;

&lt;p&gt;This is the state you are probably going to be using the most as it is the one that you need to interact with other AWS services.&lt;/p&gt;

&lt;p&gt;You need to provide the &lt;code&gt;Resource&lt;/code&gt; field which is usually the ARN of your AWS service.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"LambdaState": {
  "Type": "Task",
  "Resource": "arn:aws:lambda:us-east-1:123456789012:function:HelloWorld",
  "Next": "NextState"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the time of writing AWS natively supports the following AWS services:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lambda&lt;/li&gt;
&lt;li&gt;AWS Batch&lt;/li&gt;
&lt;li&gt;DynamoDB&lt;/li&gt;
&lt;li&gt;Amazon ECS/AWS Fargate&lt;/li&gt;
&lt;li&gt;Amazon SNS&lt;/li&gt;
&lt;li&gt;Amazon SQS&lt;/li&gt;
&lt;li&gt;Amazon Glue&lt;/li&gt;
&lt;li&gt;SageMaker&lt;/li&gt;
&lt;li&gt;Amazon EMR&lt;/li&gt;
&lt;li&gt;CodeBuild&lt;/li&gt;
&lt;li&gt;Athena&lt;/li&gt;
&lt;li&gt;Amazon EKS&lt;/li&gt;
&lt;li&gt;API Gateway&lt;/li&gt;
&lt;li&gt;AWS Glue DataBrew&lt;/li&gt;
&lt;li&gt;AWS Step Functions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find an up to date list of &lt;a href="https://docs.aws.amazon.com/step-functions/latest/dg/concepts-service-integrations.html"&gt;supported services&lt;/a&gt; on Amazon’s developer guide. If your service isn’t listed here than the easiest thing to do is to write a lambda function that calls it for you.&lt;/p&gt;

&lt;h4&gt;
  
  
  Choice
&lt;/h4&gt;

&lt;p&gt;A workflow isn’t complete without some decisions being made. The choice state lets you specify conditions that need to be met to go to a particular state. For example, you might require manual approval if the value of the item is over a certain amount.&lt;/p&gt;

&lt;p&gt;Given step functions are essentially infrastructure, you might not want to put too much logic into them. If you find yourself trying to write complex conditions it would be better to write this in a lambda function. At least that way you can write some unit tests around it (you do write unit tests don’t you?).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"ToBeOrNotToBe": {
  "Type": "Choice",
  "Choices": [
    {
      "Variable": "$.amount",
      "NumericGreaterThan": 1000
      "Next": "CEOApproval"
    },
    {
      "Variable": "$.amount",
      "NumericGreaterThan": 500
      "Next": "ManagerApproval"
    }
  ],
  "Default": "Approved"
},
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Wait
&lt;/h4&gt;

&lt;p&gt;Wait step does what you would expect. It tells the workflow to wait a certain number of seconds or until a specified timestamp.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"wait_ten_seconds": {
  "Type": "Wait",
  "Seconds": 10,
  "Next": "NextState"
}

"wait_until" : {
  "Type": "Wait",
  "Timestamp": "2021-03-14T01:59:00Z",
  "Next": "NextState"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To be honest, I can’t think of many reasons you might want to do this. If you are waiting for a process to finish you would be better off using a callback or an activity.&lt;/p&gt;

&lt;p&gt;One example, could be an ordering system that allows customers to make multiple orders within a 30 minute period. If all the orders are placed within that time then they can all be dispatched as one order. In this case, after receiving an order you could have the system wait 30 minutes and then bundle other orders together as the next step.&lt;/p&gt;

&lt;p&gt;Although you would have to stop subsequent orders from kicking off workflows of their own.&lt;/p&gt;

&lt;h4&gt;
  
  
  Succeed
&lt;/h4&gt;

&lt;p&gt;If you want to complete your workflow early then you can use the succeed state. For example, you might have a choice state that determines nothing else needs to be done if a certain condition has been met.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"SuccessState": {
  "Type": "Succeed"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As this is a terminal state you do not need to specify a &lt;code&gt;Next&lt;/code&gt; or &lt;code&gt;End&lt;/code&gt; field.&lt;/p&gt;

&lt;h4&gt;
  
  
  Fail
&lt;/h4&gt;

&lt;p&gt;Another terminal state you might want to use is when something unexpected happens. For example, a choice state could be used if any of the values coming through are null.&lt;/p&gt;

&lt;p&gt;You can also optionally specify a Cause and Error message that will be returned. As with the succeed state, there is no &lt;code&gt;Next&lt;/code&gt; or &lt;code&gt;End&lt;/code&gt; field.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"FailState": {
  "Type": "Fail",
  "Cause": "Invalid response.",
  "Error": "ErrorA"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Parallel
&lt;/h4&gt;

&lt;p&gt;Some steps in your workflow can be run in parallel. If they don’t depend on other steps and take in similar parameters you can run them at the same time with the parallel state. For example, you might want to do two API calls at once.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"RunTwoThingsInParallel": {
    "Type": "Parallel",
    "Next": "NextStateAfterParallel",
    "Branches": [
        {
          "StartAt": "APICall1",
          "States": {
            "APICall1": {
              "Type": "Task",
              "Resource": "arn:aws:lambda:us-east-1:123456789012:function:GetInfo1",
              "End": true
            }
          }
        },
        {
          "StartAt": "APICall2",
          "States": {
            "APICall2": {
              "Type": "Task",
              "Resource": "arn:aws:lambda:us-east-1:123456789012:function:GetInfo2",
              "End": true
            }
          }
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The states you want to run in parallel get added as branches. Each one has to be marked as an end state and you need to specify in the parallel state which state you want to run next.&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/ff829e1ba0025196e53f69b817e497e8/0b533/parallelstate.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1Z_bOLcF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.alexhyett.com/static/ff829e1ba0025196e53f69b817e497e8/0b533/parallelstate.png" alt="Parallel State Diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once all the states in your parallel state have finished executing the parallel state will return an array of results. The items in the array are in the order they appear in your parallel state. So in my example, the results of APICall1 will be the first item of the array and the results of APICall2 will be the second item.&lt;/p&gt;

&lt;p&gt;Generally, you want to use a Pass state after a Parallel state to turn the array output of a parallel state back into a single JSON object.&lt;/p&gt;

&lt;h4&gt;
  
  
  Map
&lt;/h4&gt;

&lt;p&gt;The map state is similar to Map in Javascript. It is used to iterate over an array and execute a state for each item in your array.&lt;/p&gt;

&lt;p&gt;Taking the example from the &lt;a href="https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-map-state.html"&gt;AWS docs&lt;/a&gt;, if you have an input like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "ship-date": "2016-03-14T01:59:00Z",
  "detail": {
    "delivery-partner": "UQS",
    "shipped": [
      { "prod": "R31", "dest-code": 9511, "quantity": 1344 },
      { "prod": "S39", "dest-code": 9511, "quantity": 40 },
      { "prod": "R31", "dest-code": 9833, "quantity": 12 },
      { "prod": "R40", "dest-code": 9860, "quantity": 887 },
      { "prod": "R40", "dest-code": 9511, "quantity": 1220 }
    ]
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can use the following Map state to iterate over it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Validate-All": {
  "Type": "Map",
  "InputPath": "$.detail",
  "ItemsPath": "$.shipped",
  "MaxConcurrency": 0,
  "Iterator": {
    "StartAt": "Validate",
    "States": {
      "Validate": {
        "Type": "Task",
          "Resource": "arn:aws:lambda:us-east-1:123456789012:function:ship-val",
        "End": true
      }
    }
  },
  "ResultPath": "$.detail.shipped",
  "End": true
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here the &lt;code&gt;InputPath&lt;/code&gt; is used to select just &lt;code&gt;detail&lt;/code&gt; from the JSON input. Then we use &lt;code&gt;ItemsPath&lt;/code&gt; to select the array we want to iterate over. You can then specify in &lt;code&gt;Iterator&lt;/code&gt; the task you want to run for each item in your array.&lt;/p&gt;

&lt;p&gt;In this case, &lt;code&gt;MaxConcurrency&lt;/code&gt; is set to &lt;code&gt;0&lt;/code&gt;, which doesn’t place a quota on tasks being run in parallel. If you need the items in your array to be run in order then you can set &lt;code&gt;MaxConcurrency&lt;/code&gt; to &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inputs and Ouputs
&lt;/h3&gt;

&lt;p&gt;There are several ways that you can manipulate the data that is sent as input and received as an output from your states. Not all of these fields are available in every state so you need to check the &lt;a href="https://docs.aws.amazon.com/step-functions/latest/dg/concepts-states.html"&gt;AWS docs&lt;/a&gt; before using them.&lt;/p&gt;

&lt;p&gt;The only fields which are common to all states are &lt;code&gt;InputPath&lt;/code&gt; and &lt;code&gt;OutputPath&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  InputPath
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;InputPath&lt;/code&gt; is used to select the part of the state input that you want to pass on to your Task. For example given the following input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "result1": {
    "val1": 1,
    "val2": 2,
    "val3": 3
  },
  "result2": {
    "val1": "a",
    "val2": "b",
    "val3": "c"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you set the &lt;code&gt;InputPath&lt;/code&gt; to &lt;code&gt;$.result2&lt;/code&gt; it would send the following as the state input.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "val1": "a",
  "val2": "b",
  "val3": "c"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  OutputPath
&lt;/h4&gt;

&lt;p&gt;As we saw with &lt;code&gt;InputPath&lt;/code&gt; the same can be done with the output. If you have a lambda that returns several fields but you are only interested in one of them then you can use the &lt;code&gt;OutputPath&lt;/code&gt; to select the piece you are interested in.&lt;/p&gt;

&lt;h4&gt;
  
  
  Parameters
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;Parameters&lt;/code&gt; field is the most useful one of all as it allows you to create new JSON inputs from your existing fields. I often use the Parameters field on the Pass state just to change the output of the previous state.&lt;/p&gt;

&lt;p&gt;I gave one example of using Parameters when describing the Pass state. The other option is to use Parameters to turn the array output of the Map or Parallel state into a single object.&lt;/p&gt;

&lt;p&gt;Take this output from a Parallel state that called two different APIs to get information.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[
  {
    "product": "T-Shirt",
    "color": "green",
    "size": "large"
  },
  {
    "product": "T-Shirt",
    "stock": 100
  }
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use the parameters field to turn this into a single object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Parameters": {
  "product.$": "$[0].product",
  "color.$": "$[0].color",
  "size.$": "$[0].size",
  "stock.$": "$[1].stock"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  ResultPath
&lt;/h4&gt;

&lt;p&gt;You can use &lt;code&gt;ResultPath&lt;/code&gt; to specify how the output of your state is going to appear. The default behaviour of the states is to just return the output. This means the original input to your state is lost.&lt;/p&gt;

&lt;p&gt;This is the equivalent of having &lt;code&gt;"ResultPath": "$"&lt;/code&gt; which means the output of your state is the whole result and replaces input.&lt;/p&gt;

&lt;p&gt;This isn’t always useful. Instead, you could append the result of your lambda on to the input. Let’s say we want the output to be added to a &lt;code&gt;result&lt;/code&gt; field. This can be doing using &lt;code&gt;"ResultPath": "$.result"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Or if you want to completely ignore the output of your state and just return the input you can do &lt;code&gt;"ResultPath": null&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  ResultSelector
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;ResultSelector&lt;/code&gt; is similar to the Parameters field but works on the output instead of the input. It allows you to create a new object from the output of your state. The ResultSelector is executed before the ResultPath.&lt;/p&gt;

&lt;p&gt;For example, say we have a lambda that gets the stock for a particular product.&lt;/p&gt;

&lt;p&gt;We have this input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "product": "T-Shirt",
  "color": "green",
  "size": "large"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the output of our lambda function is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "product": "T-Shirt",
  "stock": 100
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But we want to get an output that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "product": "T-Shirt",
  "color": "green",
  "size": "large",
  "availability": {
    "stock": 100
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To do that we need to have the following set:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"ResultSelector": {
    "stock.$": "$.stock"
},
"ResultPath": "$.availability"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Handling Errors in Step Functions
&lt;/h3&gt;

&lt;p&gt;One of the main reason for using step functions is all the hard work of building a workflow engine is done for you. This also includes retries and error handling.&lt;/p&gt;

&lt;h4&gt;
  
  
  Retries
&lt;/h4&gt;

&lt;p&gt;If a transient error occurs you generally want to retry a few times before raising an error. Adding in retries will make your application more resilient to failure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Retry": [ {
   "ErrorEquals": ["States.ALL"],
   "IntervalSeconds": 3,
   "MaxAttempts": 2,
   "BackoffRate": 2
} ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If an error occurs the result of the above will be the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Error occurs&lt;/li&gt;
&lt;li&gt;Waits 3 seconds&lt;/li&gt;
&lt;li&gt;Retries the function. If it fails again then:&lt;/li&gt;
&lt;li&gt;Wait 6 seconds&lt;/li&gt;
&lt;li&gt;Retries the function. If it fails again then raise an error.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;code&gt;ErrorEquals&lt;/code&gt; can be an exception raised from your application or one of the AWS error names which are: &lt;code&gt;States.ALL&lt;/code&gt;, &lt;code&gt;States.DataLimitExceeded&lt;/code&gt;, &lt;code&gt;States.Runtime&lt;/code&gt;, &lt;code&gt;States.Timeout&lt;/code&gt;, &lt;code&gt;States.TaskFailed&lt;/code&gt;, or &lt;code&gt;States.Permissions&lt;/code&gt;. You can read more about these in the &lt;a href="https://docs.aws.amazon.com/step-functions/latest/dg/concepts-error-handling.html"&gt;AWS docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can set &lt;code&gt;MaxAttempts&lt;/code&gt; to 0 if you don’t want a particular error to be retried. For example, if your step function hasn’t got enough permissions to run then a retry probably isn’t going to help.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Retry": [ {
   "ErrorEquals": ["States.Permissions"],
   "MaxAttempts": 0
}, {
   "ErrorEquals": ["States.ALL"]
} ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you only specify &lt;code&gt;ErrorEquals&lt;/code&gt; and none of the other fields then it will retry 3 times with a 1 second interval and a backoff rate of 2.&lt;/p&gt;

&lt;p&gt;Note: &lt;code&gt;States.ALL&lt;/code&gt; has &lt;code&gt;ALL&lt;/code&gt; in capitals, unlike the other error names. If you use &lt;code&gt;States.All&lt;/code&gt; it won’t error it will just ignore it.&lt;/p&gt;

&lt;h4&gt;
  
  
  Catch blocks
&lt;/h4&gt;

&lt;p&gt;Once the retries have been tried, then the execution will fail unless you set a catch block. The catch block lets you direct the workflow to an error handling state if a particular error is thrown. For example, you might want to send a message to say the workflow has failed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Catch": [ {
      "ErrorEquals": ["States.ALL"],
      "Next": "ErrorState"
 } ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As with retries you can add multiple errors and have them go to different states if you want.&lt;/p&gt;

&lt;h3&gt;
  
  
  Activities &amp;amp; Callbacks
&lt;/h3&gt;

&lt;p&gt;If you use the standard workflow you can use activities and callbacks. There is a subtle difference between activities and callbacks but each allows you to perform some work in your workflow asynchronously.&lt;/p&gt;

&lt;h4&gt;
  
  
  Activities
&lt;/h4&gt;

&lt;p&gt;Activities were added first to step functions. Once you create an activity you can use the Task state to send work to your activity using the activity ARN.&lt;/p&gt;

&lt;p&gt;The activity doesn’t do anything it acts as a queue of tasks that need to be picked up. To use activities you need to have a process that polls &lt;code&gt;GetActivityTask&lt;/code&gt;. When an activity task is available you will get back a task token.&lt;/p&gt;

&lt;p&gt;Once work has completed you can use the Amazon SDK or an API Gateway to call &lt;code&gt;SendTaskSuccess&lt;/code&gt; or &lt;code&gt;SendTaskFailure&lt;/code&gt; with your task token.&lt;/p&gt;

&lt;h4&gt;
  
  
  Callbacks
&lt;/h4&gt;

&lt;p&gt;Callbacks work similarly but you don’t have to poll for a task token it is given to you when your task is called. Instead, you add &lt;code&gt;.waitForTaskToken&lt;/code&gt; on the end of the &lt;a href="https://docs.aws.amazon.com/step-functions/latest/dg/connect-to-resource.html#connect-wait-token"&gt;ARN in your Task&lt;/a&gt;. The catch is that not all AWS services support callbacks. At the time of writing only the &lt;a href="https://docs.aws.amazon.com/step-functions/latest/dg/connect-supported-services.html"&gt;following AWS services support callbacks&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lambda&lt;/li&gt;
&lt;li&gt;Amazon ECS/Fargate&lt;/li&gt;
&lt;li&gt;Amazon SNS&lt;/li&gt;
&lt;li&gt;Amazon SQS&lt;/li&gt;
&lt;li&gt;API Gateway&lt;/li&gt;
&lt;li&gt;AWS Step Functions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As with Activities you then need to use Amazon SDK or an API Gateway to call &lt;code&gt;SendTaskSuccess&lt;/code&gt; or &lt;code&gt;SendTaskFailure&lt;/code&gt; with your task token once work has completed.&lt;/p&gt;

&lt;p&gt;Callbacks and Activities can be used for manual approval processes or tasks that take a long time to run.&lt;/p&gt;

&lt;p&gt;To implement a manual approval step you could do the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get your step function to send a message to SQS with &lt;code&gt;.waitForTaskToken&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Set up an API Gateway that has approve and decline endpoints that send &lt;code&gt;SendTaskSuccess&lt;/code&gt; or &lt;code&gt;SendTaskFailure&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Write a lambda that picks up the SQS message and then sends an email via SES with links to your approve and decline endpoints with the taskToken added as a query string parameter.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Best Practices when using AWS Step Functions
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/step-functions/latest/dg/sfn-best-practices.html"&gt;AWS have a list of best practices&lt;/a&gt; that you should follow when writing step functions, I recommend you give those a &lt;a href="https://docs.aws.amazon.com/step-functions/latest/dg/sfn-best-practices.html"&gt;read&lt;/a&gt; before trying out step functions in production.&lt;/p&gt;

&lt;p&gt;In addition to Amazon’s own best practices, I have a few of my own after having some experience with step functions.&lt;/p&gt;

&lt;h4&gt;
  
  
  States should be idempotent
&lt;/h4&gt;

&lt;p&gt;Chances are if a workflow fails you are going to want to replay it. Unfortunately, step functions don’t give you the ability to replay a workflow from a particular step.&lt;/p&gt;

&lt;p&gt;There is a blog post on the &lt;a href="https://aws.amazon.com/blogs/compute/resume-aws-step-functions-from-any-state/"&gt;AWS website&lt;/a&gt; that outlines how you can do it. However, the approach they outline involves dynamically creating a step function that has a choice state at the start that bypasses other steps. I don’t think that is a practical solution for a production environment.&lt;/p&gt;

&lt;p&gt;You could add a choice state at the beginning of your workflow that has branches to every other state in your workflow. However, not only is this quite messy, you can’t isolate individual states in a parallel state.&lt;/p&gt;

&lt;p&gt;Therefore the preferred approach is to ensure your states are idempotent and therefore can be run multiple times without any adverse effects.&lt;/p&gt;

&lt;h4&gt;
  
  
  Pass a unique id to your lambda functions
&lt;/h4&gt;

&lt;p&gt;Trying to stitch together logs in a serverless system can be a nightmare if not done correctly. Especially if your lambdas are used outside of your step function. If a lambda raises an error how do you know what step function execution it relates too?&lt;/p&gt;

&lt;p&gt;When you execute a step function it requires a unique execution id (unique within 90 days) to be sent through. You need to keep in mind that you may need to replay the workflow in which case you can’t use just the unique id from another system. You will need to append something else to it.&lt;/p&gt;

&lt;p&gt;You can however pass this unique id down into your state input. The execution id is available in the &lt;a href="https://docs.aws.amazon.com/step-functions/latest/dg/input-output-contextobject.html"&gt;context object&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Step functions are a great tool to have in your toolbox if you are working with a serverless architecture. I am certainly going to be using them on a few projects in the future.&lt;/p&gt;

&lt;p&gt;I will set up a working example at some point in the future and write about it in another blog post.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>github</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Useful Docker Commands Worth Saving</title>
      <dc:creator>Alex Hyett</dc:creator>
      <pubDate>Fri, 12 Feb 2021 11:30:00 +0000</pubDate>
      <link>https://dev.to/alexhyettdev/useful-docker-commands-worth-saving-36bd</link>
      <guid>https://dev.to/alexhyettdev/useful-docker-commands-worth-saving-36bd</guid>
      <description>&lt;p&gt;I use docker every day. All the applications I write at work or at home end up in docker containers. Most of the time though, I am only running &lt;code&gt;docker-compose up&lt;/code&gt; so when I need to do something more complicated I have to look it up.&lt;/p&gt;

&lt;p&gt;So this post is a resource for me but I am hoping these commands will be useful for you too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cleaning up after Docker
&lt;/h2&gt;

&lt;p&gt;If you have been using docker for a while you are going to end up with quite a few docker images hanging around. These commands should help clear them up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Delete old containers
&lt;/h3&gt;

&lt;p&gt;Most of the cleanup commands that are shared will clean out all of your containers. This command cleans up just those you haven’t used in a while:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker ps --filter "status=exited" | grep 'weeks ago' | awk '{print $1}' | xargs docker rm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Delete untagged images
&lt;/h3&gt;

&lt;p&gt;If an image isn’t tagged then you likely aren’t using it so we can get rid of these as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker images | grep "&amp;lt;none&amp;gt;" | awk '{print $3}' | xargs docker rmi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Clean slate
&lt;/h3&gt;

&lt;p&gt;If you really want to get rid of everything not currently running, including volumes and images you can use this one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker system prune -a --volumes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Building docker images
&lt;/h2&gt;

&lt;p&gt;Most of you will already be familiar with &lt;code&gt;docker build&lt;/code&gt; here are some of the less used docker commands.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rebuild all docker-compose images with no cache
&lt;/h3&gt;

&lt;p&gt;By default doing a &lt;code&gt;docker-compose build&lt;/code&gt; will use cached images. If you want to make sure they are built from scratch and pull down images you need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose build --no-cache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Rebuild just one service in your docker-compose
&lt;/h3&gt;

&lt;p&gt;This one is especially useful on my Raspberry Pi where builds are a bit slower. If you know only one service has changed you can rebuild just that one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose build --no-cache elasticsearch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Connecting to a docker container
&lt;/h2&gt;

&lt;p&gt;If you need to debug why a container isn’t working you often need to connect to the running container.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connect to a running container
&lt;/h3&gt;

&lt;p&gt;To use this command you need to run &lt;code&gt;docker ps&lt;/code&gt; to get a list of running containers, then use the container ID in the command below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker exec -it 0c7b2063b2a2 /bin/bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your container doesn’t have bash you might be able to use sh.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker exec -it 0c7b2063b2a2 /bin/sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Connecting to a stopped container
&lt;/h3&gt;

&lt;p&gt;If you want to connect to a stopped container it is a little more complicated.&lt;/p&gt;

&lt;p&gt;We first need to use &lt;code&gt;docker ps -a&lt;/code&gt; to see the stopped container. Then you need to copy the container ID of the stopped container and commit it to a new image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker commit 0488e172aa70 test/image
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can then run it and connect to it using:&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 -it --rm test/image /bin/bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the docker container has a faulty script at startup you may need to use this:&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 -it --rm --entrypoint /bin/bash test/image
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Copy files between container and host
&lt;/h2&gt;

&lt;p&gt;Occasionally you might want to copy files to and from a running container. Using the Container ID again you can use the following commands.&lt;/p&gt;

&lt;h3&gt;
  
  
  Copy files FROM container
&lt;/h3&gt;

&lt;p&gt;If you just want one file, for example the hosts file you can use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker cp 0c7b2063b2a2:/etc/hosts hosts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or if you want a whole folder you can do this (where etc is a folder in the working directory on the host):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker cp 0c7b2063b2a2:/etc/. etc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Copy files TO container
&lt;/h3&gt;

&lt;p&gt;Similarly you can copy to the container to just by switching the parameters around:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker cp fstab 0c7b2063b2a2:/etc/fstab
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final words
&lt;/h2&gt;

&lt;p&gt;If I come across any other useful commands I will post them here. If you have any useful commands you use then please share them in the comments.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
